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']);
  }
}

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.

By - CEO.
Tags: Angular2 Webpack Testing Import

Comments

 

Thank you

Your comment has been submitted and will be published once it has been approved.

OK

OOPS!

Your post has failed. Please return to the page and try again. Thank You!

OK