SlideShare a Scribd company logo
http://digitaldrummerj.me
Justin James
Web Developer
Professional Speaker
digitaldrummerj.me
@digitaldrummerj
http://digitaldrummerj.me
unit tests are proof that my
code does what I think it does
http://digitaldrummerj.me
Document FunctionalityPinpoint Bugs
Check RegressionConfirm Functionality
Unit Test Will Make You Faster
http://digitaldrummerj.me
Checks Single
Assumption
Single Unit
Of Work
Automated
Code
Tenents
http://digitaldrummerj.me
We only get these advantages when we are comfortable writing good
tests
Disclaimer
http://digitaldrummerj.me
RunnableDependableMaintainable
Good Unit Tests
http://digitaldrummerj.me
Well-namedEasy To Write
Easy To ReadNot Tricky
Maintainable
http://digitaldrummerj.me
Tests Right ThingsContinued Relevance
IsolatedConsistent Results
Dependable
http://digitaldrummerj.me
Failures Point To
The Problem
Repeatable
Fast
Runnable
Single Click
http://digitaldrummerj.me
Unit testing
will not fix
bad practices
Don't do the right thing
for the wrong reason
Know the Goals
http://digitaldrummerj.me
"Don't let the fear that testing can't catch
all bugs stop you from writing the tests
that will catch most bugs"
Martin Fowler
Refactoring
http://digitaldrummerj.me
You need a strong grasp of the
basic patterns in order to test
an Angular application
http://digitaldrummerj.me
ServicesRouting
ComponentsModules
Angular Main Building Blocks
13
http://digitaldrummerj.me
http://nodejs.org
Node JS (6.9+)
npm install -g @angular/cli
NPM Global Packages
Angular CLI
Setup
14
http://digitaldrummerj.me
ng new AppName --style scss --routing
Create New Project
ng generate [TYPE] [NAME]
Generate
npm start
Start Application
npm test
Unit Test
Creating
Applications
15
http://digitaldrummerj.me
Test Runner Test Framework Testing Utilities
Karma
Tools and Technology
Jasmine Angular
http://digitaldrummerj.me
http://digitaldrummerj.me
http://digitaldrummerj.me
Debugging with Karma
Use the Chrome Developer Console to debug tests
Use console.log to observe data and events
describe('First spec', () => {
it('should pass', () => {
expect(true).toBeTruthy();
});
});
Simple Passing Test
describe('First spec', () => {
it('should pass', () => {
expect(true).toBeTruthy();
});
});
Simple Passing Test
describe('First spec', () => {
it('should pass', () => {
expect(true).toBeTruthy();
});
});
Simple Passing Test
describe(Second spec', () => {
it('should fail', () => {
expect(false).toBeTruthy();
});
});
Simple Failing Test
http://digitaldrummerj.me
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string;
constructor() { }
ngOnInit() { }
}
Component
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string = "World";
constructor() { }
ngOnInit() { }
}
Component
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string = "World";
constructor() { }
ngOnInit() { }
}
Component
http://digitaldrummerj.me
1. Configure Module
http://digitaldrummerj.me
TestBed
Creates an Angular testing module
Configure with TestBed.configureTestingModule
Use BeforeEach to reset test module before each spec
fixture
import { async, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleComponent]
})
.compileComponents()
.then(() => {
});
}));
}
fixture
import { async, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleComponent]
})
.compileComponents()
.then(() => {
});
}));
}
fixture
import { async, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleComponent]
})
.compileComponents()
.then(() => {
});
}));
}
fixture
import { async, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleComponent]
})
.compileComponents()
.then(() => {
});
}));
}
http://digitaldrummerj.me
1. Configure Module
2. Create Fixture
http://digitaldrummerj.me
TestBed.createComponent
Creates an instance of the component under test
Returns a component test fixture
Closes the TestBed from further configuration
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
let fixture: ComponentFixture<SimpleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleComponent ]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(SimpleComponent);
});
}));
}
The Fixture
http://digitaldrummerj.me
1. Configure Module
2. Create Fixture
3. Get Component Instance
http://digitaldrummerj.me
ComponentFixture
Handle to the test environment
fixture.componentInstance to access to component
The Component Instance
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
let component: SimpleComponent;
let fixture: ComponentFixture<SimpleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TemplateComponent ]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(TemplateComponent);
component = fixture.componentInstance;
});
}));
}
The Component Instance
it('sets the `subject` class member', () => {
expect(component.subject).toBe('world');
});
http://digitaldrummerj.me
ComponentFixture.detectChanges
Triggers change detection
Not triggered by TestBed.createComponent
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
describe('SimpleComponent', () => {
let component: SimpleComponent;
let fixture: ComponentFixture<SimpleComponent>;
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [ SimpleComponent ]
})
.createComponent(SimpleComponent)
.then(() => {
component = fixture.componentInstance;
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
}));
}
The Debug Element
http://digitaldrummerj.me
DebugElement
DebugElement to access the component's DOM
DebugElement.query to query the DOM
By.css to query DOM using CSS selectors
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleComponent } from './simple.component';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
describe('SimpleComponent', () => {
let component: SimpleComponent;
let fixture: ComponentFixture<SimpleComponent>;
let element: DebugElement;
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [ SimpleComponent ]
})
.createComponent(SimpleComponent)
.then(() => {
component = fixture.componentInstance;
element = fixture.debugElement;
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
}));
}
The Debug Element
it('greets the subject', () => {
const h1 = element.query(By.css('h1'));
expect(h1.nativeElement.innerText).toBe('Hello world!');
});
The Debug Element
it('greets the subject', () => {
const h1 = element.query(By.css('h1'));
expect(h1.nativeElement.innerText).toBe('Hello world!');
});
The Debug Element
it('greets the subject', () => {
const h1 = element.query(By.css('h1'));
expect(h1.nativeElement.innerText).toBe('Hello world!');
});
The Debug Element
http://digitaldrummerj.me
Service
export class GreetingService {
subject: string = 'world';
}
import { GreetingService } from '@./services/greeting.service';
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string = this.service.subject;
constructor(private service: GreetingService) { }
ngOnInit() { }
}
Component
import { GreetingService } from '@./services/greeting.service';
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string = this.service.subject;
constructor(private service: GreetingService) { }
ngOnInit() { }
}
Component
import { GreetingService } from '@./services/greeting.service';
@Component({
selector: 'app-service',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss']
})
export class SimpleComponent implements OnInit {
subject: string = this.service.subject;
constructor(private service: GreetingService) { }
ngOnInit() { }
}
Component
http://digitaldrummerj.me
Don't Use Real Services
http://digitaldrummerj.me
Don't Use Real Services
Use Test Double
http://digitaldrummerj.me
Don't Use Real Services
Use Test Double
Use A Stub
http://digitaldrummerj.me
Don't Use Real Services
Use Test Double
Use A Stub
Use A Spy
http://digitaldrummerj.me
Override Provider With
useValue or useClass
Local Members
describe('Simple Component with Service', () => {
let component: SimpleComponent;
let fixture: ComponentFixture<SimpleComponent>;
let element: DebugElement;
let greetingServiceStub: GreetingServiceStub
let greetingService: GreetingService;
});
Local Members
describe('Simple Component with Service', () => {
let component: SimpleComponent;
let fixture: ComponentFixture<SimpleComponent>;
let element: DebugElement;
let greetingServiceStub: GreetingServiceStub
let greetingService: GreetingService;
});
export class GreetingServiceStub {
service = "Test World!"
}
fixture = TestBed.configureTestingModule({
declarations: [SimpleComponent ],
providers: [{ provide: GreetingService, useClass: GreetingServiceStub }]
})
.createComponent(SimpleComponent)
.then(() => {
fixture = TestBed.createComponent(SimpleComponent);
component = fixture.componentInstance;
element = fixture.debugElement;
greetingService = de.injector.get(GreetingService);
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
Test Double
export class GreetingServiceStub {
service = "Test World!"
}
fixture = TestBed.configureTestingModule({
declarations: [SimpleComponent ],
providers: [{ provide: GreetingService, useClass: GreetingServiceStub }]
})
.createComponent(SimpleComponent)
.then(() => {
fixture = TestBed.createComponent(SimpleComponent);
component = fixture.componentInstance;
element = fixture.debugElement;
greetingService = de.injector.get(GreetingService);
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
Test Double
http://digitaldrummerj.me
Get Service Reference With
debugElement.injector
export class GreetingServiceStub {
service = "Test World!"
}
fixture = TestBed.configureTestingModule({
declarations: [SimpleComponent ],
providers: [{ provide: GreetingService, useValue: greetingServiceStub }]
})
.createComponent(SimpleComponent)
.then(() => {
fixture = TestBed.createComponent(SimpleComponent);
component = fixture.componentInstance;
element = fixture.debugElement;
greetingService = de.injector.get(GreetingService);
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
});
Test Double
Actual Test
it('updates component subject when service subject is changed', () => {
greetingService.subject.name = 'cosmos';
fixture.detectChanges();
expect(component.subject.name).toBe('cosmos');
const h1 = de.query(By.css('h1')).nativeElement;
expect(h1.innerText).toBe('Hello cosmos!');
});
Actual Test
it('updates component subject when service subject is changed', () => {
greetingService.subject.name = 'cosmos';
fixture.detectChanges();
expect(component.subject.name).toBe('cosmos');
const h1 = de.query(By.css('h1')).nativeElement;
expect(h1.innerText).toBe('Hello cosmos!');
});
Actual Test
it('updates component subject when service subject is changed', () => {
greetingService.subject.name = 'cosmos';
fixture.detectChanges();
expect(component.subject.name).toBe('cosmos');
const h1 = de.query(By.css('h1')).nativeElement;
expect(h1.innerText).toBe('Hello cosmos!');
});
Actual Test
it('updates component subject when service subject is changed', () => {
greetingService.subject.name = 'cosmos';
fixture.detectChanges();
expect(component.subject.name).toBe('cosmos');
const h1 = de.query(By.css('h1')).nativeElement;
expect(h1.innerText).toBe('Hello cosmos!');
});
Actual Test
it('updates component subject when service subject is changed', () => {
greetingService.subject.name = 'cosmos';
fixture.detectChanges();
expect(component.subject.name).toBe('cosmos');
const h1 = de.query(By.css('h1')).nativeElement;
expect(h1.innerText).toBe('Hello cosmos!');
});
http://digitaldrummerj.me
Service
@Injectable()
export class GreetingService {
subject: string = 'world' ;
getGreeting() { return Promise.resolve('Hello'); }
getSubject() { return Promise.resolve(this.subject.name); }
getPunctuation() { return Promise.resolve('!'); }
}
Service
@Injectable()
export class GreetingService {
subject: string = 'world' ;
getGreeting() { return Promise.resolve('Hello'); }
getSubject() { return Promise.resolve(this.subject.name); }
getPunctuation() { return Promise.resolve('!'); }
}
Component
export class SimpleComponent implements OnInit {
subject: string;
greeting: string;
punctuation: string;
constructor(private service: GreetingService) { }
ngOnInit() {
this.service.getGreeting()
.then(res => this.greeting = res);
this.service.getSubject()
.then(res => this.subject = res);
this.service.getPunctuation()
.then(res => this.punctuation = res);
}
}
Component
export class SimpleComponent implements OnInit {
subject: string;
greeting: string;
punctuation: string;
constructor(private service: GreetingService) { }
ngOnInit() {
this.service.getGreeting()
.then(res => this.greeting = res);
this.service.getSubject()
.then(res => this.subject = res);
this.service.getPunctuation()
.then(res => this.punctuation = res);
}
}
Setup
beforeEach(async(() => {
fixture = TestBed.configureTestingModule({
declarations: [SimpleComponent ],
providers: [ GreetingService ]
})
.createComponent(SimpleComponent)
.then(() => {
component = fixture.componentInstance;
element = fixture.debugElement;
greetingService = de.injector.get(GreetingService);
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
}));
Setup
beforeEach(async(() => {
fixture = TestBed.configureTestingModule({
declarations: [SimpleComponent ],
providers: [ GreetingService ]
})
.createComponent(SimpleComponent)
.then(() => {
component = fixture.componentInstance;
element = fixture.debugElement;
greetingService = de.injector.get(GreetingService);
return fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
}));
Spy with fixture.whenStable
it('gets `greeting` after promise (async)', async(() => {
spyOn(greetingService, 'getGreeting')
.and.returnValue(Promise.resolve('Greetings'));
expect(component.greeting).toBe('Greetings');
});
}));
Spy with fixture.whenStable
it('gets `greeting` after promise (async)', async(() => {
spyOn(greetingService, 'getGreeting')
.and.returnValue(Promise.resolve('Greetings'));
expect(component.greeting).toBe('Greetings');
});
}));
Spy with fixture.whenStable
it('gets `greeting` after promise (async)', async(() => {
spyOn(greetingService, 'getGreeting')
.and.returnValue(Promise.resolve('Greetings'));
expect(component.greeting).toBe('Greetings');
});
}));
Spy with fixture.whenStable
it('gets `greeting` after promise (async)', async(() => {
spyOn(greetingService, 'getGreeting')
.and.returnValue(Promise.resolve('Greetings'));
expect(component.greeting).toBe('Greetings');
});
}));
http://digitaldrummerj.me
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
Spy with tick
it('gets `subject` after promise (fakeAsync)', fakeAsync(() => {
spyOn(greetingService, 'getSubject')
.and.returnValue(Promise.resolve('universe'));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(component.subject).toBe('universe');
}));
http://digitaldrummerj.me
http://digitaldrummerj.me
Component with Inputs and Outputs
The goal is to ensure that binding works as expected
Test input: simply update value and ensure it renders
Test output: trigger triggerEventHandler and subscribe to
the EventEmitter
@Component({
selector: 'app-input-output',
template: `
<h1>Hello {{subject}}!</h1>
<button (click)="depart()">We Out</button>
`
})
export class InputOutputComponent {
@Input('subject') subject: string;
@Output('leave') leave: EventEmitter<string> = new EventEmitter();
depart() {
this.leave.emit(`Later ${this.subject}!`);
}
}
Component
@Component({
selector: 'app-input-output',
template: `
<h1>Hello {{subject}}!</h1>
<button (click)="depart()">We Out</button>
`
})
export class InputOutputComponent {
@Input('subject') subject: string;
@Output('leave') leave: EventEmitter<string> = new EventEmitter();
depart() {
this.leave.emit(`Later ${this.subject}!`);
}
}
Component
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [ InputOutputComponent ]
})
.createComponent(InputOutputComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
button = de.query(By.css('button'));
component.subject = 'galaxy';
fixture.detectChanges();
});
Setup
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [ InputOutputComponent ]
})
.createComponent(InputOutputComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
button = de.query(By.css('button'));
component.subject = 'galaxy';
fixture.detectChanges();
});
Setup
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [ InputOutputComponent ]
})
.createComponent(InputOutputComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
button = de.query(By.css('button'));
component.subject = 'galaxy';
fixture.detectChanges();
});
Setup
it('has `subject` as an @Input', () => {
expect(component.subject).toBe('galaxy');
});
Input
it('says goodbye to the `subject`', () => {
let farewell;
component.leave.subscribe(event => farewell = event);
button.triggerEventHandler('click', { button: 0 });
expect(farewell).toBe('Later galaxy!');
});
Output
it('says goodbye to the `subject`', () => {
let farewell;
component.leave.subscribe(event => farewell = event);
button.triggerEventHandler('click', { button: 0 });
expect(farewell).toBe('Later galaxy!');
});
Output
it('says goodbye to the `subject`', () => {
let farewell;
component.leave.subscribe(event => farewell = event);
button.triggerEventHandler('click', { button: 0 });
expect(farewell).toBe('Later galaxy!');
});
Output
http://digitaldrummerj.me
http://digitaldrummerj.me
Routed Component
Avoid router complexity.
Use RouterTestingModule
Test that component navigates to proper route
beforeEach(() => {
fixture = TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{path: '', redirectTo: '/items', pathMatch: 'full' },
{path: 'items', component: ItemsComponent},
{path: 'routed/:subject', component: RoutedComponent},
{path: 'widgets', component: WidgetsComponent},
{path: '**', redirectTo: '/items', pathMatch: 'full'}
]);
],
declarations: [
RoutedComponent
]
})
.createComponent(RoutedComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
fixture.detectChanges();
});
Routes
import { RouterTestingModule } from '@angular/router/testing';
beforeEach(() => {
fixture = TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{path: '', redirectTo: '/items', pathMatch: 'full' },
{path: 'items', component: ItemsComponent},
{path: 'routed/:subject', component: RoutedComponent},
{path: 'widgets', component: WidgetsComponent},
{path: '**', redirectTo: '/items', pathMatch: 'full'}
]);
],
declarations: [
RoutedComponent
]
})
.createComponent(RoutedComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
fixture.detectChanges();
}); Routes
import { RouterTestingModule } from '@angular/router/testing';
beforeEach(() => {
fixture = TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{path: '', redirectTo: '/items', pathMatch: 'full' },
{path: 'items', component: ItemsComponent},
{path: 'routed/:subject', component: RoutedComponent},
{path: 'widgets', component: WidgetsComponent},
{path: '**', redirectTo: '/items', pathMatch: 'full'}
]);
],
declarations: [
RoutedComponent
]
})
.createComponent(RoutedComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
fixture.detectChanges();
}); Routes
RouterLink
import { RouterLinkWithHref } from '@angular/router';
import { fakeAsync } from '@angular/core/testing';
it('should have 1 routerLink in template', fakeAsync(() => {
allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
expect(allLinks.length).toBe(1, 'should have 1 link');
}));
RouterLink
import { RouterLinkWithHref } from '@angular/router';
import { fakeAsync } from '@angular/core/testing';
it('should have 1 routerLink in template', fakeAsync(() => {
allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
expect(allLinks.length).toBe(1, 'should have 1 link');
}));
RouterLink
import { RouterLinkWithHref } from '@angular/router';
import { fakeAsync } from '@angular/core/testing';
it('should have 1 routerLink in template', fakeAsync(() => {
allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
expect(allLinks.length).toBe(1, 'should have 1 link');
}));
RouterLink
import { RouterLinkWithHref } from '@angular/router';
import { fakeAsync } from '@angular/core/testing';
it('should have 1 routerLink in template', fakeAsync(() => {
allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
expect(allLinks.length).toBe(1, 'should have 1 link');
}));
RouterLink
import { RouterLinkWithHref } from '@angular/router';
import { fakeAsync } from '@angular/core/testing';
it('should have 1 routerLink in template', fakeAsync(() => {
allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
expect(allLinks.length).toBe(1, 'should have 1 link');
}));
Location Check
import { fakeAsync } from '@angular/core/testing';
it('all items takes me home', fakeAsync(() => {
const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
const linkDes = allLinkDes[0];
expect(location.path()).toBe('', 'link should not have navigated yet');
linkDes.triggerEventHandler('click', { button: 0 });
tick();
expect(location.path()).toBe('/items');
}));
Location Check
import { fakeAsync } from '@angular/core/testing';
it('all items takes me home', fakeAsync(() => {
const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
const linkDes = allLinkDes[0];
expect(location.path()).toBe('', 'link should not have navigated yet');
linkDes.triggerEventHandler('click', { button: 0 });
tick();
expect(location.path()).toBe('/items');
}));
Location Check
import { fakeAsync } from '@angular/core/testing';
it('all items takes me home', fakeAsync(() => {
const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
const linkDes = allLinkDes[0];
expect(location.path()).toBe('', 'link should not have navigated yet');
linkDes.triggerEventHandler('click', { button: 0 });
tick();
expect(location.path()).toBe('/items');
}));
Location Check
import { fakeAsync } from '@angular/core/testing';
it('all items takes me home', fakeAsync(() => {
const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
const linkDes = allLinkDes[0];
expect(location.path()).toBe('', 'link should not have navigated yet');
linkDes.triggerEventHandler('click', { button: 0 });
tick();
expect(location.path()).toBe('/items');
}));
Location Check
import { fakeAsync } from '@angular/core/testing';
it('all items takes me home', fakeAsync(() => {
const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
const linkDes = allLinkDes[0];
expect(location.path()).toBe('', 'link should not have navigated yet');
linkDes.triggerEventHandler('click', { button: 0 });
tick();
expect(location.path()).toBe('/items');
}));
http://digitaldrummerj.me
Document FunctionalityPinpoint Bugs
Check RegressionConfirm Functionality
Unit Test Will Make You Faster
http://digitaldrummerj.me
Checks Single
Assumption
Single Unit
Of Work
Automated
Code
Tenents
http://digitaldrummerj.me
RunnableDependableMaintainable
Good Unit Tests
http://digitaldrummerj.me
Resources
Angular Unit Testing Guide:
angular.io/guide/testing
Code Repository:
github.com/digitaldrummerj/angular-tutorial-code/tree/chapter-
unit-test
Slides:
slideshare.net/digitaldrummerj/angular-unit-testing-from-the-
trenches
http://digitaldrummerj.me
digitaldrummerj.me
@digitaldrummerj
http://digitaldrummerj.me
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
Angular Unit Testing from the Trenches

