Angular 2 Testing


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,840