Testing most things
in JavaScript
Leeds JS
Colin Oakley
@htmlandbacon
HELLO
why do we test ?
reassurance
quality
automation
side effects
types of testing
unit
intergration
acceptance
unit testing in
javascript
testing frameworks
• mocha
• jasmine
• jest
• ava
• TAP
example code
module.exports = {
format(firstName = '', lastName = '') {
let string = '';
if(firstName != '') {
string = firstName;
}
if(lastName != '') {
if(string.length === 0) {
string = lastName;
} else {
string = string + ' ' + lastName;
}
}
return string.toUpperCase();
}
};
example unit tests
const mocha = require('mocha');
const assert = require('chai').assert;
const name = require('../examples/nameFormatter');
const firstName = 'Kevin';
const lastName = 'Flynn';
describe('calling name.format(firstName, lastName)', function() {
it('should return empty string when nothing is supplied', function() {
const returendName = name.format();
assert.equal(returendName, '');
});
it('should return uppercase first name when firstName is provided', function() {
const returendName = name.format(firstName);
assert.equal(returendName, 'KEVIN');
});
it('should return uppercase firstName and lastName when both are provided', function() {
const returendName = name.format(firstName, lastName);
assert.equal(returendName, 'KEVIN FLYNN');
});
});
example output
calling name.format(firstName, lastName)
✓ should return empty string when nothing is supplied
✓ should return uppercase first name when firstName is provided
✓ should return uppercase firstName and lastName when both are provided
hinting for failure
it('should return uppercase first name when firstName is provided', function() {
const returendName = name.format(firstName);
assert.equal(returendName, 'KEVIN', 'uppercase first name not found');
});
failed output
calling name.format(firstName, lastName)
✓ should return empty string when nothing is supplied
1) should return uppercase first name when firstName is provided
2) should return uppercase firstName and lastName when both are provided
1 passing (12ms)
2 failing
1) calling name.format(firstName, lastName) should return uppercase first name when firstName is provided:
AssertionError: uppercase first name not found: expected 'Kevin' to equal 'KEVIN'
at Context.<anonymous> (test/index.js:16:12)
2) calling name.format(firstName, lastName) should return uppercase firstName and lastName when both are provided:
AssertionError: uppercase whole name not found: expected 'Kevin Flynn' to equal 'KEVIN FLYNN'
at Context.<anonymous> (test/index.js:20:12)
mocha hooks
• before()
• after()
• beforeEach()
• afterEach()
assertions styles chai - BDD
• expect(foo).to.be.undefined;
• expect(everything).to.be.ok;
• expect(thing).to.include.members([3, 2]);
• expect(foo).to.be.above(5);
assertions styles chai - TDD
• assert.isUndefined(food)
• assert.isOkay(everything)
• assert.include(thing, [3, 2])
• assert.isAbove(5)
Jest
Jest - assertions
• expect(foo).toBeUndefined()
• expect(inputs).toBeTruthy()
• expect(foo).toBeDefined()
Jest - Snapshots
DOM
TDD
ci build
example ci build
1. code style check
2. run tests
3. coverage
4. deploy
5. start intergration tests
6. start accetance tests
code style
code style js
• Spacing (2 spaces, tabs, moons, whatever!)
• Single quotes for strings
• No unused variables
• No semicolons
• Space after keywords
• Space after function name
https://github.com/airbnb/javascript
code coverage
• istanbul
• sonar cube
• Blanket
• JScoverage
code coverage example
-------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-------------------|----------|----------|----------|----------|----------------|
examples/ | 88.89 | 83.33 | 100 | 88.89 | |
nameFormatter.js | 88.89 | 83.33 | 100 | 88.89 | 12 |
-------------------|----------|----------|----------|----------|----------------|
All files | 88.89 | 83.33 | 100 | 88.89 | |
-------------------|----------|----------|----------|----------|----------------|
=============================== Coverage summary ===============================
Statements : 88.89% ( 8/9 )
Branches : 83.33% ( 5/6 )
Functions : 100% ( 1/1 )
Lines : 88.89% ( 8/9 )
================================================================================
mocks
spies
stubs
and pain
intergration
example intergration tests
const validUserID = 'A0001';
const invalidUserID = 'B0001';
describe('/api/users', function() {
describe('/:id', function() {
it('returns user when valid user id is given', function(done) {
superagent
.get(`http://localhost:${port}/api/users/${validUserID}`)
.end(function(err, res) {
assert.equal(res.status, status.OK);
const result = JSON.parse(res.text);
assert.equal(result.id, validUserID);
done();
});
});
it('returns 404 error with message when user does not exist', function(done) {
superagent
.get(`http://localhost:${port}/api/users/${invalidUserID}`)
.end(function(err, res) {
assert.equal(res.status, status.NOT_FOUND);
const result = JSON.parse(res.text);
assert.equal(result.message, `User ${invalidUserID} was not found.`);
done();
});
});
});
});
acceptance
acceptance testing frameworks
• NightwatchJS
• Intern
• WebDriverIO
example NightwatchJS
const base = (process.env.SITE || 'http://localhost:3000');
describe('Demo', function () {
describe('with Nightwatch', function () {
after(function (client, done) {
client.end(function () {
done();
});
});
afterEach(function (client, done) {
if (client.options.screenshots) {
client.saveScreenshot(client.currentTest.name + '.png');
}
done();
});
it('should access a page and assert a valid title', function (client) {
client
.url(base + '/docs/examples/elements/forms')
.expect.element('body').to.be.present.before(100);
client.assert.containsText('h1', 'Your details');
});
});
});
example cuecumber
NightwatchJS
# features/checkpage.feature
Feature: Check page title
Scenario: Acccessing page
Given I open the form elements page
Then the title is "Your detail"
And I don't even know
MORE TESTING
a11y
automated tools
• aXe
• pa11y
• tenon
• HTML Codesniffer
pally example
pa11y words.htmlandbacon.com
Welcome to Pa11y
> PhantomJS browser created
> Testing the page "http://words.htmlandbacon.com"
Results for words.htmlandbacon.com:
• Notice: Check that the title element describes the document.
├── WCAG2AA.Principle2.Guideline2_4.2_4_2.H25.2
├── html > head > title
└── <title>html &amp; bacon - The rambling...</title>
0 Errors
0 Warnings
24 Notices
visual regression
getting started
1. npm install -g backstopjs
2. backstop genConfig
3. Tweak url / css selectors in json
4. backstop reference
5. backstop test
backstop.js - example
in summary
KTHXBYE

