Bob at Genuitec
Virtual evangelist at large. The face of Genuitec often appearing in graphics. Pen-name for our more shy writers of content.
Posted on Oct 25th 2017
Testing is one of the most important aspects of the modern application development process. Today, high quality, robust applications are the norm; nobody appreciates a buggy application. This article will walk you through testing your Angular application using capabilities that are already part of your Angular project.
Creating a New Angular Project
We’re going to be using Angular IDE through this tutorial, though you can use Webclipse or MyEclipse too, following exactly the same steps. Before beginning, do ensure you are in the Angular Perspective (Window > Perspective > Open Perspective > Other > Angular) for an optimum workspace layout and development experience.
In the new Angular project wizard (File > New > Angular Project), type `Angular1` as the project name – you may change the Node.js, npm and Angular CLI versions if desired.
Finally, click Finish to complete the creation of a new Angular project. In the Terminal+ view, you can see several commands being executed by Angular CLI. After the installation of all the modules, we are now ready to continue with development.
Did you know that CodeLive makes it really easy to explore Angular applications? Hover over a component on the page to view the component name, and click the component to view the dashboard list, filtered for the selected component. Easily jump to the corresponding TypeScript, HTML, or CSS file.
Watch this video for a quick demo. |
|
Testing Modules in Your Angular Project
Jasmine
Jasmine is a JavaScript testing framework that supports a software development practice called Behavior Driven Development (BDD) – a specific flavor of Test Driven Development (TDD). It does not require a DOM, and has a clean, obvious syntax so that you can easily write tests.
The general syntax of a Jasmine test suite is given as follows:
describe(" dunebook Jasmine example", function() {
it("This is a spec that defines test", function() {
expect(actual).toBe(true);
});
});
Let’s understand the code snippet above:
- `describe` defines a test suite, it takes a string and function as parameters.
- `it`, like `describe`, also takes a string and a function, and defines a test/spec.
- `expect` takes a value, called the actual, which is compared via one of several matchers (`toBe` in this case) to an expected value.
This introduction is a good place to get started with Jasmine.
Karma
Karma, on the other hand, is not a framework to write tests, but it is a test runner. Karma gives us the ability to run automated tests in several different browsers, and makes it easy to integrate with a continuous integration server. It requires additional frameworks, like Jasmine.
Note: Your Angular project already contains both Karma and Jasmine packages by default, no additional installations are necessary.
Testing an Angular Component
We’re going to modify the generated root component so we can create a simple test.
Modifying our Angular Root Component
Replace the source of `src/app/app.component.ts` with:
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angular 4 testing';
}
Replace the source of `app.component.html` with:
<div style="text-align: center">
<h1>Welcome to {{title}}!!</h1>
</div>
The `app.component.spec.ts` file is responsible for testing, we’re going to replace its source with:
import {TestBed, async} from '@angular/core/testing';
import {AppComponent} from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('it should work well', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'angular 4 testing'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('angular 4 testing');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular 4 testing');
}));
});
Let’s go over the above code snippet in detail:
- We have imported the required modules namely `TestBed` and `async` from `angular/core/testing`.
- In the first test script, we wrote a simple expect statement for the component, tobeTruthy.
- In the second test script, we created an instance of the component via `TestBed.createComponent`:
• We created an instance of the root component via
TestBed.createComponent
.
• Using `debugElement`, we created the instance of the created component, `app`.
• Using `app`, we are able to get the title of the component, and use the `toEqual` assertion to check whether the title is equal to “angular 4 testing”.
- In the last test script we use `debugElement#nativeElement` method and target `<h1>`. Here we are checking if `<h1>` contains, “Welcome to angular 4 testing”.
- The difference between the second and third test scripts is that in third script we are waiting for changes to be detected by using the `detectChanges` method.
Testing an Angular Service
We’re going to go ahead now and test an Angular Service as well. To create a new Angular Service in Angular IDE, we use File > New > Service. Type app
into the Element Name field.
The `app.service.spec.ts` file is responsible for testing, we’re going to replace its source with:
import { TestBed, inject } from '@angular/core/testing';
import { AppService } from './app.service';
describe('AppService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AppService]
});
});
it('App service is Running Fine', inject([AppService], (service: AppService) => {
expect(service).toBeTruthy();
}));
});
The assertion in the code above is true if `service`, an instance of `AppService` (obtained via Dependency Injection), is not null.
Running the Tests
Before we run the tests, you might want to run the project itself, to see the output we’re expecting, in the browser.
There is a server view in Angular IDE, as shown in the image below (Window > Show View > Servers). Right click on your project (here `Angular1`) and select `Start Server`. Angular IDE serves the application on `localhost port 4200` by default. So open http://localhost:4200 in your browser to see the running app.
You should see a screen like the one below at http://localhost:4200.
In the Terminal+ view (Window > Show View > Terminal+) type the following command to run the tests:
`ng test`
This command opens a browser window with the test results – as you can see in the above screenshot, all specified tests have been executed and have passed.
Note: Press Ctrl + C
in the Terminal+ view to end the test run, else it will reopen the test result browser each time you close it.
Creating an Advanced Test
Our tests so far were rather basic, let’s do something a bit more advanced, by replacing our service code and service test.
Replace the code in `src/app/app.service.ts` with:
import {Injectable} from '@angular/core';
@Injectable()
export class AppService {
getTopicCount() {
const Topics = [
{name: 'laravel'},
{name: 'angular'},
{name: 'java'},
{name: 'python'}
];
return Object.keys(Topics).length;
}
}
We’ve defined the `getTopicCount` method to return the number of elements in the `Topics` array.
Replace the testing code in `app.service.spec.ts` with:
import {TestBed, inject} from '@angular/core/testing';
import {AppService} from './app.service';
describe('AppService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AppService]
});
});
it(' Angular Service should return 3 values', inject([AppService],
(service: AppService) => {
let countTopic = service.getTopicCount;
expect(countTopic).toBe(3);
}));
});
Like the last service test example, we obtain the `service` instance using dependency injection, then call the `getTopicCount` method to check whether the count returned is 3. The `beforeEach` function is called before each test in the suite, usually used to set up tests; in this case it ensures we can get the `service` instance we are interested in.
Again, in Terminal+, execute `ng test` to execute the tests.
After running the test, you will see that our latest test has failed, as our test expected a count of 3, while the actual count was 4.
In Closing
If you’re interested in learning more about testing, you may also want to take a look at Mocha, which is an alternative to Jasmine. Libraries like Chai and Sinon, may also come in handy, and can be used within your tests in either framework.
While we used Angular IDE in our tutorial today, if you already have an Eclipse install you are happy with, you can get the same functionality by installing Webclipse. Finally, if you’re looking for a full-stack experience with Java EE at the back end, try MyEclipse.
Whatever framework and tool you use, don’t forget to test, and develop tests alongside regular functionality – it’s a strategy that will pay huge dividends in the long run.