Angular 2 Testing

Jesse Sanders

Application Architect

jesse.sanders@briebug.com

www.briebug.com

CEO, BrieBug

Developer for 20 years

 

Focused on JavaScript based solutions

 

About Me

We will cover

  • Unit Testing
  • Integration Testing

www.briebug.com

Why Test?

www.briebug.com

What types of tests should I write?

www.briebug.com

Unit Tests

Definition: Unit Testing is a level of software testing where individual function/method of a software are tested. The purpose is to validate that each function of the software performs as designed. This is normally performed by software developers.

www.briebug.com

How do I setup a test?

Jasmine vs Mocha?

Karma.JS

Karma.conf.js

Code Coverage

www.briebug.com

www.briebug.com

/* tslint:disable:no-unused-variable */
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {DebugElement} from '@angular/core';

import {MyComponentComponent} from './my-component.component';

describe('MyComponentComponent', () => {
    let component: MyComponentComponent;
    let fixture: ComponentFixture<MyComponentComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [MyComponentComponent]
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(MyComponentComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });
});

So now what?

www.briebug.com

Common Gotcha

fixture.detectChanges

ngOnInit

Router Calls

www.briebug.com

Spy vs. Mock

www.briebug.com

Service/Http Calls

Mobile Native Library Calls

Router functions

Long running processes

Anything you want to isolate from outside function calls.

You control the return values...

spyOn

Pros:

  • can imitate function calls by replacement
  • track how many times it was called
  • what arguments were passed
  • great for handling router methods

Cons:

  • Not as reusable
  • Easy to replace the actual functions
  • No longer testing the function calls

www.briebug.com

Mocks

Pros:

  • reusable
  • closely mimic existing services
  • doesn't replace method
  • easy to change out return values

Cons:

  • Unable to tell what arguments were passed

www.briebug.com

Router Methods

RouterTestingModule vs spyOn

www.briebug.com

Managing Dependencies

test.module.ts

www.briebug.com

www.briebug.com

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule, ReactiveFormsModule, NgForm} from '@angular/forms';

import {TranslateModule, TranslateService} from 'ng2-translate/ng2-translate';
import {ToastModule, ToastsManager} from 'ng2-toastr/ng2-toastr';
import {AuthService, SessionService} from '../app/services';
import {ToastsManagerMock, AuthServiceMock, SessionServiceMock} from './index';

@NgModule({
    imports: [
        CommonModule, ReactiveFormsModule, FormsModule, TranslateModule.forRoot(), ToastModule
    ],
    declarations: [],
    providers: [
        TranslateService,
        NgForm,
        {provide: AuthService, useClass: AuthServiceMock},
        {provide: SessionService, useClass: SessionServiceMock},
        {provide: ToastsManager, useClass: ToastsManagerMock}
    ],
    exports: [
        CommonModule, ReactiveFormsModule, FormsModule, TranslateModule, ToastModule
    ]
})
export class TestModule {}
// arrange
// act
// assert

www.briebug.com

Test Layout

Let's look at some tests

www.briebug.com

Unit Test Questions

www.briebug.com

Component Integration Testing

"HTML Testing"

www.briebug.com

Component Integration Testing vs Protactor/e2e

www.briebug.com

Basics

  • No magic data setup
  • Mock/Spy external calls to control inputs
  • Interact with HTML only*
  • Test what the user would do and see

www.briebug.com

www.briebug.com


import {DebugElement} from '@angular/core';
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
import {By} from '@angular/platform-browser';

New Imports

www.briebug.com

 let usernameEl: any,
    passwordEl: any,
    usernameErrorEl: any,
    passwordErrorEl: any,
    loginButtonEl: DebugElement,
    loginForm: FormGroup,
    usernameFormControl: AbstractControl,
    passwordFormControl: AbstractControl...
        
beforeEach(() => {
    // call ngOnInit
    fixture.detectChanges();

    // get the html elements
    usernameEl = fixture.debugElement.query(By.css('#username')).nativeElement;
    passwordEl = fixture.debugElement.query(By.css('#password')).nativeElement;
    loginButtonEl = fixture.debugElement.query(By.css('.btn-primary'));

    // Get the form and form controls
    loginForm = component.loginForm;
    usernameFormControl = loginForm.controls['username'];
    passwordFormControl = loginForm.controls['password'];
});

Select your controls

Common Gotcha

*ng-if - my control isn't there during beforeEach

www.briebug.com

dispatchEvent

browser utility that updates the form group with the new value.

 

  • input
  • change

www.briebug.com

dispatchEvent Gotcha

www.briebug.com

myDropdownEl.value = 2;
dispatchEvent(statusEl, 'change');

dueDateEl.value = '01/01/2016';
dispatchEvent(dueDateEl, 'change');

Text

  • Dropdown must have option you are setting
  • Date formats must match
  • invalid/improperly formatted values can silently fail

Console.log

 

When in doubt use console log to inspect.

www.briebug.com

Router Spy

spyOn(router, 'navigate');

www.briebug.com

www.briebug.com

it('form should show validators when invalid', async(() => {
    // arrange
    fixture.whenStable().then(() => {
        // act
        myButton.triggerEventHandler('click', null);
        fixture.detectChanges();

        // assert
        expect(someValue).toBe('abc');
    });
}));

fixture.whenStable

www.briebug.com

myButton.triggerEventHandler('click', null);
fixture.detectChanges();

triggerEventHandler

Let's look at some tests...

www.briebug.com

Angular 2 Testing

By briebug

Angular 2 Testing

  • 1,705