More Related Content

What's hot

Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
Jim Lynch
 
Angular Unit Testing
Angular Unit TestingAngular Unit Testing
Angular Unit Testing
Shailendra Chauhan
 
Unit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma introUnit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma intro
Maurice De Beijer [MVP]
 
AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit Test
Chiew Carol
 
Angular testing
Angular testingAngular testing
Angular testing
Raissa Ferreira
 
Testing React Applications
Testing React ApplicationsTesting React Applications
Testing React Applications
stbaechler
 
Jquery- One slide completing all JQuery
Jquery- One slide completing all JQueryJquery- One slide completing all JQuery
Jquery- One slide completing all JQuery
Knoldus Inc.
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
FITC
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It's
Jim Lynch
 
Angular Application Testing
Angular Application TestingAngular Application Testing
Angular Application Testing
Troy Miles
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with Jest
Michał Pierzchała
 
How Testability Inspires AngularJS Design / Ran Mizrahi
How Testability Inspires AngularJS Design / Ran MizrahiHow Testability Inspires AngularJS Design / Ran Mizrahi
How Testability Inspires AngularJS Design / Ran Mizrahi
Ran Mizrahi
 
Automated Web Testing using JavaScript
Automated Web Testing using JavaScriptAutomated Web Testing using JavaScript
Automated Web Testing using JavaScript
Simon Guest
 
