Select theme:
Checkout Radzen Demos and download latest Radzen!
Unit tests are a first class citizen in Angular 2. The officially recommended test runner is Karma and the testing library is Jasmine. Karma executes unit tests in a browser (sometimes a headless one such as PhantomJS). Thus unit tests run in a close to real-world environment.
In this blog post I will show you a different approach to running tests - with Mocha, Chai and Sinon. Mocha has a test runner which works with Node and does not need a browser. We need Chai and sinon because Mocha does not include assertion and mocking libraries. We will use Webpack for bundling and module loading.
We need a simple Angular 2 Webpack application. Let's use the one from the official Webpack introduction.
Then we need to install some npm packages:
npm install --save-dev mocha mocha-webpack chai @types/chai sinon @types/sinon webpack-node-externals jsdom
Modify the AppComponent from the Webpack introduction tutorial to implement a few simple tasks.
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: require('./app.component.html')
})
export class AppComponent {
value = 0;
onIncrementClick() {
this.value = Math.min(100, ++this.value);
}
onDecrementClick() {
this.value = Math.max(-100, --this.value);
}
}
<main>
<h2>Value: {{ value }}</h2>
<button class="increment" (click)="onIncrementClick()">+</button>
<button class="decrement" (click)="onDecrementClick()">-</button>
</main>
Add tests for those tasks.
import { getTestBed, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { By } from '@angular/platform-browser';
import { expect } from 'chai';
import { spy } from 'sinon';
describe(`AppComponent ${i}`, () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent]
});
});
afterEach(() => {
getTestBed().resetTestingModule();
});
it('should display 0 as initial value', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const h2 = fixture.debugElement.query(By.css('h2'));
expect(h2.nativeElement.textContent).to.equal('Value: 0');
});
it('should increment the value', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.componentInstance.onIncrementClick();
fixture.detectChanges();
const h2 = fixture.debugElement.query(By.css('h2'));
expect(h2.nativeElement.textContent).to.equal('Value: 1');
});
it('should invoke onIncrementClick when the user clicks the increment button', () => {
const fixture = TestBed.createComponent(AppComponent);
const onIncrementClick = spy(fixture.componentInstance, 'onIncrementClick');
const button = fixture.debugElement.query(By.css('.increment'));
button.triggerEventHandler('click', {});
expect(onIncrementClick.called).to.equal(true);
});
/* snip */
});
There are a few differences with the Karma/Jasmine approach.
expect(value).to.equal(true)
instead of expect(value).toEqual(true)
.First we need to create e Webpack config file that will ignore Node server-side modules during packaging.
var webpackMerge = require('webpack-merge');
var nodeExternals = require('webpack-node-externals');
var commonConfig = require('./webpack.test');
module.exports = webpackMerge(commonConfig, {
externals: [
nodeExternals()
]
});
Then we need to create a shim file which will load all dependencies and configure Angular for testing. This
file is very similar to karma-test-shim.js
from the official Webpack introduction tutorial.
var jsdom = require('jsdom')
var document = jsdom.jsdom('<!doctype html><html><body></body></html>');
var window = document.defaultView;
global.document = document;
global.HTMLElement = window.HTMLElement;
global.XMLHttpRequest = window.XMLHttpRequest;
require('core-js/es6');
require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
The biggest difference is that we are using jsdom
to provide stubs for a few DOM APIs that Angular 2 needs.
We also need a configuration file for mocha-webpack
in order to avoid passing all options through command line.
--webpack-config config/webpack.mocha.js
--require config/mocha-test-shim.js
src/**/*.spec.ts
The first line specifies the Webpack config file, the second line includes the shim and the last line specifies where the test files are.
Finally we can update the npm test scripts in package.json
to run our tests with mocha-webpack
.
"scripts": {
"test": "mocha-webpack",
"test:watch": "mocha-webpack --watch"
}
Running the tests is as simple as npm test
. To run them in watch mode use npm run test:watch
.
Let's compare the test running time of Karma and Mocha. The angular2-webpack-mocha github repo is a sample application configured for both Karma and Mocha. Here are the results of running the tests 10 times:
Mocha is more than one second faster than Karma. Running the tests 100 times show 15-20% improvement over Karma.
Testing in watch mode also feels snappier with Mocha.
Radzen is free to use. You can also test the premium features for 15 days.
Download NowSelect theme: