Slide 1

Slide 1 text

T E S T D R I V E N

Slide 2

Slide 2 text

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. “ ” What is node.js?

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

basicFundamentals

Slide 5

Slide 5 text

packageManagement

Slide 6

Slide 6 text

is not an acronym

Slide 7

Slide 7 text

is an acronym National Association of Pastoral Musicians

Slide 8

Slide 8 text

github: install: isaacs/npm comes with node

Slide 9

Slide 9 text

npm install localInstallation

Slide 10

Slide 10 text

npm install --global globalInstallation -g or --global

Slide 11

Slide 11 text

npm install --link dualInstallation

Slide 12

Slide 12 text

npm install --save[-dev|-optional] dependencyReferences --save or --save-dev or --save-optional

Slide 13

Slide 13 text

npm install updateDependencies

Slide 14

Slide 14 text

testingNode.js

Slide 15

Slide 15 text

TDD is to coding style as yoga is to posture. Even when you're not actively practicing, having done so colors your whole life healthier.” j. kerr “

Slide 16

Slide 16 text

assertingCorrectness

Slide 17

Slide 17 text

var  assert  =  require('assert');

Slide 18

Slide 18 text

assert(value)            .ok(value)            .equal(actual,  expected)            .notEqual(actual,  expected)            .deepEqual(actual,  expected)            .notDeepEqual(actual,  expected)            .strictEqual(actual,  expected)            .notStrictEqual(actual,  expected)            .throws(block,  [error])            .doesNotThrow(block,  [error])            .ifError(value)

Slide 19

Slide 19 text

var assert = require('assert'); // Will pass assert.ok(true); // Will throw an exception assert.ok(false); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 20

Slide 20 text

var assert = require('assert'); // Will throw 'false == true' error assert.ok(typeof 'hello' === 'number'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 21

Slide 21 text

$  node  test.js assert.js:104    throw  new  assert.AssertionError({                ^ AssertionError:  false  ==  true        at  Object.  (my-­‐test.js:7:8)        at  Module._compile  (module.js:449:26)        at  Object.Module._extensions..js  (module.js:467:10)        at  Module.load  (module.js:356:32)        at  Function.Module._load  (module.js:312:12)        at  Module.runMain  (module.js:487:10)        at  process.startup.processNextTick.process._tick... $  _

Slide 22

Slide 22 text

Chai Assertion Library

Slide 23

Slide 23 text

github: install: chaijs/chai npm install chai

Slide 24

Slide 24 text

isTrue,                      isFalse, isNull,                      isNotNull, isUndefined,            isDefined, isFunction,              isNotFunction, isArray,                    isNotArray, isBoolean,                isNotBoolean, isNumber,                  isNotNumber, isString,                  isNotString,                                    include,                                    lengthOf,                                    operator,                                    closeTo   isObject,                  isNotObject, typeOf,                      notTypeOf, instanceOf,              notInstanceOf, match,                        notMatch, property,                  notProperty, deepProperty,          notDeepProperty, propertyVal,            propertyNotVal, deepPropertyVal,    deepPropertyNotVal, additional assertions

Slide 25

Slide 25 text

var  assert  =  require('chai').assert;

Slide 26

Slide 26 text

var assert = require('chai').assert; // Will throw 'expected 'hello' to be a number' assert.isNumber('hello'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 27

Slide 27 text

$  node  test.js expected  'hello'  to  be  a  number $  _

Slide 28

Slide 28 text

var chai = require('chai') , assert = chai.assert; chai.Assertion.includeStack = true; // Will throw and display stack assert.isNumber('hello'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 29

Slide 29 text

testDriven

Slide 30

Slide 30 text

Exceptions alone are insufficient

Slide 31

Slide 31 text

$  node  my-­‐test.js assert.js:104    throw  new  assert.AssertionError({                ^ AssertionError:  false  ==  true        at  Object.  (my-­‐test.js:7:8)        at  Module._compile  (module.js:449:26)        at  Object.Module._extensions..js  (module.js:467:10)        at  Module.load  (module.js:356:32)        at  Function.Module._load  (module.js:312:12)        at  Module.runMain  (module.js:487:10)        at  process.startup.processNextTick.process._tick... $  _

Slide 32

Slide 32 text

We need a testing framework!

Slide 33

Slide 33 text

mocha simple, flexible, fun

Slide 34

Slide 34 text

mocha github: install: visionmedia/mocha npm install -g mocha

Slide 35

Slide 35 text

$  npm  install  -­‐g  mocha $  mkdir  test $  mocha        ✔  0  tests  complete  (1ms) $  

Slide 36

Slide 36 text

var  mocha  =  require('mocha');

Slide 37

Slide 37 text

suite('Testing  out  this  thing',  function()  {        test('should  do  stuff',  function()  {                //  Assertion  tests        }); }); tddSyntax

Slide 38

Slide 38 text

./test/my-test.js var assert = require('chai').assert; suite('Assertions', function() { test('should pass on truthiness', function() { assert.ok(true); }); test('should fail on falsiness', function() { assert.ok(false); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 39

Slide 39 text

$  mocha  -­‐-­‐ui  tdd  -­‐-­‐reporter  spec    Assertions        ✓  should  pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _

Slide 40

Slide 40 text

groupedTests

Slide 41

Slide 41 text

suite('Testing  out  this  thing',  function()  {        suite('with  a  subset  of  this  other  thing',  function()  {                test('should  do  stuff',  function()  {                        //  Assertion  tests                });        }); }); groupSyntax

Slide 42

Slide 42 text

var assert = require('chai').assert; suite('Assertions', function() { suite('of truthiness', function() { test('should pass on true', function() { assert.isTrue(true); }); test('should pass on false', function() { assert.isFalse(false); }); }); suite('of type', function() { test('should pass on number', function() { assert.isNumber(5); }); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 43

Slide 43 text

$  mocha  -­‐-­‐ui  tdd  -­‐-­‐reporter  spec    Assertions        of  truthiness            ✓  should  pass  on  true              ✓  should  pass  on  false          of  type            ✓  should  pass  on  number      ✔  3  tests  complete  (6ms) $  _

Slide 44

Slide 44 text

pendingTests

Slide 45

Slide 45 text

suite('Testing  out  this  thing',  function()  {        suite('with  a  subset  of  this  other  thing',  function()  {                test('should  do  stuff  someday');        }); }); pendingSyntax

Slide 46

Slide 46 text

var assert = require('chai').assert; suite('Assertions', function() { suite('of truthiness', function() { test('should pass on true', function() { assert.isTrue(true); }); test('should pass on false', function() { assert.isFalse(false); }); }); suite('of type', function() { test('should pass on number', function() { assert.isNumber(5); }); test('should pass on object'); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 47

Slide 47 text

$  mocha  -­‐-­‐ui  tdd  -­‐-­‐reporter  spec    Assertions        of  truthiness            ✓  should  pass  on  true              ✓  should  pass  on  false          of  type            ✓  should  pass  on  number              -­‐  should  pass  on  object    ✔  4  tests  complete  (6ms)    •  1  test  pending $  _

Slide 48

Slide 48 text

setupTeardown

Slide 49

Slide 49 text

setup(); teardown();

Slide 50

Slide 50 text

suite('Testing  out  this  thing',  function()  {        setup(function(){                //  ...        };        suite('with  a  subset  of  this  other  thing',  function()  {                test('should  do  stuff',  function()  {                        //  Assertion  tests                });                teardown(function(){                        //  ...                };        }); }); setupTeardown

Slide 51

Slide 51 text

asynchronousTests

Slide 52

Slide 52 text

test('it  should  not  error',  function(done)  {        search.find("Apples",  done); }); asynchronousSyntax

Slide 53

Slide 53 text

test('it  should  not  error',  function(done)  {        search.find("Apples",  done); }); test('it  should  return  2  items',  function(done)  {        search.find("Apples",  function(err,  res)  {                if  (err)  return  done(err);                res.should.have.length(2);                done();        }); }); asynchronousSyntax

Slide 54

Slide 54 text

All Mocha functions accept this callback

Slide 55

Slide 55 text

suite('When searching for Apples', function(done) { setup(function(done){ items.save(['fiji apples', 'empire apples'], done); }); test('it should not error', function(done) { search.find("Apples", done); }); test('it should return 2 items', function(done) { search.find("Apples", function(err, res) { if (err) return done(err); res.should.have.length(2); done(); }); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 56

Slide 56 text

simplifyExecution

Slide 57

Slide 57 text

be nice to yourself: $  mocha  -­‐-­‐ui  tdd  -­‐-­‐reporter  spec should be simplified to $  make  test and $  npm  test

Slide 58

Slide 58 text

./makefile # Makefile for sample module test: mocha --reporter spec --ui tdd .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 59

Slide 59 text

./makefile # Makefile for sample module test: mocha \ --reporter spec \ --ui tdd .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 60

Slide 60 text

./package.json { "name": "sample", "version": "0.1.0", "devDependencies": { "chai": "~1.2.0" }, "scripts": { "test": "make test" } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 61

Slide 61 text

$  make  test    Assertions        ✓  should  pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _

Slide 62

Slide 62 text

$  npm  test    Assertions        ✓  should  pass  on  truthiness          1)  should  fail  on  falsiness    ✖  1  of  2  tests  failed:    1)  Assertions  should  fail  on  falsiness:              AssertionError:  false  ==  true            at  (stack  trace  omitted  for  brevity) $  _

Slide 63

Slide 63 text

eliminate global dependency $  npm  install  mocha  -­‐-­‐link

Slide 64

Slide 64 text

test: @./node_modules/.bin/mocha \ --reporter spec \ --ui tdd .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 65

Slide 65 text

update dev dependencies $  npm  install  mocha  -­‐-­‐save-­‐dev $  npm  install  chai    -­‐-­‐save-­‐dev

Slide 66

Slide 66 text

$  npm  install  mocha  -­‐-­‐save-­‐dev $  npm  install  chai    -­‐-­‐save-­‐dev $  git  diff  package.json   diff  -­‐-­‐git  a/package.json  b/package.json index  439cf44..3609bb9  100644 -­‐-­‐-­‐  a/package.json +++  b/package.json @@  -­‐1,5  +1,7  @@  {      "name":  "sample", -­‐    "version":  "0.1.0" +    "version":  "0.1.0", +    "devDependencies":  { +        "mocha":  "~1.4.0", +        "chai":  "~1.2.0" +    }  }

Slide 67

Slide 67 text

notificationSystems

Slide 68

Slide 68 text

mocha --slow slowThreshold -s or --slow

Slide 69

Slide 69 text

$  mocha  -­‐-­‐reporter  spec  -­‐-­‐slow  5    When  accessing  the  Ticket  API        with  valid  Morale  credentials,            and  with  a  project  that  exists,                getting  a  list  of  tickets                    ✓  should  return  without  an  error                      ✓  should  return  a  populated  array  (5ms)                    ✓  should  contain  a  task  or  bug  ticket                  adding  a  new  ticket                    ✓  should  return  without  an  error                      ✓  should  return  the  new  ticket  (11ms)  ✔  5  tests  complete  (22ms) $  _

Slide 70

Slide 70 text

mocha --watch continuousTesting -w or --watch

Slide 71

Slide 71 text

$  mocha  -­‐-­‐watch  ✔  5  tests  complete  (22ms)  ^  watching

Slide 72

Slide 72 text

Growl

Slide 73

Slide 73 text

mac: win: also: apple app store growlForWindows.com growlNotify

Slide 74

Slide 74 text

mocha --growl growlNotifications -G or --growl

Slide 75

Slide 75 text

⌘S

Slide 76

Slide 76 text

test: @./node_modules/.bin/mocha \ --reporter spec \ --ui tdd watch: @./node_modules/.bin/mocha \ --reporter min \ --ui tdd \ --growl \ --watch .PHONY: test watch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 77

Slide 77 text

behaviorDriven

Slide 78

Slide 78 text

suite('Test  this  thing',  function()  {        test('Do  stuff',  function()  {                //  Assertion  tests        }); }); describe('Testing  out  this  thing',  function()  {        it('should  do  stuff',  function()  {                //  Assertion  tests        }); }); bddSyntax tddSyntax

Slide 79

Slide 79 text

test: @./node_modules/.bin/mocha \ --reporter spec \ --ui bdd watch: @./node_modules/.bin/mocha \ --reporter min \ --ui bdd \ --growl \ --watch .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 80

Slide 80 text

test: @./node_modules/.bin/mocha \ --reporter spec watch: @./node_modules/.bin/mocha \ --reporter min \ --growl \ --watch .PHONY: test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 81

Slide 81 text

setupTeardown

Slide 82

Slide 82 text

before(); beforeEach(); after(); afterEach();

Slide 83

Slide 83 text

describe('Testing  out  this  thing',  function()  {        before(function(){                //  ...        };        describe('with  a  subset  of  that  thing',  function()  {                it('should  do  stuff',  function()  {                        //  Assertion  tests                });                afterEach(function(){                        //  ...                };        }); }); setupTeardown

Slide 84

Slide 84 text

There is no tdd equivalent to ‘each’

Slide 85

Slide 85 text

suite(); test(); setup(); teardown(); describe(); it(); before(); after(); beforeEach(); afterEach(); bddMethods tddMethods

Slide 86

Slide 86 text

expectAssertions

Slide 87

Slide 87 text

expectSyntax expect(person).to.be.an('object');        .with.property('age')        .that.is.a('number')        .that.equals(34); assert.isObject(person); assert.property(person,  "age"); assert.isNumber(person.age); assert.equals(person.age,  34); assertSyntax

Slide 88

Slide 88 text

var  expect  =  require('chai').expect;

Slide 89

Slide 89 text

assertionChains for readability

Slide 90

Slide 90 text

expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);

Slide 91

Slide 91 text

.to .be .been .is .that .and .have .with syntaxSugar for readability

Slide 92

Slide 92 text

expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);

Slide 93

Slide 93 text

expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);

Slide 94

Slide 94 text

.property subjectChange from original object

Slide 95

Slide 95 text

expect(person)    .that.is.an('object')    .with.property('address')        .that.is.an('object')        .with.property('city')            .that.is.a('string')            .and.equals('Detroit')

Slide 96

Slide 96 text

shouldAssertions

Slide 97

Slide 97 text

person.should.be.an('object')        .with.property('age')        .that.is.a('number')        .that.equals(34); shouldSyntax assert.isObject(person); assert.property(person,  "age"); assert.isNumber(person.age); assert.equals(person.age,  34); assertSyntax

Slide 98

Slide 98 text

var  should  =  require('chai').should();

Slide 99

Slide 99 text

var  chai      =  require('chai')    ,  expect  =  chai.expect    ,  should  =  chai.should();

Slide 100

Slide 100 text

assertionChains same as expect style* except for existence

Slide 101

Slide 101 text

expect(foo).to.not.exist; expect(bar).to.exist; should.not.exist(foo); should.exist(foo);

Slide 102

Slide 102 text

skippedTests

Slide 103

Slide 103 text

describe('Testing  out  this  thing',  function()  {        it.skip('should  be  skipped',  function()  {                //  Assertion  tests        }); }); describe.skip('This  entire  suite  will  be  skipped',  function()  {        it('should  do  stuff',  function()  {                //  Assertion  tests        }); }); skipSyntax

Slide 104

Slide 104 text

describe('Assertions', function() { describe('of truthiness', function() { it('should pass on truthiness', function() { assert.isTrue(true); }); it('should pass on falsiness', function() { assert.isFalse(false); }); }); describe('of type', function() { it.skip('should pass on number', function() { assert.isNumber(5); }); it('should pass on object'); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 105

Slide 105 text

$  make  test    Assertions        of  truthiness            ✓  should  pass  on  truthiness              ✓  should  pass  on  falsiness          of  type            -­‐  should  pass  on  number              -­‐  should  pass  on  object    ✔  4  tests  complete  (6ms)    •  2  test  pending $  _

Slide 106

Slide 106 text

Skip is available in bdd only

Slide 107

Slide 107 text

mockingObjects

Slide 108

Slide 108 text

JavaScript ships with a mocking framework ...it’s called JavaScript

Slide 109

Slide 109 text

var me = {firstName: 'Jay' , lastName: 'Harris' , getFullName: function() { return this.firstName + ' ' + this.lastName; }}; // Returns 'Jay Harris' me.getFullName(); me.getFullName = function() { return 'John Doe'; }; // Returns 'John Doe' me.getFullName(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 110

Slide 110 text

nock HTTP Mocking Library

Slide 111

Slide 111 text

nock github: install: flatiron/nock npm install nock

Slide 112

Slide 112 text

var http = require('http'); var reqOptions = { host: 'api.twitter.com', path: '/1/statuses/user_timeline.json?' + 'screen_name=jayharris' }; var resCallback = function(res) { var responseData = ''; res.on('data', function(chunk) { responseData += chunk; }); res.on('end', function() { console.log(responseData); }); }; http.request(reqOptions, resCallback).end(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 113

Slide 113 text

var nock = require('nock'); var twitter = nock('http://api.twitter.com') .get('/1/statuses/user_timeline.json?'+ 'screen_name=jayharris') .reply(200, "This worked"); // Returns "This worked" http.request(reqOptions, resCallback).end(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 114

Slide 114 text

// Returns live Twitter data http.request(reqOptions, resCallback).end(); var nock = require('nock'); var twitter = nock('http://api.twitter.com') .get('/1/statuses/user_timeline.json?'+ 'screen_name=jayharris') .reply(200, "This worked"); // Returns "This worked" http.request(reqOptions, resCallback).end(); // Returns live Twitter data http.request(reqOptions, resCallback).end(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 115

Slide 115 text

nock nock.recorder.rec(  ); nock.recorder.play(  );

Slide 116

Slide 116 text

Spies, Stubs, & Mocks Sinon.js

Slide 117

Slide 117 text

github: install: cjohansen/Sinon.JS npm install sinon Sinon.js

Slide 118

Slide 118 text

browserTesting

Slide 119

Slide 119 text

Fast, headless browser Zombie.js

Slide 120

Slide 120 text

github: install: assaf/zombie npm install zombie Zombie.js

Slide 121

Slide 121 text

loadTesting

Slide 122

Slide 122 text

Performance Suite nodeload

Slide 123

Slide 123 text

github: install: benschmaus/nodeload npm install nodeload nodeload

Slide 124

Slide 124 text

activelyPractice

Slide 125

Slide 125 text

Color your whole life healthier

Slide 126

Slide 126 text

jay harris [email protected] #testdrivennode @jayharris P R E S I D E N T