JAVASCRIPT Test Driven Development & Jasmine
JAVASCRIPT Test Driven Development & JasmineJAVASCRIPT Test Driven Development & Jasmine
JAVASCRIPT Test Driven Development & Jasmine
Anup Singh
 
iOS Unit Testing
iOS Unit TestingiOS Unit Testing
iOS Unit Testing
sgleadow
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
noelrap
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
Rabble .
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
Jitendra Zaa
 

What's hot (20)

Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
 
Angular Unit Testing
Angular Unit TestingAngular Unit Testing
Angular Unit Testing
 
Unit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma introUnit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma intro
 
AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit Test
 
Angular testing
Angular testingAngular testing
Angular testing
 
Testing React Applications
Testing React ApplicationsTesting React Applications
Testing React Applications
 
Jquery- One slide completing all JQuery
Jquery- One slide completing all JQueryJquery- One slide completing all JQuery
Jquery- One slide completing all JQuery
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It's
 
Angular Application Testing
Angular Application TestingAngular Application Testing
Angular Application Testing
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with Jest
 
How Testability Inspires AngularJS Design / Ran Mizrahi
How Testability Inspires AngularJS Design / Ran MizrahiHow Testability Inspires AngularJS Design / Ran Mizrahi
How Testability Inspires AngularJS Design / Ran Mizrahi
 
