Unit testing in Angular2
Unit tests in Angular2 with Karma¶
To follow up on my first article about Angular2 I want to unit test our Login component:
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:
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:
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:
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:
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.