Skip to content
Unit testing in Angular2

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']);
  }
}
Need professional services?
Rely on our Expertise

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.

Want to become a super load tester?
Request a Demo