Automated Web Testing using JavaScript
Automated Web Testing using JavaScriptAutomated Web Testing using JavaScript
Automated Web Testing using JavaScript
 
Codeception
CodeceptionCodeception
Codeception
 
JAVASCRIPT Test Driven Development & Jasmine
JAVASCRIPT Test Driven Development & JasmineJAVASCRIPT Test Driven Development & Jasmine
JAVASCRIPT Test Driven Development & Jasmine
 
Unit Testing in iOS
Unit Testing in iOSUnit Testing in iOS
Unit Testing in iOS
 
iOS Unit Testing
iOS Unit TestingiOS Unit Testing
iOS Unit Testing
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
 

Similar to Angular Unit Testing from the Trenches

Angular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptxAngular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptx
accordv12
 
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
Wim Selles
 
Ember testing internals with ember cli
Ember testing internals with ember cliEmber testing internals with ember cli
Ember testing internals with ember cli
Cory Forsyth
 
Pruebas en Plone: conceptos básicos y ejemplos
Pruebas en Plone: conceptos básicos y ejemplosPruebas en Plone: conceptos básicos y ejemplos
Pruebas en Plone: conceptos básicos y ejemplos
Héctor Velarde
 
Turner js
Turner jsTurner js
Turner js
Carmel Cohen
 
Gems Of Selenium
Gems Of SeleniumGems Of Selenium
Gems Of Selenium
Skills Matter
 
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
Codemotion
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
Visual Engineering
 
Protractor framework architecture with example
Protractor framework architecture with exampleProtractor framework architecture with example
Protractor framework architecture with example
shadabgilani
 
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
Wim Selles
 
Commit University - Exploring Angular 2
Commit University - Exploring Angular 2Commit University - Exploring Angular 2
Commit University - Exploring Angular 2
Commit University
 
UI Testing
UI TestingUI Testing
UI Testing
Josh Black
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
Andrea Paciolla
 
Test strategy for web development
Test strategy for web developmentTest strategy for web development
Test strategy for web developmentalice yang
 
Unit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and HowsUnit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and Hows
atesgoral
 
MeetJS Summit 2016: React.js enlightenment
MeetJS Summit 2016: React.js enlightenmentMeetJS Summit 2016: React.js enlightenment
MeetJS Summit 2016: React.js enlightenment
Artur Szott
 
mean stack
mean stackmean stack
mean stack
michaelaaron25322
 
Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2
Ahmed Moawad
 
Code igniter unittest-part1
Code igniter unittest-part1Code igniter unittest-part1
Code igniter unittest-part1
Albert Rosa
 
Test automation
Test  automationTest  automation
Test automation
Kaushik Banerjee
 

Similar to Angular Unit Testing from the Trenches (20)

Angular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptxAngular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptx
 
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
 
