Test Automation
For tests we decided to use Karma -http://karma-runner.github.io/1.0/index.html
The Karma test runner is ideal for writing and running unit tests while developing the application. It can be an integral part of the project's development and continuous integration processes.
Next i will describe step by step how to create test in context of out project structure. We have three main file categories - Components, Services, Modules.
Testing Components:
Let's assume we have a components folder with following structure:
-app
-components
-testComponent
-test.component.ts
-test.component.html
-test.component.css
/app/components/test.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent {
title = "Hello world!!!";
}
/app/components/test.component.html
<h1>
{{title}}
</h1>
/app/components/test.component.css
h1 {
color : red;
}
So for create a test you should to do following:
1) Create file test.component.spec.ts in the root of yours components directory. You can create file with any name by "spec.ts" should be always present in it.
2) Add to /app/components/test.component.spec.ts file following content:
import { TestBed, async } from '@angular/core/testing';
import { TestComponent } from './test.component';
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestComponent
]
});
TestBed.compileComponents();
});
it('should create the test app', async(() => {
const fixture = TestBed.createComponent(TestComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'Hello world!!!'`, async(() => {
const fixture = TestBed.createComponent(TestComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('Hello world!!!');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Hello world!!!');
}));
});
Testing services and models
Let's assume we have a folder with following structure:
-app
-services
-todo.data.service.ts
-models
-todo.ts
/app/services/todo.data.service.ts
import {Injectable} from '@angular/core';
import {Todo} from '../model/item';
@Injectable()
export class TodoDataService {
// Placeholder for last id so we can simulate
// automatic incrementing of id's
lastId: number = 0;
// Placeholder for todo's
todos: Todo[] = [];
constructor() {
}
// Simulate POST /todos
addTodo(todo: Todo): TodoDataService {
if (!todo.id) {
todo.id = ++this.lastId;
}
this.todos.push(todo);
return this;
}
// Simulate DELETE /todos/:id
deleteTodoById(id: number): TodoDataService {
this.todos = this.todos
.filter(todo => todo.id !== id);
return this;
}
// Simulate PUT /todos/:id
updateTodoById(id: number, values: Object = {}): Todo {
let todo = this.getTodoById(id);
if (!todo) {
return null;
}
Object.assign(todo, values);
return todo;
}
// Simulate GET /todos
getAllTodos(): Todo[] {
return this.todos;
}
// Simulate GET /todos/:id
getTodoById(id: number): Todo {
return this.todos
.filter(todo => todo.id === id)
.pop();
}
// Toggle todo complete
toggleTodoComplete(todo: Todo){
let updatedTodo = this.updateTodoById(todo.id, {
complete: !todo.complete
});
return updatedTodo;
}
}
/app/models/todo.ts
export class Todo {
id: number;
title: string = '';
complete: boolean = false;
constructor(values: Object = {}) {
Object.assign(this, values);
}
}
Next you need to create two test files for each component with names todo.spec.ts and todo.service.test.ts with following contents:
/app/services/todo.service.test.ts
import {TestBed, async, inject} from '@angular/core/testing';
import {Todo} from '../model/todo';
import {TodoDataService} from './todo-data.service';
describe('TodoDataService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TodoDataService]
});
});
it('should ...', inject([TodoDataService], (service: TodoDataService) => {
expect(service).toBeTruthy();
}));
describe('#getAllTodos()', () => {
it('should return an empty array by default', inject([TodoDataService], (service: TodoDataService) => {
expect(service.getAllTodos()).toEqual([]);
}));
it('should return all todos', inject([TodoDataService], (service: TodoDataService) => {
let todo1 = new Todo({title: 'Hello 1', complete: false});
let todo2 = new Todo({title: 'Hello 2', complete: true});
service.addTodo(todo1);
service.addTodo(todo2);
expect(service.getAllTodos()).toEqual([todo1, todo2]);
}));
});
describe('#save(todo)', () => {
it('should automatically assign an incrementing id', inject([TodoDataService], (service: TodoDataService) => {
let todo1 = new Todo({title: 'Hello 1', complete: false});
let todo2 = new Todo({title: 'Hello 2', complete: true});
service.addTodo(todo1);
service.addTodo(todo2);
expect(service.getTodoById(1)).toEqual(todo1);
expect(service.getTodoById(2)).toEqual(todo2);
}));
});
describe('#deleteTodoById(id)', () => {
it('should remove todo with the corresponding id', inject([TodoDataService], (service: TodoDataService) => {
let todo1 = new Todo({title: 'Hello 1', complete: false});
let todo2 = new Todo({title: 'Hello 2', complete: true});
service.addTodo(todo1);
service.addTodo(todo2);
expect(service.getAllTodos()).toEqual([todo1, todo2]);
service.deleteTodoById(1);
expect(service.getAllTodos()).toEqual([todo2]);
service.deleteTodoById(2);
expect(service.getAllTodos()).toEqual([]);
}));
it('should not removing anything if todo with corresponding id is not found', inject([TodoDataService], (service: TodoDataService) => {
let todo1 = new Todo({title: 'Hello 1', complete: false});
let todo2 = new Todo({title: 'Hello 2', complete: true});
service.addTodo(todo1);
service.addTodo(todo2);
expect(service.getAllTodos()).toEqual([todo1, todo2]);
service.deleteTodoById(3);
expect(service.getAllTodos()).toEqual([todo1, todo2]);
}));
});
describe('#updateTodoById(id, values)', () => {
it('should return todo with the corresponding id and updated data', inject([TodoDataService], (service: TodoDataService) => {
let todo = new Todo({title: 'Hello 1', complete: false});
service.addTodo(todo);
let updatedTodo = service.updateTodoById(1, {
title: 'new title'
});
expect(updatedTodo.title).toEqual('new title');
}));
it('should return null if todo is not found', inject([TodoDataService], (service: TodoDataService) => {
let todo = new Todo({title: 'Hello 1', complete: false});
service.addTodo(todo);
let updatedTodo = service.updateTodoById(2, {
title: 'new title'
});
expect(updatedTodo).toEqual(null);
}));
});
describe('#toggleTodoComplete(todo)', () => {
it('should return the updated todo with inverse complete status', inject([TodoDataService], (service: TodoDataService) => {
let todo = new Todo({title: 'Hello 1', complete: false});
service.addTodo(todo);
let updatedTodo = service.toggleTodoComplete(todo);
expect(updatedTodo.complete).toEqual(true);
service.toggleTodoComplete(todo);
expect(updatedTodo.complete).toEqual(false);
}));
});
});
/app/models/todo.spec.ts
import {Todo} from './todo';
describe('Todo', () => {
it('should create an instance', () => {
expect(new Todo()).toBeTruthy();
});
it('should accept values in the constructor', () => {
let todo = new Todo({
title: 'hello',
complete: true
});
expect(todo.title).toEqual('hello');
expect(todo.complete).toEqual(true);
});
});
Next you can execute test by run "npm test" command in the root of your Angular 2 project.
You will have test execution information in terminal and in yours browser also.
Useful information:
How to test services -https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-dependency
How test test Asynchronous services -https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-async-service
How to test routes -https://angular.io/docs/ts/latest/guide/testing.html#!#routed-component
Last updated