1 of 32

Angular 2 Unit Testing

October 25, 2016

Vikram Subramanian

Google

2 of 32

Introduction

  • Angular 2.0 final!
  • Changes in Test API from RC to final
    • Decrease number of concepts and boilerplate
    • Independent of test framework
    • NgModule

3 of 32

Introduction

Pragmatic Unit testing

  • Decisions to make
  • Testing Recipes

4 of 32

Decisions

Should I write unit tests?

    • Yes
    • Yes!!

Framework to use

    • Jasmine
    • Mocha

Test Runner

    • Karma

5 of 32

Decisions - Angular specific

  • Testing Module Setup
    • How much to mock?
    • Isolation vs Being close to production

  • Test Method Setup
    • () => { }
    • async() => { }
    • fakeAsync() => { }

6 of 32

Decisions - Angular specific (contd.)

  • Locating elements
    • Using DOM API
    • Using DebugElement

  • Dispatching events
    • Using DOM API
    • Using DebugElement

7 of 32

Recipes

8 of 32

Recipes - Simple Component with templateUrl

[app.component.ts]

@Component({

moduleId: module.id,

selector: 'my-app',

templateUrl: 'app.component.html'

})

export class AppComponent {}

[app.component.html]

<h1>My First Angular App</h1>

9 of 32

Recipes - Simple Component with templateUrl

[app.component.spec.ts]

...

beforeEach(async(() => {

TestBed.configureTestingModule({imports: [AppModule]});

// Precompile components with templateUrl.

TestBed.compileComponents();

}));

...

10 of 32

Recipes - Simple Component with templateUrl

[app.component.spec.ts]

...

// Synchronous test method.

it('displays properly', () => {

let fixture = TestBed.createComponent(AppComponent);

fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe("My First Angular App");

});

...

11 of 32

Recipes

12 of 32

Component

Users Service

Backend

Angular App

13 of 32

Recipes - Component Communicating with Backend service

beforeEach(async(() => {

TestBed.configureTestingModule({imports: [AppModule]});

// Override providers for the UsersService in the App module.

TestBed.overrideModule(AppModule,

{set:

{providers: [{provide: UsersService, useClass: MockUsersService}]}

}

);

TestBed.compileComponents();

}));

14 of 32

Recipes - Component Communicating with Backend service

it('displays user details on click', async(() => {

...

// Locate the fetch button.

let debugFetchButton = fixture.debugElement.query(By.css('button'));

expect(debugFetchButton).not.toBe(null);

// Trigger the click event through the DOM.

debugFetchButton.nativeElement.click();

...

}

15 of 32

Recipes - Component Communicating with Backend service

it('displays users list on click', async(() => {

...

// Wait for the async getUsers to complete and Angular to become stable.

fixture.whenStable().then(() => {

// Trigger rendering component state to DOM.

fixture.detectChanges();

// Check that the user list is displayed.

...

}

}

16 of 32

Recipes - Component Communicating with Backend service

// fakeAsync() version.

it('displays user details on click(fakeAsync)', fakeAsync(() => {

...

// Trigger the click event through the DOM.

debugFetchButton.nativeElement.click();

// Wait for Promise resolution and Angular to stabilize.

tick();

fixture.detectChanges();

...

}

17 of 32

Recipes - Component Communicating with Backend service

import {XHRBackend} from '@angular/http';

import {MockBackend} from '@angular/http/testing';

...

// Setup for mocking the HTTP Backend.

beforeEach(() => {

TestBed.configureTestingModule({

imports: [HttpModule],

providers: [

UsersService,

{ provide: XHRBackend, useClass: MockBackend }

]

...

18 of 32

Recipes - Component Communicating with Backend service

it('returns all users', async(() => {

let backend = TestBed.get(XHRBackend);

let http = TestBed.get(Http);

let service = new UsersService(http);

let fakeUsers = makeUsers();

let options = new ResponseOptions({status: 200, body: fakeUsers});

let response = new Response(options);

backend.connections.subscribe(

(c: MockConnection) => c.mockRespond(response));

service.getUsers().then(users => { ...

19 of 32

Recipes

20 of 32

Recipes - Testing Application Routing

TestBed.configureTestingModule({

imports: [

RouterTestingModule.withRoutes(ROUTES),

AppModule

]

});

21 of 32

Recipes - Testing Application Routing

// async version

router.navigateByUrl('/about');

fixture.whenStable().then(() => {

fixture.detectChanges();

// Verify we navigated to About page.

let desc = fixture.debugElement.query(By.css('.description'));

expect(desc).not.toBe(null);

expect(desc.nativeElement.textContent).toContain('All about this sample');

});

22 of 32

Recipes - Testing Application Routing

// fakeAsync Version

router.navigateByUrl('/about');

tick();

fixture.detectChanges();

// Verify we navigated to About page.

let desc = fixture.debugElement.query(By.css('.description'));

expect(desc).not.toBe(null);

expect(desc.nativeElement.textContent).toContain('All about this sample');

23 of 32

Recipes

Testing Nested Components

24 of 32

Recipes - Testing Nested Components

[app.component.html]

<app-banner></app-banner>

<app-welcome></app-welcome>

<user-details></user-details>

25 of 32

Testing nested components - Approach 1

Don’t mock out anything

beforeEach(async(() => {

TestBed.configureTestingModule({imports: [AppModule]});

TestBed.compileComponents();

}));

26 of 32

Testing nested components - Approach 2

Mock all dependencies

...

TestBed.configureTestingModule({

declarations: [AppComponent, MockBannerComponent,

MockWelcomeComponent, MockUserDetailsComponent],

});

...

27 of 32

Testing nested components - Approach 3

Shallow Testing - NO_ERRORS_SCHEMA

...

TestBed.configureTestingModule({

declarations: [AppComponent],

schemas: [NO_ERRORS_SCHEMA],

});

...

28 of 32

WRITE UNIT TESTS!!

29 of 32

Resources

30 of 32

Acknowledgements

  • Mashhood Rastgar, Gerard Sans - Ideas for the talk
  • Jasmine Plunker template - Ken Rimple - @krimple
  • Ward and the docs team for putting up the best docs

31 of 32

32 of 32

Backup

Properties of good unit tests

  • Fast
  • Isolated
  • Repeatable
  • Self-verifying
  • Timely

Source: https://pragprog.com/magazines/2012-01/unit-tests-are-first