create a new component named user with command
ng g c user
change the new component's markup to
<div *ngIf='isLoggedIn'>
<h1>User logged in</h1>
<p>User is : {{user.name}}</p>
</div>
<div *ngIf='!isLoggedIn'>
<h1>User not logged in</h1>
<p>Please log in first</p>
</div>
in user.component add the properties shown below
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
user: { name: string };
isLoggedIn = false;
constructor() { }
ngOnInit() { }
}
edit user.component.spec.ts as shown below
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserComponent } from './user.component';
describe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [UserComponent]
})
.compileComponents();
}));
it('should create the app', () => {
fixture = TestBed.createComponent(UserComponent);
let app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
run the tests using the command
ng test
Top
Index
testing dependencies : components and services
add file
user.service.ts to user folder
edit the file as shown below
export class UserService {
user = { name: 'Flip' };
}
in user.component inject the service adding the service as a provider
in ngOnInit set the user property to the user the service provides
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css'],
providers: [UserService]
})
export class UserComponent implements OnInit {
user: { name: string };
isLoggedIn = false;
constructor(private userService: UserService) { }
ngOnInit() {
this.user = this.userService.user;
}
}
in user.component.spec.ts add a new test to check the if the service is injected
in the browser Karma should show no user is logged in
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserComponent } from './user.component';
import { UserService } from './user.service';
describe('UserComponent', () => {
...
it('should user the user name from the service', () => {
fixture = TestBed.createComponent(UserComponent);
let app = fixture.debugElement.componentInstance;
// get an instance of the service
let userService = fixture.debugElement.injector.get(UserService);
// inject the service
fixture.detectChanges();
expect(userService.user.name).toEqual(app.user.name);
});
...
});
add a new test to check the user name is shown when the user is logged in
add a similar test to check the user name is not shwon when the user is not logged
in
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserComponent } from './user.component';
import { UserService } from './user.service';
describe('UserComponent', () => {
...
it('should show the user name when logged in', () => {
fixture = TestBed.createComponent(UserComponent);
let app = fixture.debugElement.componentInstance;
app.isLoggedIn = true;
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).toContain(app.user.name);
});
it('should not show the user name when not logged in', () => {
fixture = TestBed.createComponent(UserComponent);
let app = fixture.debugElement.componentInstance;
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).not.toContain(app.user.name);
});
});
Top
Index
add a service to the sample
service imitates an asynchronous task
export class DataService {
getDetails() {
const resultPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data');
}, 1500);
});
return resultPromise;
}
}
inject the service into the component
add a data property and set the property in ngInit
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
import { DataService } from '../shared/data.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css'],
providers: [UserService, DataService]
})
export class UserComponent implements OnInit {
user: { name: string };
isLoggedIn = false;
data: string;
constructor(private userService: UserService, private dataService: DataService) { }
ngOnInit() {
this.user = this.userService.user;
this.dataService.getDetails()
.then((data: string) => this.data = data);
}
}
add two tests to user.component.spec.ts
each test uses the spyOn method of testing environment
the returnValue method supplies the result expected of the service
the first test calls the service synchronously
the call returns immediately with a null returned
failure or success depends upon the argument to the toBe method
when the arg is undefined the test succeeds
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserComponent } from './user.component';
import { UserService } from './user.service';
import { DataService } from '../shared/data.service';
describe('UserComponent', () => {
const component: UserComponent = null;
let fixture: ComponentFixture<UserComponent>;
...
it('should not fetch data successfully if not called asynchronously', () => {
fixture = TestBed.createComponent(UserComponent);
const app = fixture.debugElement.componentInstance;
const dataService = fixture.debugElement.injector.get(DataService);
// method of testing environment
const spy = spyOn(dataService, 'getDetails')
// will execute async code but returns provided data
// will run in async fashion
.and.returnValue(Promise.resolve('Data'));
fixture.detectChanges();
// OK
expect(app.data).toBe(undefined);
// fails
// expect(app.data).toBe('Data');
});
the second test wraps the callback using the async method
fixture.whenStable waits for the service method to complete and its callback contains
the expect().toBe() methods
because the call completes asynchronously the service method 'returns' the argument
to the returnValue method
// wrap callback with async function
it('should fetch data successfully if called asynchronously', async(() => {
fixture = TestBed.createComponent(UserComponent);
const app = fixture.debugElement.componentInstance;
const dataService = fixture.debugElement.injector.get(DataService);
// method of testing environment
const spy = spyOn(dataService, 'getDetails')
.and.returnValue(Promise.resolve('Data'));
fixture.detectChanges();
// waits for all async tasks to finish
fixture.whenStable().then(() => {
expect(app.data).toBe('Data');
});
}));
});
Top
Index
using "fakeAsync" and "tick"
add new test copying the previous test
change async call to fakeAsync
when using fakeAsync there is no need to call fixture.whenStable()
instead call tick method before expect method
same test as before just a different way to run it
import { async, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { UserComponent } from './user.component';
import { UserService } from './user.service';
import { DataService } from '../shared/data.service';
describe('UserComponent', () => {
const component: UserComponent = null;
let fixture: ComponentFixture<UserComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [UserComponent]
})
.compileComponents();
}));
...
// wrap callback with fakeAsync function
it('should fetch data successfully if called asynchronously', fakeAsync(() => {
fixture = TestBed.createComponent(UserComponent);
const app = fixture.debugElement.componentInstance;
const dataService = fixture.debugElement.injector.get(DataService);
// method of testing environment
const spy = spyOn(dataService, 'getDetails')
.and.returnValue(Promise.resolve('Data'));
fixture.detectChanges();
// call tick before expect
// in fakeAsync environment wait for all async tasks to complete
tick();
expect(app.data).toBe('Data');
}));
});
Top
Index
isolated vs. non-isolated tests
a pipe or simple method call can be tested independently from Angular
an isolated test can be run when the thing to be tested does not rely on Angular
or other pieces of the application
if not isolated must use testing methods
a simple pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'reverse'
})
export class ReversePipe implements PipeTransform {
transform(value: string) {
return value.split('').reverse().join('');
}
}
the isolated test
no Angular or other dependencies imported
import {ReversePipe } from './reverse.pipe';
describe('Pipe: ReversePipe', () => {
it('should reverse the inputs', () => {
const reversePipe = new ReversePipe();
expect(reversePipe.transform('hello')).toEqual('olleh');
});
});
Top
Index