Angular 2 Unit Testing
October 25, 2016
Vikram Subramanian
Introduction
Introduction
Pragmatic Unit testing
Decisions
Should I write unit tests?
Framework to use
Test Runner
Decisions - Angular specific
Decisions - Angular specific (contd.)
Recipes
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>
Recipes - Simple Component with templateUrl
[app.component.spec.ts]
...
beforeEach(async(() => {
TestBed.configureTestingModule({imports: [AppModule]});
// Precompile components with templateUrl.
TestBed.compileComponents();
}));
...
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");
});
...
Recipes
Component
Users Service
Backend
Angular App
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();
}));
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();
...
}
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.
...
}
}
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();
...
}
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 }
]
...
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 => { ...
Recipes
Recipes - Testing Application Routing
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(ROUTES),
AppModule
]
});
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');
});
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');
Recipes
Testing Nested Components
Recipes - Testing Nested Components
[app.component.html]
<app-banner></app-banner>
<app-welcome></app-welcome>
<user-details></user-details>
Testing nested components - Approach 1
Don’t mock out anything
beforeEach(async(() => {
TestBed.configureTestingModule({imports: [AppModule]});
TestBed.compileComponents();
}));
Testing nested components - Approach 2
Mock all dependencies
...
TestBed.configureTestingModule({
declarations: [AppComponent, MockBannerComponent,
MockWelcomeComponent, MockUserDetailsComponent],
});
...
Testing nested components - Approach 3
Shallow Testing - NO_ERRORS_SCHEMA
...
TestBed.configureTestingModule({
declarations: [AppComponent],
schemas: [NO_ERRORS_SCHEMA],
});
...
WRITE UNIT TESTS!!
Resources
Acknowledgements
Backup
Properties of good unit tests
Source: https://pragprog.com/magazines/2012-01/unit-tests-are-first