Unit tests in Angular2 with Karma
To follow up on my first article about Angular2 I want to unit test our Login component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import {Component} from 'angular2/core';
import {AuthenticationService} from '../services/authentication';
import {Router} from 'angular2/router';
@Component({
selector: 'login',
providers: [ ],
directives: [ ],
pipes: [ ],
styles: [ require('./login.css') ],
template: require('./login.html')
})
export class Login {
private credentials: string = '';
constructor(private authService: AuthenticationService, private router: Router) {
}
onSignIn(){
this.authService.setCredentials(this.credentials);
this.router.navigate(['Home']);
}
}
|
The sample project comes with karma configured and some tests already written.
So it’s straightforward to create a new unit test for our component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import {
it,
inject,
injectAsync,
describe,
beforeEachProviders,
TestComponentBuilder
} from 'angular2/testing';
import {Component, provide} from 'angular2/core';
import {AuthenticationService} from '../services/authentication';
import {Router} from 'angular2/router';
import {Login} from './login';
describe('Login', () => {
beforeEachProviders(() => [
Login,
AuthenticationService
]);
it('should set credentials and redirect on sign in', inject([ Login, AuthenticationService, Router], (login, auth, router) => {
[...]
}));
});
|
Mocking services
The hard part is to mock the Router Angular2 service. Indeed if we do not provide a Router implementation karma fails with the following error:
1
2
3
4
5
|
FAILED TESTS:
Login
✖ should set credentials and redirect on sign in
PhantomJS 2.1.1 (Linux 0.0.0)
Failed: No provider for Router! (Login -> Router)
|
A quick googling tells us that we need to provide many Router related services:
1
2
3
4
5
6
|
beforeEachProviders(() => [
ROUTER_PROVIDERS,
provide(APP_BASE_HREF, {useValue: '/'}),
provide(ROUTER_PRIMARY_COMPONENT, {useValue: Login}),
provide(ApplicationRef, {useClass: MockApplicationRef}
]);
|
This may be a bit cumbersome in my case, and I preferred to provide a mock object instead of the real Router:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
describe('Login', () => {
beforeEachProviders(() => [
Login,
AuthenticationService,
provide(Router, {useValue: jasmine.createSpyObj('Router', ['navigate'])})
]);
it('should set credentials and redirect on sign in', inject([ Login, AuthenticationService, Router], (login, auth, router) => {
login.secretKey = 'test';
spyOn(auth, 'setCredentials');
login.onSignIn();
expect(config.setCredentials).toHaveBeenCalledWith('test');
expect(router.navigate).toHaveBeenCalledWith(['Home']);
}));
});
|
This may be done with any service: we could have injected a spy AuthenticationService object instead of only spying on its setCredentials()
method.
Also I think the syntax to write unit tests is much shorter in Angular2 than in AngularJS.
Please subscribe my email for future posts.
In reply to Vaibhav Jain
Hi, for privacy reason we don’t store any information about you (that includes your email). If you want, you can subscribe to the RSS feed of any blog category like Development.