Testing most things in JavaScript - LeedsJS 31/05/2017

  • 1.
    Testing most things inJavaScript Leeds JS Colin Oakley @htmlandbacon
  • 2.
  • 3.
    why do wetest ?
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 11.
  • 12.
    testing frameworks • mocha •jasmine • jest • ava • TAP
  • 13.
    example code module.exports ={ format(firstName = '', lastName = '') { let string = ''; if(firstName != '') { string = firstName; } if(lastName != '') { if(string.length === 0) { string = lastName; } else { string = string + ' ' + lastName; } } return string.toUpperCase(); } };
  • 14.
    example unit tests constmocha = require('mocha'); const assert = require('chai').assert; const name = require('../examples/nameFormatter'); const firstName = 'Kevin'; const lastName = 'Flynn'; describe('calling name.format(firstName, lastName)', function() { it('should return empty string when nothing is supplied', function() { const returendName = name.format(); assert.equal(returendName, ''); }); it('should return uppercase first name when firstName is provided', function() { const returendName = name.format(firstName); assert.equal(returendName, 'KEVIN'); }); it('should return uppercase firstName and lastName when both are provided', function() { const returendName = name.format(firstName, lastName); assert.equal(returendName, 'KEVIN FLYNN'); }); });
  • 15.
    example output calling name.format(firstName,lastName) ✓ should return empty string when nothing is supplied ✓ should return uppercase first name when firstName is provided ✓ should return uppercase firstName and lastName when both are provided
  • 16.
    hinting for failure it('shouldreturn uppercase first name when firstName is provided', function() { const returendName = name.format(firstName); assert.equal(returendName, 'KEVIN', 'uppercase first name not found'); });
  • 17.
    failed output calling name.format(firstName,lastName) ✓ should return empty string when nothing is supplied 1) should return uppercase first name when firstName is provided 2) should return uppercase firstName and lastName when both are provided 1 passing (12ms) 2 failing 1) calling name.format(firstName, lastName) should return uppercase first name when firstName is provided: AssertionError: uppercase first name not found: expected 'Kevin' to equal 'KEVIN' at Context.<anonymous> (test/index.js:16:12) 2) calling name.format(firstName, lastName) should return uppercase firstName and lastName when both are provided: AssertionError: uppercase whole name not found: expected 'Kevin Flynn' to equal 'KEVIN FLYNN' at Context.<anonymous> (test/index.js:20:12)
  • 18.
    mocha hooks • before() •after() • beforeEach() • afterEach()
  • 19.
    assertions styles chai- BDD • expect(foo).to.be.undefined; • expect(everything).to.be.ok; • expect(thing).to.include.members([3, 2]); • expect(foo).to.be.above(5);
  • 20.
    assertions styles chai- TDD • assert.isUndefined(food) • assert.isOkay(everything) • assert.include(thing, [3, 2]) • assert.isAbove(5)
  • 21.
  • 22.
    Jest - assertions •expect(foo).toBeUndefined() • expect(inputs).toBeTruthy() • expect(foo).toBeDefined()
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
    example ci build 1.code style check 2. run tests 3. coverage 4. deploy 5. start intergration tests 6. start accetance tests
  • 28.
  • 29.
    code style js •Spacing (2 spaces, tabs, moons, whatever!) • Single quotes for strings • No unused variables • No semicolons • Space after keywords • Space after function name https://github.com/airbnb/javascript
  • 30.
    code coverage • istanbul •sonar cube • Blanket • JScoverage
  • 31.
    code coverage example -------------------|----------|----------|----------|----------|----------------| File| % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | -------------------|----------|----------|----------|----------|----------------| examples/ | 88.89 | 83.33 | 100 | 88.89 | | nameFormatter.js | 88.89 | 83.33 | 100 | 88.89 | 12 | -------------------|----------|----------|----------|----------|----------------| All files | 88.89 | 83.33 | 100 | 88.89 | | -------------------|----------|----------|----------|----------|----------------| =============================== Coverage summary =============================== Statements : 88.89% ( 8/9 ) Branches : 83.33% ( 5/6 ) Functions : 100% ( 1/1 ) Lines : 88.89% ( 8/9 ) ================================================================================
  • 32.
  • 33.
  • 34.
    example intergration tests constvalidUserID = 'A0001'; const invalidUserID = 'B0001'; describe('/api/users', function() { describe('/:id', function() { it('returns user when valid user id is given', function(done) { superagent .get(`http://localhost:${port}/api/users/${validUserID}`) .end(function(err, res) { assert.equal(res.status, status.OK); const result = JSON.parse(res.text); assert.equal(result.id, validUserID); done(); }); }); it('returns 404 error with message when user does not exist', function(done) { superagent .get(`http://localhost:${port}/api/users/${invalidUserID}`) .end(function(err, res) { assert.equal(res.status, status.NOT_FOUND); const result = JSON.parse(res.text); assert.equal(result.message, `User ${invalidUserID} was not found.`); done(); }); }); }); });
  • 35.
  • 36.
    acceptance testing frameworks •NightwatchJS • Intern • WebDriverIO
  • 37.
    example NightwatchJS const base= (process.env.SITE || 'http://localhost:3000'); describe('Demo', function () { describe('with Nightwatch', function () { after(function (client, done) { client.end(function () { done(); }); }); afterEach(function (client, done) { if (client.options.screenshots) { client.saveScreenshot(client.currentTest.name + '.png'); } done(); }); it('should access a page and assert a valid title', function (client) { client .url(base + '/docs/examples/elements/forms') .expect.element('body').to.be.present.before(100); client.assert.containsText('h1', 'Your details'); }); }); });
  • 38.
    example cuecumber NightwatchJS # features/checkpage.feature Feature:Check page title Scenario: Acccessing page Given I open the form elements page Then the title is "Your detail" And I don't even know
  • 39.
  • 40.
  • 41.
    automated tools • aXe •pa11y • tenon • HTML Codesniffer
  • 42.
    pally example pa11y words.htmlandbacon.com Welcometo Pa11y > PhantomJS browser created > Testing the page "http://words.htmlandbacon.com" Results for words.htmlandbacon.com: • Notice: Check that the title element describes the document. ├── WCAG2AA.Principle2.Guideline2_4.2_4_2.H25.2 ├── html > head > title └── <title>html &amp; bacon - The rambling...</title> 0 Errors 0 Warnings 24 Notices
  • 43.
  • 44.
    getting started 1. npminstall -g backstopjs 2. backstop genConfig 3. Tweak url / css selectors in json 4. backstop reference 5. backstop test
  • 45.
  • 46.
  • 47.