full stack unit testing
TRANSCRIPT
Full-stack unit-
testingTech talk @ Pause
Plan1. Overview of frameworks and libraries for testing
2. Testing of client side web application
a. Test runner
b. Unit testing stack
c. End-2-end testing
d. Reporting
e. Sample: angular application testing
3. Testing of server side
a. Mocking of data providers
b. Fixtures
c. Essentials of tests
4. Pitfalls of unit testing
Overview of frameworks
Testing of client-side
1. Test runner - runs unit tests suite in various
browsers and write reports using different
formats
2. Frameworks - skeleton for unit tests suites
3. Utils libraries - allow to write tests in more
expressive
Test runner
1. Dynamically generate index file using
required libs, sources and tests
2. Execute web server on background
3. Run tests in different browsers
4. Collect test results from expectations and
asserts
5. Format them into report
Test runner
Test’em - simple test
runner1. Framework config:
Mocha, Jasmine
2. Test files
3. Serve files
4. Browser stack config
Test runner
Karma - advanced test
runner1. Plugins
2. Multiple report formats
supported
3. Various browsers
4. Dynamic index.html
generation
Test runner
Essentials
1. Synchronization between source files
2. Integration of frameworks
3. Debugging
Unit testing stack
Testing framework
API● TDD
● BDD
Main frameworks● Mocha
● Jasmine
● describeo skip
o only
● it
● before(done)
● after(done)
● beforeEach
● afterEach
Unit testing stack
Assertion libraries
Chai● Plugins
● Supports should and
expect style
Should
● expect(foo).to.deep.
equal(someNested
Array)
● should.exist(foo)
● foo.bar.should.have
.property(‘bar’)
Unit testing stack
Plugins for chai
● Allows you to write
tests in terms of
libraries you are
using
● Syntactic sugar
● Sinon.js
● jQuery
● Backbone
● Q
Unit testing stack
End-2-end testing
Protractor (previously
angular-scenario):
● Access to angular
scope from view
● Selectors for
directives
● Uses selenium
● by.o id
o css
o model
o binding
● elemento sendKeys
o setPosition
o setSize
End-2-end testing
Reporting
● jUnit XML - Jenkins
love it
● Progress, Dots - for
console lovers
● Coverage -
objective metrics
● jUnit is has better
support of Jasmine
● Coverage settings -
include all sources
not tests
Reporting
Sample angular app testingbeforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
GamesCtrl = $controller('HotelsCtrl', {
$scope: scope, Hotels: factory
});
}));
it('should set default value for orderProp', function () {
expect(scope.orderProp).toBe('title');
});
it('should have a List of Hotels', function () {
expect(scope.games.length).toBe(2);
expect(scope.games[0].title).toBe('Hilton');
expect(scope.games[1].freeRooms).toBe(10);
});
Testing of server side
Mocking of data providers
Faker.js
● id’s
● names, emails
● cities, locations
● messages, sentences, paragraphs
Sinon.js
● Mocking API using sinon.mock(API)
Sample code//Hotels data
'use strict';
var Faker = require('Faker');
function generateOne() {
return {
name: Faker.random.bk_noun(),
address: Faker.Addresses.streetAddress(),
capacity: {
standard: Faker.helpers.randomNumber(100),
economy: Faker.helpers.randomNumber(100),
luxury: Faker.helpers.randomNumber(100)
}
};
}
module.exports.generateOne = generateOne;
module.exports.generateMany = function (count) {
var result = [];
for (var i = 0; i < count; i++) {
result.push(generateOne())
}
return result;
}
Database mapping testing
1. No need to test mongoose API
2. Create stub data using Faker API and
predefined JSON
3. Insert it into DB inside before callback
4. Run unit test suites on test data
5. Remove data from DB inside after callback
Sample codevar fixtures = require('fixtures.js');
var api = require('api/');
var expect = require('chai').expect;
var COUNT = 100;
describe('Booking API', function () {
describe('#search', function () {
before(function (done) {
fixtures.prepareData('hotels',
COUNT, done);
});
it('should return all hotes if no query
params provided', function (done)
{api.search('hotels', function (err,
data) {
expect(err).to.be.null;
expect(data).to.be.an('object');
expect(data.length).to.be.eql(COUNT);
done();
….
after(function (done) {
fixtures.destroyData('hotels');
Alternatives
● https://github.com/petejkim/factory-lady
● http://chaijs.com/plugins/chai-factories
● https://www.npmjs.com/package/rosie
Fixtures
Rules
1. Do not hardcode the id’s and data that is
generated by database
2. Put all domain specific data into fixtures,
group them by collections
3. Do not put null to client API object. Use
dummy object instead.
Sample code// Fixtures
var async = require('async');
var hotelsData = require('./hotelsData');
var Hotel = require('./hotelModel');
module.exports.prepareData = function (name, count, cb) {
if (name !== 'hotels') { cb(new Error('Invalid data type')); return; }
async.forEach(hotelsData.generateMany(count), function (item, callback) {
var hotel = Hotel.createNew(item);
hotel.save(callback);
}, cb)
}
Sample codevar client = null;
function notInitializedThrow () {throw new Error
('Client not initialized')};
module.exports.init = function (config, cb) {
if (client === null) {
client = API.createClient(config);
// doing some initializations tuff
client.on('error', cb);
client.on('connected', cb.bind(this, null));
}
}
module.exports.getClient = function () {
if (client === null) {
return {
method1: notInitializedThrow,
method2: notInitializedThrow
}
} else {
return client;
}
}
Essentials
1. Test should not depend on each others
results
2. They should not use shared data (only in
read only mode)
3. Do not allow skipped tests in releases
4. Tests are not just for make it green
Integration tests
1. They are checking of how modules are
working together
2. Ideal for checking of entity lifecycle -
creation, linking to others, api calls,
querying, destroying
3. Execution environment should be
configurable
References
Tools:
● WS:o WebSocket API client
● HTTP:o Nock
Article:● https://davidbeath.com/posts/testing-http-responses-in-
nodejs.html
Stack
1. Mocha as core framework
2. Chai / Expect as assertions
3. Request.js for HTTP requests
4. node-websocket as ws client
5. Faker or factory-lady for generation of test
data
Best
1. To put grunt test or test command as git
merge to master hook
2. To put shortcut for testing into npm test
assuming that grunt is not globally installed
3. JSHint/JSLint as pre commit hook
Worst
1. To mix TDD and BDDa. TDD: Suite, test, step, setup, done
b. BDD: Expectations, asserts
2. To spaghetti the dependencies inside test
code
Sample codedescribe('API method', function () {
var temporaryData = null;
it('should do one thing', function (done) {
var result = API.call('somemethod');
temporaryData = API.call('anotherMethod');
expect(result).to.have.keys(['code', 'details', 'message']);
});
it('should do another thing', function (done) {
API.call('setup', temporaryData);
var result = API.call('anotherMethod');
expect(result).to.be.null;
Pitfalls
1. Separated environment for integration testing, unit
testing development and production
2. Do not rely on synchronous operations especially when
they are not (callbacks, initialization process)
3. Extra console.log’s are breaking unit test reportings
Sample codedescribe('Service API', function () {
before(function (done) {
thirdparty1.init(/*callback?*/);
thirdparty2.init(/*callback?*/);
thirdparty3.init(/*callback?*/);
done();
});
before(function (done) {
thirdparty1.done(/*callback?*/);
thirdparty2.done(/*callback?*/);
thirdparty3.done(/*callback?*/);
done();
});
});
describe('Service API', function () {
before(function (done) {
async.waterfall([
thirdparty1.init,
thirdparty2.init,
thirdparty3.init
], done);
});
before(function (done) {
async.waterfall([
thirdparty1.done,
thirdparty2.done,
thirdparty3.done
], done);
});
});
References
Books1. Testable Javascript book
2. JavaScript Allongé
3. Testing with CoffeeScript
Articles/Blogs:1. Full spectrum testing with angular.js and Karma
2. Cross-browser testing on multiple devices
3. Mocha + CoffeeScript tutorial
4. http://code.tutsplus.com/tutorials/testing-in-nodejs--net-35018
Tools
1. Node version manager
2. Mocha
3. Jasmine
4. Karma
5. Istanbul
6. Protractor
7. Selenium
QA