testing with node.js
DESCRIPTION
An introduction on testing Node.js code. Covers frontend (UI) testing, backend unit tests and code coverage. Also mentions how to use a Makefile to run frontend and backend tests at the same time.TRANSCRIPT
withTesting
1
Self introduction
Jonathan Waller
2
Content
What is Node.js?
Testing the code
Checking test coverage
Testing the user interface
Pulling it all together
3
4
WHAT IS NODE.JS?
What is Node.js?
$ node example.jsServer running at http://127.0.0.1:8080/
var http = require('http');
http.createServer(function (request, response) { response.writeHead(200, {'Content-‐Type': 'text/plain'}); response.end('Hello World\n');}).listen(8080);
console.log('Server running at http://127.0.0.1:8080/');
Running it
example.js
5
Node is non-blockingBlocking code
var fileContents1 = fs.readFileSync('file1.txt');console.log(fileContents1);
var fileContents2 = fs.readFileSync('file2.txt');console.log(fileContents2);
6
0s 5s 10s
Node is non-blockingNon-blocking code
var callback = function(err, fileContents){ console.log(fileContents);}
fs.readFile('file1.txt', callback);fs.readFile('file2.txt', callback);
7
0s 5s 10s
$ node example.js
8
9
TESTING THE CODE
Testing the code
10
EXPECT.JS
MOCHA
expect(myObject.id).to.be(undefined);expect(myObject).to.eql({ a: 'b' })expect(myVariable1).to.be.a('number');expect(myVariable2).to.be.an('array');
describe('file.js', function() { describe('functionName', function() { it('action', function() { ...
11
function readFile1(){ return fs.readFileSync('file1.txt');}
function readFile2(){ return fs.readFileSync('file2.txt');}
modules.exports.readFile1 = readFile1;modules.exports.readFile2 = readFile2;
12
SYNC_EXAMPLE.JS
TESTING SYNCHRONOUS CODE 1
var fileContents1 = fs.readFileSync('file1.txt');console.log(fileContents1);
var fileContents2 = fs.readFileSync('file2.txt');console.log(fileContents2);
var sync_example = require(‘./sync_example’);
TEST/SYNC_EXAMPLE.JS
SYNC_EXAMPLE.JS
function readFile1(){ return fs.readFileSync('file1.txt');}
function readFile2(){ return fs.readFileSync('file2.txt');}
modules.exports.readFile1 = readFile1;modules.exports.readFile2 = readFile2;
13
var sync_example = require(‘../sync_example’);
describe('sync_example.js', function() { describe('readFile1', function() { it('reads the content of the file', function() { var fileContents1 = sync_example.readFile1(); expect(fileContents1.length).to.be.greaterThan(0); }); }); describe('readFile2', function() { it('reads the content of the file', function() { var fileContents2 = sync_example.readFile2(); expect(fileContents2.length).to.be.greaterThan(0); }); });});
TEST/SYNC_EXAMPLE.JS
SYNC_EXAMPLE.JS
TESTING SYNCHRONOUS CODE 2
function readFile1(callback){ fs.readFile('file1.txt', callback);}
function readFile2(callback){ fs.readFile('file2.txt', callback);}
modules.exports.readFile1 = readFile1;modules.exports.readFile2 = readFile2;
14
ASYNC_EXAMPLE.JS
TESTING ASYNCHRONOUS CODE 1
var callback = function(err, fileContents){ console.log(fileContents);}
fs.readFile('file1.txt', callback);fs.readFile('file2.txt', callback);
ASYNC_EXAMPLE.JS
function readFile1(callback){ fs.readFile('file1.txt', callback);}
function readFile2(callback){ fs.readFile('file2.txt', callback);}
modules.exports.readFile1 = readFile1;modules.exports.readFile2 = readFile2;
15
describe('async_example.js', function() { describe('readFile1', function() { it('reads the content of the file', function(done) { async_example.readFile1(function(err,fileContents1){ expect(fileContents1.length).to.be.greaterThan(0); done(); }); }); }); describe('readFile2', function() { it('reads the content of the file', function(done) { async_example.readFile2(function(err,fileContents2){ expect(fileContents2.length).to.be.greaterThan(0); done(); }); }); });});
TEST/ASYNC_EXAMPLE.JS
ASYNC_EXAMPLE.JS
TESTING ASYNCHRONOUS CODE 2
16
async_example.readFile1(function(err,fileContents1){ expect(fileContents1.length).to.be.greaterThan(0); done();});
TESTING ASYNCHRONOUS CODE 3
var callback1 = function(err,fileContents1){ expect(fileContents1.length).to.be.greaterThan(0); done();}async_example.readFile1(callback1);
=
$ mocha test/example.js -‐r expect.js
17
JSCoverage
18
CHECKING TEST COVERAGE
19
Checking test coverage
20
JSCoveragejson-cov$ jscoverage index.js
Checking test coverage
21
Running tests (Running the instrumented code)$ mocha test/index.js -‐R json-‐cov > coverage.html
$ jscoverage index.js
Instrumenting JS file
SpookyJS
22
TESTING THE USER INTERFACE
UI testingSpookyJS is a scriptable web testing framework for Mocha
Wrapper for CasperJs and PhantomJS
Uses WebKit, so supports client-side Javascript
23
WEBKIT
CasperJS
SpookyJS
var Spooky = require('spooky');
var spooky = new Spooky( { child: { port: 8080, script: './lib/bootstrap.js', //Loads casperJS spooky_lib: './node_modules' } }, function (err, error, response) { if (err || error) { var e = new Error('Failed to initialize SpookyJS'); e.details = err || error; throw e; }
spooky.on('error', function (e) {console.error(e);}); spooky.on('console', function (line) {console.log(line);});
spooky.start();
spooky.then(function (){ this.echo('Hello, this is SpookyJS'); });
spooky.open('http://www.google.com/');
spooky.then(function () { this.echo('Now viewing: ' + this.getCurrentUrl()); });
spooky.run(); });
24
SAMPLE SPOOKY SCRIPT
var util = require('util');var expect = require('expect.js');
describe("Test that SpookyJS is working", function () { var context = {}; var hooks = require('../util/hooks');
before(hooks.before(context));
describe('Test that SpookyJS can navigate to Google', function () { it('navigates to google.com, and returns the current url', function (done) {
context.spooky.start();
context.spooky.then(function (){ this.echo('Hello, this is SpookyJS'); });
context.spooky.open('http://www.google.com/');
context.spooky.then(function () { this.echo(this.getCurrentUrl()); });
function onConsole(line) { if (line === 'http://www.google.com/') { context.spooky.removeListener('console', onConsole); done(); return; } } context.spooky.on('console', onConsole);
context.spooky.run();
}); }); after(hooks.after(context));});
25
TEST/FRONTEND.JS
$ mocha test/frontend/example.js -‐r expect.js
26
$ make test
27
PULLING IT ALL TOGETHER
Make: Folder structure
28
testbackend: @mocha $$(find test/backend -‐name "*.js") -‐r expect.js -‐R spec
...など
Testing the backendMakefile
Run tests$ make testbackend
29
testbackend: @./node_modules/.bin/mocha $$(find test/backend -‐name "*.js") -‐r expect.js -‐R spec
Checking test coverageMakefile
Check test coverage$ make coverage
30
coverage: @echo 'Checking test code coverage...'
#Cleaning up @rm -‐rf _src-‐with-‐coverage/ && rm -‐rf _test-‐with-‐coverage/ #Instrumenting code @./node_modules/jscoverage/jscoverage src _src-‐with-‐coverage
#Creating tests for instrumented code @cp -‐r test _test-‐with-‐coverage @find _test-‐with-‐coverage -‐name '*.js' -‐exec sed -‐i '' 's/\/src\//\/_src-‐with-‐coverage\//g' "{}" \;
#Running tests... @./node_modules/.bin/mocha $$(find _test-‐with-‐coverage -‐name "*.js") -‐r expect.js -‐R html-‐cov > coverage.html
#Cleaning up @rm -‐rf _src-‐with-‐coverage/ && rm -‐rf _test-‐with-‐coverage/
@echo 'Done. Result written to coverage.html.'
31
32
Run tests$ make testfrontend
Makefile
Testing the frontend
test: @./node_modules/.bin/mocha $$(find test/frontend -‐name "*.js") -‐r expect.js -‐R spec
testbackend: @./node_modules/.bin/mocha $$(find test/backend -‐name "*.js") -‐r expect.js -‐R spec testfrontend: @./node_modules/.bin/mocha $$(find test/frontend -‐name "*.js") -‐r expect.js -‐R spec
coverage: @echo 'Checking test code coverage...' ... @echo 'Done. Result written to coverage.html.'
test: @./node_modules/.bin/mocha $$(find test/ -‐name "*.js") -‐r expect.js -‐R spec
all: test coverage @echo 'Tested frontend and backend. Coverage doc saved to coverage.html.'
Makefile
33
Run all tests$ make test
Run all tests + show test coverage$ make all
Summary
Node.js is Javascript on the server.
Unit testing with Mocha + Expect.js
Checking coverage with JSCoverage
User interface testing with SpookyJS
Running everything with Make
34
Thank youありがとうございました
35
Contact Jonathan
@jonwaller
facebook.com/jonwaller0
gplus.to/jonwallerwww.jonwaller.net/ja/
36
ReferencesNode.js
http://nodejs.org/
Unit testing (Mocha)
http://visionmedia.github.com/mocha/
Test coverage
http://tjholowaychuk.com/post/18175682663/mocha-test-coverage
User interface testing
http://casperjs.org/
https://github.com/WaterfallEngineering/SpookyJS
Other useful stuff
https://npmjs.org/ - Learn about commonly used node packages
http://jenkins-ci.org/ - Set up continuous integration (e.g. Automatically testing when you commit)
37