Ember testing internals with ember cli
Ember testing internals with ember cliEmber testing internals with ember cli
Ember testing internals with ember cli
 
Pruebas en Plone: conceptos básicos y ejemplos
Pruebas en Plone: conceptos básicos y ejemplosPruebas en Plone: conceptos básicos y ejemplos
Pruebas en Plone: conceptos básicos y ejemplos
 
Turner js
Turner jsTurner js
Turner js
 
Gems Of Selenium
Gems Of SeleniumGems Of Selenium
Gems Of Selenium
 
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Protractor framework architecture with example
Protractor framework architecture with exampleProtractor framework architecture with example
Protractor framework architecture with example
 
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
 
Commit University - Exploring Angular 2
Commit University - Exploring Angular 2Commit University - Exploring Angular 2
Commit University - Exploring Angular 2
 
UI Testing
UI TestingUI Testing
UI Testing
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
 
Test strategy for web development
Test strategy for web developmentTest strategy for web development
Test strategy for web development
 
Unit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and HowsUnit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and Hows
 
MeetJS Summit 2016: React.js enlightenment
MeetJS Summit 2016: React.js enlightenmentMeetJS Summit 2016: React.js enlightenment
MeetJS Summit 2016: React.js enlightenment
 
mean stack
mean stackmean stack
mean stack
 
Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2Exploring Angular 2 - Episode 2
Exploring Angular 2 - Episode 2
 
Code igniter unittest-part1
Code igniter unittest-part1Code igniter unittest-part1
Code igniter unittest-part1
 
Test automation
Test  automationTest  automation
Test automation
 

More from Justin James

KCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with SailsKCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with Sails
Justin James
 
StirTrek 2018 - Rapid API Development with Sails
StirTrek 2018 - Rapid API Development with SailsStirTrek 2018 - Rapid API Development with Sails
StirTrek 2018 - Rapid API Development with Sails
Justin James
 
Mobile Dev For Web Devs
Mobile Dev For Web DevsMobile Dev For Web Devs
Mobile Dev For Web Devs
Justin James
 
Up and Running with Angular
Up and Running with AngularUp and Running with Angular
Up and Running with Angular
Justin James
 
Everyone is a Public Speaker
Everyone is a Public SpeakerEveryone is a Public Speaker
Everyone is a Public Speaker
Justin James
 
Visual Studio Tools for Apache Cordova (TACO) and Ionic
Visual Studio Tools for Apache Cordova (TACO) and IonicVisual Studio Tools for Apache Cordova (TACO) and Ionic
Visual Studio Tools for Apache Cordova (TACO) and Ionic
Justin James
 
Ionic - Revolutionizing Hybrid Mobile Application Development
Ionic - Revolutionizing Hybrid Mobile Application DevelopmentIonic - Revolutionizing Hybrid Mobile Application Development
Ionic - Revolutionizing Hybrid Mobile Application Development
Justin James
 
Chocolatey - making the process of installing software on windows easy as pie
Chocolatey - making the process of installing software on windows easy as pieChocolatey - making the process of installing software on windows easy as pie
Chocolatey - making the process of installing software on windows easy as pie
Justin James
 
Nuget is easier than you think and you should be using it as both a consumer ...
Nuget is easier than you think and you should be using it as both a consumer ...Nuget is easier than you think and you should be using it as both a consumer ...
Nuget is easier than you think and you should be using it as both a consumer ...
Justin James
 

More from Justin James (9)

KCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with SailsKCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with Sails
 
StirTrek 2018 - Rapid API Development with Sails
StirTrek 2018 - Rapid API Development with SailsStirTrek 2018 - Rapid API Development with Sails
StirTrek 2018 - Rapid API Development with Sails
 
Mobile Dev For Web Devs
Mobile Dev For Web DevsMobile Dev For Web Devs
Mobile Dev For Web Devs
 
Up and Running with Angular
Up and Running with AngularUp and Running with Angular
Up and Running with Angular
 
Everyone is a Public Speaker
Everyone is a Public SpeakerEveryone is a Public Speaker
Everyone is a Public Speaker
 
Visual Studio Tools for Apache Cordova (TACO) and Ionic
Visual Studio Tools for Apache Cordova (TACO) and IonicVisual Studio Tools for Apache Cordova (TACO) and Ionic
Visual Studio Tools for Apache Cordova (TACO) and Ionic
 
Ionic - Revolutionizing Hybrid Mobile Application Development
Ionic - Revolutionizing Hybrid Mobile Application DevelopmentIonic - Revolutionizing Hybrid Mobile Application Development
Ionic - Revolutionizing Hybrid Mobile Application Development
 
Chocolatey - making the process of installing software on windows easy as pie
Chocolatey - making the process of installing software on windows easy as pieChocolatey - making the process of installing software on windows easy as pie
Chocolatey - making the process of installing software on windows easy as pie
 
Nuget is easier than you think and you should be using it as both a consumer ...
Nuget is easier than you think and you should be using it as both a consumer ...Nuget is easier than you think and you should be using it as both a consumer ...
Nuget is easier than you think and you should be using it as both a consumer ...
 

Recently uploaded

A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
Cyanic lab
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Globus
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
e20449
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
Tier1 app
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Shahin Sheidaei
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
kalichargn70th171
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Jay Das
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 

Recently uploaded (20)

A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 

Angular Unit Testing from the Trenches

  • 1. http://digitaldrummerj.me Justin James Web Developer Professional Speaker digitaldrummerj.me @digitaldrummerj
  • 2. http://digitaldrummerj.me unit tests are proof that my code does what I think it does
  • 3. http://digitaldrummerj.me Document FunctionalityPinpoint Bugs Check RegressionConfirm Functionality Unit Test Will Make You Faster
  • 5. http://digitaldrummerj.me We only get these advantages when we are comfortable writing good tests Disclaimer
  • 8. http://digitaldrummerj.me Tests Right ThingsContinued Relevance IsolatedConsistent Results Dependable
  • 9. http://digitaldrummerj.me Failures Point To The Problem Repeatable Fast Runnable Single Click
  • 10. http://digitaldrummerj.me Unit testing will not fix bad practices Don't do the right thing for the wrong reason Know the Goals
  • 11. http://digitaldrummerj.me "Don't let the fear that testing can't catch all bugs stop you from writing the tests that will catch most bugs" Martin Fowler Refactoring
  • 12. http://digitaldrummerj.me You need a strong grasp of the basic patterns in order to test an Angular application
  • 14. http://digitaldrummerj.me http://nodejs.org Node JS (6.9+) npm install -g @angular/cli NPM Global Packages Angular CLI Setup 14
  • 15. http://digitaldrummerj.me ng new AppName --style scss --routing Create New Project ng generate [TYPE] [NAME] Generate npm start Start Application npm test Unit Test Creating Applications 15
  • 16. http://digitaldrummerj.me Test Runner Test Framework Testing Utilities Karma Tools and Technology Jasmine Angular
  • 19. http://digitaldrummerj.me Debugging with Karma Use the Chrome Developer Console to debug tests Use console.log to observe data and events
  • 20. describe('First spec', () => { it('should pass', () => { expect(true).toBeTruthy(); }); }); Simple Passing Test
  • 21. describe('First spec', () => { it('should pass', () => { expect(true).toBeTruthy(); }); }); Simple Passing Test
  • 22. describe('First spec', () => { it('should pass', () => { expect(true).toBeTruthy(); }); }); Simple Passing Test
  • 23. describe(Second spec', () => { it('should fail', () => { expect(false).toBeTruthy(); }); }); Simple Failing Test
  • 25. @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string; constructor() { } ngOnInit() { } } Component
  • 26. @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string = "World"; constructor() { } ngOnInit() { } } Component
  • 27. @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string = "World"; constructor() { } ngOnInit() { } } Component
  • 29. http://digitaldrummerj.me TestBed Creates an Angular testing module Configure with TestBed.configureTestingModule Use BeforeEach to reset test module before each spec
  • 30. fixture import { async, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [SimpleComponent] }) .compileComponents() .then(() => { }); })); }
  • 31. fixture import { async, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [SimpleComponent] }) .compileComponents() .then(() => { }); })); }
  • 32. fixture import { async, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [SimpleComponent] }) .compileComponents() .then(() => { }); })); }
  • 33. fixture import { async, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [SimpleComponent] }) .compileComponents() .then(() => { }); })); }
  • 35. http://digitaldrummerj.me TestBed.createComponent Creates an instance of the component under test Returns a component test fixture Closes the TestBed from further configuration
  • 36. import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { let fixture: ComponentFixture<SimpleComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [SimpleComponent ] }) .compileComponents() .then(() => { fixture = TestBed.createComponent(SimpleComponent); }); })); } The Fixture
  • 37. http://digitaldrummerj.me 1. Configure Module 2. Create Fixture 3. Get Component Instance
  • 38. http://digitaldrummerj.me ComponentFixture Handle to the test environment fixture.componentInstance to access to component
  • 39. The Component Instance import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { let component: SimpleComponent; let fixture: ComponentFixture<SimpleComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ TemplateComponent ] }) .compileComponents() .then(() => { fixture = TestBed.createComponent(TemplateComponent); component = fixture.componentInstance; }); })); }
  • 40. The Component Instance it('sets the `subject` class member', () => { expect(component.subject).toBe('world'); });
  • 42. import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; describe('SimpleComponent', () => { let component: SimpleComponent; let fixture: ComponentFixture<SimpleComponent>; beforeEach(() => { fixture = TestBed.configureTestingModule({ declarations: [ SimpleComponent ] }) .createComponent(SimpleComponent) .then(() => { component = fixture.componentInstance; return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); })); } The Debug Element
  • 43. http://digitaldrummerj.me DebugElement DebugElement to access the component's DOM DebugElement.query to query the DOM By.css to query DOM using CSS selectors
  • 44. import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SimpleComponent } from './simple.component'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; describe('SimpleComponent', () => { let component: SimpleComponent; let fixture: ComponentFixture<SimpleComponent>; let element: DebugElement; beforeEach(() => { fixture = TestBed.configureTestingModule({ declarations: [ SimpleComponent ] }) .createComponent(SimpleComponent) .then(() => { component = fixture.componentInstance; element = fixture.debugElement; return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); })); } The Debug Element
  • 45. it('greets the subject', () => { const h1 = element.query(By.css('h1')); expect(h1.nativeElement.innerText).toBe('Hello world!'); }); The Debug Element
  • 46. it('greets the subject', () => { const h1 = element.query(By.css('h1')); expect(h1.nativeElement.innerText).toBe('Hello world!'); }); The Debug Element
  • 47. it('greets the subject', () => { const h1 = element.query(By.css('h1')); expect(h1.nativeElement.innerText).toBe('Hello world!'); }); The Debug Element
  • 49. Service export class GreetingService { subject: string = 'world'; }
  • 50. import { GreetingService } from '@./services/greeting.service'; @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string = this.service.subject; constructor(private service: GreetingService) { } ngOnInit() { } } Component
  • 51. import { GreetingService } from '@./services/greeting.service'; @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string = this.service.subject; constructor(private service: GreetingService) { } ngOnInit() { } } Component
  • 52. import { GreetingService } from '@./services/greeting.service'; @Component({ selector: 'app-service', templateUrl: './simple.component.html', styleUrls: ['./simple.component.scss'] }) export class SimpleComponent implements OnInit { subject: string = this.service.subject; constructor(private service: GreetingService) { } ngOnInit() { } } Component
  • 54. http://digitaldrummerj.me Don't Use Real Services Use Test Double
  • 55. http://digitaldrummerj.me Don't Use Real Services Use Test Double Use A Stub
  • 56. http://digitaldrummerj.me Don't Use Real Services Use Test Double Use A Stub Use A Spy
  • 58. Local Members describe('Simple Component with Service', () => { let component: SimpleComponent; let fixture: ComponentFixture<SimpleComponent>; let element: DebugElement; let greetingServiceStub: GreetingServiceStub let greetingService: GreetingService; });
  • 59. Local Members describe('Simple Component with Service', () => { let component: SimpleComponent; let fixture: ComponentFixture<SimpleComponent>; let element: DebugElement; let greetingServiceStub: GreetingServiceStub let greetingService: GreetingService; });
  • 60. export class GreetingServiceStub { service = "Test World!" } fixture = TestBed.configureTestingModule({ declarations: [SimpleComponent ], providers: [{ provide: GreetingService, useClass: GreetingServiceStub }] }) .createComponent(SimpleComponent) .then(() => { fixture = TestBed.createComponent(SimpleComponent); component = fixture.componentInstance; element = fixture.debugElement; greetingService = de.injector.get(GreetingService); return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); Test Double
  • 61. export class GreetingServiceStub { service = "Test World!" } fixture = TestBed.configureTestingModule({ declarations: [SimpleComponent ], providers: [{ provide: GreetingService, useClass: GreetingServiceStub }] }) .createComponent(SimpleComponent) .then(() => { fixture = TestBed.createComponent(SimpleComponent); component = fixture.componentInstance; element = fixture.debugElement; greetingService = de.injector.get(GreetingService); return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); Test Double
  • 63. export class GreetingServiceStub { service = "Test World!" } fixture = TestBed.configureTestingModule({ declarations: [SimpleComponent ], providers: [{ provide: GreetingService, useValue: greetingServiceStub }] }) .createComponent(SimpleComponent) .then(() => { fixture = TestBed.createComponent(SimpleComponent); component = fixture.componentInstance; element = fixture.debugElement; greetingService = de.injector.get(GreetingService); return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); }); Test Double
  • 64. Actual Test it('updates component subject when service subject is changed', () => { greetingService.subject.name = 'cosmos'; fixture.detectChanges(); expect(component.subject.name).toBe('cosmos'); const h1 = de.query(By.css('h1')).nativeElement; expect(h1.innerText).toBe('Hello cosmos!'); });
  • 65. Actual Test it('updates component subject when service subject is changed', () => { greetingService.subject.name = 'cosmos'; fixture.detectChanges(); expect(component.subject.name).toBe('cosmos'); const h1 = de.query(By.css('h1')).nativeElement; expect(h1.innerText).toBe('Hello cosmos!'); });
  • 66. Actual Test it('updates component subject when service subject is changed', () => { greetingService.subject.name = 'cosmos'; fixture.detectChanges(); expect(component.subject.name).toBe('cosmos'); const h1 = de.query(By.css('h1')).nativeElement; expect(h1.innerText).toBe('Hello cosmos!'); });
  • 67. Actual Test it('updates component subject when service subject is changed', () => { greetingService.subject.name = 'cosmos'; fixture.detectChanges(); expect(component.subject.name).toBe('cosmos'); const h1 = de.query(By.css('h1')).nativeElement; expect(h1.innerText).toBe('Hello cosmos!'); });
  • 68. Actual Test it('updates component subject when service subject is changed', () => { greetingService.subject.name = 'cosmos'; fixture.detectChanges(); expect(component.subject.name).toBe('cosmos'); const h1 = de.query(By.css('h1')).nativeElement; expect(h1.innerText).toBe('Hello cosmos!'); });
  • 70. Service @Injectable() export class GreetingService { subject: string = 'world' ; getGreeting() { return Promise.resolve('Hello'); } getSubject() { return Promise.resolve(this.subject.name); } getPunctuation() { return Promise.resolve('!'); } }
  • 71. Service @Injectable() export class GreetingService { subject: string = 'world' ; getGreeting() { return Promise.resolve('Hello'); } getSubject() { return Promise.resolve(this.subject.name); } getPunctuation() { return Promise.resolve('!'); } }
  • 72. Component export class SimpleComponent implements OnInit { subject: string; greeting: string; punctuation: string; constructor(private service: GreetingService) { } ngOnInit() { this.service.getGreeting() .then(res => this.greeting = res); this.service.getSubject() .then(res => this.subject = res); this.service.getPunctuation() .then(res => this.punctuation = res); } }
  • 73. Component export class SimpleComponent implements OnInit { subject: string; greeting: string; punctuation: string; constructor(private service: GreetingService) { } ngOnInit() { this.service.getGreeting() .then(res => this.greeting = res); this.service.getSubject() .then(res => this.subject = res); this.service.getPunctuation() .then(res => this.punctuation = res); } }
  • 74. Setup beforeEach(async(() => { fixture = TestBed.configureTestingModule({ declarations: [SimpleComponent ], providers: [ GreetingService ] }) .createComponent(SimpleComponent) .then(() => { component = fixture.componentInstance; element = fixture.debugElement; greetingService = de.injector.get(GreetingService); return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); }));
  • 75. Setup beforeEach(async(() => { fixture = TestBed.configureTestingModule({ declarations: [SimpleComponent ], providers: [ GreetingService ] }) .createComponent(SimpleComponent) .then(() => { component = fixture.componentInstance; element = fixture.debugElement; greetingService = de.injector.get(GreetingService); return fixture.whenStable().then(() => { fixture.detectChanges(); }); }); }));
  • 76. Spy with fixture.whenStable it('gets `greeting` after promise (async)', async(() => { spyOn(greetingService, 'getGreeting') .and.returnValue(Promise.resolve('Greetings')); expect(component.greeting).toBe('Greetings'); }); }));
  • 77. Spy with fixture.whenStable it('gets `greeting` after promise (async)', async(() => { spyOn(greetingService, 'getGreeting') .and.returnValue(Promise.resolve('Greetings')); expect(component.greeting).toBe('Greetings'); }); }));
  • 78. Spy with fixture.whenStable it('gets `greeting` after promise (async)', async(() => { spyOn(greetingService, 'getGreeting') .and.returnValue(Promise.resolve('Greetings')); expect(component.greeting).toBe('Greetings'); }); }));
  • 79. Spy with fixture.whenStable it('gets `greeting` after promise (async)', async(() => { spyOn(greetingService, 'getGreeting') .and.returnValue(Promise.resolve('Greetings')); expect(component.greeting).toBe('Greetings'); }); }));
  • 81. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 82. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 83. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 84. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 85. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 86. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 87. Spy with tick it('gets `subject` after promise (fakeAsync)', fakeAsync(() => { spyOn(greetingService, 'getSubject') .and.returnValue(Promise.resolve('universe')); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(component.subject).toBe('universe'); }));
  • 89. http://digitaldrummerj.me Component with Inputs and Outputs The goal is to ensure that binding works as expected Test input: simply update value and ensure it renders Test output: trigger triggerEventHandler and subscribe to the EventEmitter
  • 90. @Component({ selector: 'app-input-output', template: ` <h1>Hello {{subject}}!</h1> <button (click)="depart()">We Out</button> ` }) export class InputOutputComponent { @Input('subject') subject: string; @Output('leave') leave: EventEmitter<string> = new EventEmitter(); depart() { this.leave.emit(`Later ${this.subject}!`); } } Component
  • 91. @Component({ selector: 'app-input-output', template: ` <h1>Hello {{subject}}!</h1> <button (click)="depart()">We Out</button> ` }) export class InputOutputComponent { @Input('subject') subject: string; @Output('leave') leave: EventEmitter<string> = new EventEmitter(); depart() { this.leave.emit(`Later ${this.subject}!`); } } Component
  • 92. beforeEach(() => { fixture = TestBed.configureTestingModule({ declarations: [ InputOutputComponent ] }) .createComponent(InputOutputComponent); component = fixture.componentInstance; de = fixture.debugElement; button = de.query(By.css('button')); component.subject = 'galaxy'; fixture.detectChanges(); }); Setup
  • 93. beforeEach(() => { fixture = TestBed.configureTestingModule({ declarations: [ InputOutputComponent ] }) .createComponent(InputOutputComponent); component = fixture.componentInstance; de = fixture.debugElement; button = de.query(By.css('button')); component.subject = 'galaxy'; fixture.detectChanges(); }); Setup
  • 94. beforeEach(() => { fixture = TestBed.configureTestingModule({ declarations: [ InputOutputComponent ] }) .createComponent(InputOutputComponent); component = fixture.componentInstance; de = fixture.debugElement; button = de.query(By.css('button')); component.subject = 'galaxy'; fixture.detectChanges(); }); Setup
  • 95. it('has `subject` as an @Input', () => { expect(component.subject).toBe('galaxy'); }); Input
  • 96. it('says goodbye to the `subject`', () => { let farewell; component.leave.subscribe(event => farewell = event); button.triggerEventHandler('click', { button: 0 }); expect(farewell).toBe('Later galaxy!'); }); Output
  • 97. it('says goodbye to the `subject`', () => { let farewell; component.leave.subscribe(event => farewell = event); button.triggerEventHandler('click', { button: 0 }); expect(farewell).toBe('Later galaxy!'); }); Output
  • 98. it('says goodbye to the `subject`', () => { let farewell; component.leave.subscribe(event => farewell = event); button.triggerEventHandler('click', { button: 0 }); expect(farewell).toBe('Later galaxy!'); }); Output
  • 100. http://digitaldrummerj.me Routed Component Avoid router complexity. Use RouterTestingModule Test that component navigates to proper route
  • 101. beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ {path: '', redirectTo: '/items', pathMatch: 'full' }, {path: 'items', component: ItemsComponent}, {path: 'routed/:subject', component: RoutedComponent}, {path: 'widgets', component: WidgetsComponent}, {path: '**', redirectTo: '/items', pathMatch: 'full'} ]); ], declarations: [ RoutedComponent ] }) .createComponent(RoutedComponent); component = fixture.componentInstance; de = fixture.debugElement; fixture.detectChanges(); }); Routes
  • 102. import { RouterTestingModule } from '@angular/router/testing'; beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ {path: '', redirectTo: '/items', pathMatch: 'full' }, {path: 'items', component: ItemsComponent}, {path: 'routed/:subject', component: RoutedComponent}, {path: 'widgets', component: WidgetsComponent}, {path: '**', redirectTo: '/items', pathMatch: 'full'} ]); ], declarations: [ RoutedComponent ] }) .createComponent(RoutedComponent); component = fixture.componentInstance; de = fixture.debugElement; fixture.detectChanges(); }); Routes
  • 103. import { RouterTestingModule } from '@angular/router/testing'; beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ {path: '', redirectTo: '/items', pathMatch: 'full' }, {path: 'items', component: ItemsComponent}, {path: 'routed/:subject', component: RoutedComponent}, {path: 'widgets', component: WidgetsComponent}, {path: '**', redirectTo: '/items', pathMatch: 'full'} ]); ], declarations: [ RoutedComponent ] }) .createComponent(RoutedComponent); component = fixture.componentInstance; de = fixture.debugElement; fixture.detectChanges(); }); Routes
  • 104. RouterLink import { RouterLinkWithHref } from '@angular/router'; import { fakeAsync } from '@angular/core/testing'; it('should have 1 routerLink in template', fakeAsync(() => { allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(allLinks.length).toBe(1, 'should have 1 link'); }));
  • 105. RouterLink import { RouterLinkWithHref } from '@angular/router'; import { fakeAsync } from '@angular/core/testing'; it('should have 1 routerLink in template', fakeAsync(() => { allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(allLinks.length).toBe(1, 'should have 1 link'); }));
  • 106. RouterLink import { RouterLinkWithHref } from '@angular/router'; import { fakeAsync } from '@angular/core/testing'; it('should have 1 routerLink in template', fakeAsync(() => { allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(allLinks.length).toBe(1, 'should have 1 link'); }));
  • 107. RouterLink import { RouterLinkWithHref } from '@angular/router'; import { fakeAsync } from '@angular/core/testing'; it('should have 1 routerLink in template', fakeAsync(() => { allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(allLinks.length).toBe(1, 'should have 1 link'); }));
  • 108. RouterLink import { RouterLinkWithHref } from '@angular/router'; import { fakeAsync } from '@angular/core/testing'; it('should have 1 routerLink in template', fakeAsync(() => { allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(allLinks.length).toBe(1, 'should have 1 link'); }));
  • 109. Location Check import { fakeAsync } from '@angular/core/testing'; it('all items takes me home', fakeAsync(() => { const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); const linkDes = allLinkDes[0]; expect(location.path()).toBe('', 'link should not have navigated yet'); linkDes.triggerEventHandler('click', { button: 0 }); tick(); expect(location.path()).toBe('/items'); }));
  • 110. Location Check import { fakeAsync } from '@angular/core/testing'; it('all items takes me home', fakeAsync(() => { const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); const linkDes = allLinkDes[0]; expect(location.path()).toBe('', 'link should not have navigated yet'); linkDes.triggerEventHandler('click', { button: 0 }); tick(); expect(location.path()).toBe('/items'); }));
  • 111. Location Check import { fakeAsync } from '@angular/core/testing'; it('all items takes me home', fakeAsync(() => { const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); const linkDes = allLinkDes[0]; expect(location.path()).toBe('', 'link should not have navigated yet'); linkDes.triggerEventHandler('click', { button: 0 }); tick(); expect(location.path()).toBe('/items'); }));
  • 112. Location Check import { fakeAsync } from '@angular/core/testing'; it('all items takes me home', fakeAsync(() => { const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); const linkDes = allLinkDes[0]; expect(location.path()).toBe('', 'link should not have navigated yet'); linkDes.triggerEventHandler('click', { button: 0 }); tick(); expect(location.path()).toBe('/items'); }));
  • 113. Location Check import { fakeAsync } from '@angular/core/testing'; it('all items takes me home', fakeAsync(() => { const allLinks = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); const linkDes = allLinkDes[0]; expect(location.path()).toBe('', 'link should not have navigated yet'); linkDes.triggerEventHandler('click', { button: 0 }); tick(); expect(location.path()).toBe('/items'); }));
  • 114. http://digitaldrummerj.me Document FunctionalityPinpoint Bugs Check RegressionConfirm Functionality Unit Test Will Make You Faster
  • 117. http://digitaldrummerj.me Resources Angular Unit Testing Guide: angular.io/guide/testing Code Repository: github.com/digitaldrummerj/angular-tutorial-code/tree/chapter- unit-test Slides: slideshare.net/digitaldrummerj/angular-unit-testing-from-the- trenches

Editor's Notes

  1. Modules – building blocks that contain routes, components, services, and more. Components – contains a template, data and logic forming part of a DOM tree. Routing – Renders a component based on the URL state, drives navigation. Services – Data layer, logic that is not component related, such as API requests.
  2. Done http://sailsjs.com/get-started
  3. Test runner that is used to execute Angular unit tests Installed and configured by default with Angular CLI project Configured via the karma.conf.js file Tests (specs) are identified with a .spec.ts naming convention