Test driven node.js

4,645 views

Published on

If you don’t test it, how do you know it works? Over the past few years, we have been compelled to write unit and integration tests for our applications--code that validates code--and it is these tests that change a one-off tool into a well-architected, robust, business-ready application. Yet, every new framework requires a new testing framework, so in this session, we will discuss testing frameworks for node.js. You will walk away with a solid understanding of how to write tests against your node.js applications and modules, leading to confidence that your work is business-ready.

Published in: Technology, Business
0 Comments
29 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,645
On SlideShare
0
From Embeds
0
Number of Embeds
57
Actions
Shares
0
Downloads
0
Comments
0
Likes
29
Embeds 0
No embeds

No notes for slide

Test driven node.js

  1. 1. TEST DRIVEN
  2. 2. What is node.js? “ Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. ”
  3. 3. basicFundamentals
  4. 4. packageManagement
  5. 5. is not an acronym
  6. 6. is an acronym National Association of Pastoral Musicians
  7. 7. github: isaacs/npm install: comes with node
  8. 8. localInstallation npm install <package>
  9. 9. globalInstallation npm install <package> --global -g or --global
  10. 10. dualInstallation npm install <package> --link
  11. 11. dependencyReferences npm install <package> --save[-dev|-optional] --save or --save-dev or --save-optional
  12. 12. updateDependencies npm install
  13. 13. testingNode.js
  14. 14. “ 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
  15. 15. assertingCorrectness
  16. 16. var  assert  =  require('assert');
  17. 17. 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)
  18. 18. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var assert = require('assert'); // Will pass assert.ok(true); // Will throw an exception assert.ok(false);
  19. 19. 1 var assert = require('assert'); 2 3 // Will throw 'false == true' error 4 assert.ok(typeof 'hello' === 'number'); 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  20. 20. $  node  test.js assert.js:104    throw  new  assert.AssertionError({                ^ AssertionError:  false  ==  true        at  Object.<anonymous>  (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... $  _
  21. 21. Chai Assertion Library
  22. 22. github: chaijs/chai install: npm install chai
  23. 23. 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
  24. 24. var  assert  =  require('chai').assert;
  25. 25. 1 var assert = require('chai').assert; 2 3 // Will throw 'expected 'hello' to be a number' 4 assert.isNumber('hello'); 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  26. 26. $  node  test.js expected  'hello'  to  be  a  number $  _
  27. 27. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var chai = require('chai') , assert = chai.assert; chai.Assertion.includeStack = true; // Will throw and display stack assert.isNumber('hello');
  28. 28. testDriven
  29. 29. Exceptions alone are insufficient
  30. 30. $  node  my-­‐test.js assert.js:104    throw  new  assert.AssertionError({                ^ AssertionError:  false  ==  true        at  Object.<anonymous>  (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... $  _
  31. 31. We need a testing framework!
  32. 32. mocha simple, flexible, fun
  33. 33. mocha github: visionmedia/mocha install: npm install -g mocha
  34. 34. $  npm  install  -­‐g  mocha $  mkdir  test $  mocha        ✔  0  tests  complete  (1ms) $  
  35. 35. var  mocha  =  require('mocha');
  36. 36. tddSyntax suite('Testing  out  this  thing',  function()  {        test('should  do  stuff',  function()  {                //  Assertion  tests        }); });
  37. 37. ./test/my-test.js 1 var assert = require('chai').assert; 2 3 suite('Assertions', function() { test('should pass on truthiness', function() { 4 assert.ok(true); 5 }); 6 test('should fail on falsiness', function() { 7 assert.ok(false); 8 }); 9 10 }); 11 12 13 14 15 16
  38. 38. $  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) $  _
  39. 39. groupedTests
  40. 40. groupSyntax suite('Testing  out  this  thing',  function()  {        suite('with  a  subset  of  this  other  thing',  function()  {                test('should  do  stuff',  function()  {                        //  Assertion  tests                });        }); });
  41. 41. 1 var assert = require('chai').assert; 2 3 suite('Assertions', function() { suite('of truthiness', function() { 4 test('should pass on true', function() { 5 assert.isTrue(true); 6 }); 7 test('should pass on false', function() { 8 assert.isFalse(false); 9 }); 10 }); 11 suite('of type', function() { 12 test('should pass on number', function() { 13 assert.isNumber(5); 14 }); 15 }); 16 17 }); 18
  42. 42. $  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) $  _
  43. 43. pendingTests
  44. 44. pendingSyntax suite('Testing  out  this  thing',  function()  {        suite('with  a  subset  of  this  other  thing',  function()  {                test('should  do  stuff  someday');        }); });
  45. 45. 1 var assert = require('chai').assert; 2 3 suite('Assertions', function() { suite('of truthiness', function() { 4 test('should pass on true', function() { 5 assert.isTrue(true); 6 }); 7 test('should pass on false', function() { 8 assert.isFalse(false); 9 }); 10 }); 11 suite('of type', function() { 12 test('should pass on number', function() { 13 assert.isNumber(5); 14 }); 15 test('should pass on object'); 16 }); 17 18 });
  46. 46. $  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 $  _
  47. 47. setupTeardown
  48. 48. setup(); teardown();
  49. 49. setupTeardown 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(){                        //  ...                };        }); });
  50. 50. asynchronousTests
  51. 51. asynchronousSyntax test('it  should  not  error',  function(done)  {        search.find("Apples",  done); });
  52. 52. asynchronousSyntax 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();        }); });
  53. 53. All Mocha functions accept this callback
  54. 54. 1 suite('When searching for Apples', function(done) { setup(function(done){ 2 items.save(['fiji apples', 3 'empire apples'], done); 4 }); 5 test('it should not error', function(done) { 6 search.find("Apples", done); 7 }); 8 test('it should return 2 items', function(done) { 9 search.find("Apples", function(err, res) { 10 if (err) return done(err); 11 res.should.have.length(2); 12 done(); 13 }); 14 }); 15 16 }); 17 18
  55. 55. simplifyExecution
  56. 56. be nice to yourself: $  mocha  -­‐-­‐ui  tdd  -­‐-­‐reporter  spec should be simplified to $  make  test and $  npm  test
  57. 57. ./makefile 1 # Makefile for sample module 2 3 test: mocha --reporter spec --ui tdd 4 5 6 7 8 .PHONY: test 9 10 11 12 13 14 15 16
  58. 58. ./makefile 1 # Makefile for sample module 2 3 test: mocha 4 --reporter spec 5 --ui tdd 6 7 8 .PHONY: test 9 10 11 12 13 14 15 16
  59. 59. ./package.json 1 { "name": "sample", 2 "version": "0.1.0", 3 "devDependencies": { 4 "chai": "~1.2.0" 5 }, 6 "scripts": { 7 "test": "make test" 8 } 9 10 } 11 12 13 14 15 16
  60. 60. $  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) $  _
  61. 61. $  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) $  _
  62. 62. eliminate global dependency $  npm  install  mocha  -­‐-­‐link
  63. 63. 1 test: @./node_modules/.bin/mocha 2 --reporter spec 3 --ui tdd 4 5 6 .PHONY: test 7 8 9 10 11 12 13 14 15 16 17 18
  64. 64. update dev dependencies $  npm  install  mocha  -­‐-­‐save-­‐dev $  npm  install  chai    -­‐-­‐save-­‐dev
  65. 65. $  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" +    }  }
  66. 66. notificationSystems
  67. 67. slowThreshold mocha --slow <ms> -s <ms> or --slow <ms>
  68. 68. $  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) $  _
  69. 69. continuousTesting mocha --watch -w or --watch
  70. 70. $  mocha  -­‐-­‐watch  ✔  5  tests  complete  (22ms)  ^  watching
  71. 71. Growl
  72. 72. mac: apple app store win: growlForWindows.com also: growlNotify
  73. 73. growlNotifications mocha --growl -G or --growl
  74. 74. ⌘S
  75. 75. 1 test: @./node_modules/.bin/mocha 2 --reporter spec 3 --ui tdd 4 5 6 watch: @./node_modules/.bin/mocha 7 --reporter min 8 --ui tdd 9 --growl 10 --watch 11 12 13 .PHONY: test watch 14 15 16 17 18
  76. 76. behaviorDriven
  77. 77. tddSyntax suite('Test  this  thing',  function()  {        test('Do  stuff',  function()  {                //  Assertion  tests        }); }); bddSyntax describe('Testing  out  this  thing',  function()  {        it('should  do  stuff',  function()  {                //  Assertion  tests        }); });
  78. 78. 1 test: @./node_modules/.bin/mocha 2 --reporter spec 3 --ui bdd 4 5 6 watch: @./node_modules/.bin/mocha 7 --reporter min 8 --ui bdd 9 --growl 10 --watch 11 12 13 .PHONY: test 14 15 16 17 18
  79. 79. 1 test: @./node_modules/.bin/mocha 2 --reporter spec 3 4 5 6 watch: @./node_modules/.bin/mocha 7 --reporter min 8 --growl 9 --watch 10 11 12 13 .PHONY: test 14 15 16 17 18
  80. 80. setupTeardown
  81. 81. before(); beforeEach(); after(); afterEach();
  82. 82. setupTeardown 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(){                        //  ...                };        }); });
  83. 83. There is no tdd equivalent to ‘each’
  84. 84. tddMethods suite(); test(); setup(); teardown(); bddMethods describe(); it(); before(); after(); beforeEach(); afterEach();
  85. 85. expectAssertions
  86. 86. assertSyntax assert.isObject(person); assert.property(person,  "age"); assert.isNumber(person.age); assert.equals(person.age,  34); expectSyntax expect(person).to.be.an('object');        .with.property('age')        .that.is.a('number')        .that.equals(34);
  87. 87. var  expect  =  require('chai').expect;
  88. 88. assertionChains for readability
  89. 89. expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  90. 90. .to .be .been .is .that .and .have .with syntaxSugar for readability
  91. 91. expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  92. 92. expect(person).to.exist        .and.be.an('object')        .with.property('age')        .that.is.to.exist        .and.is.a('number')        .and.equals(34);
  93. 93. .property subjectChange from original object
  94. 94. expect(person)    .that.is.an('object')    .with.property('address')        .that.is.an('object')        .with.property('city')            .that.is.a('string')            .and.equals('Detroit')
  95. 95. shouldAssertions
  96. 96. assertSyntax assert.isObject(person); assert.property(person,  "age"); assert.isNumber(person.age); assert.equals(person.age,  34); shouldSyntax person.should.be.an('object')        .with.property('age')        .that.is.a('number')        .that.equals(34);
  97. 97. var  should  =  require('chai').should();
  98. 98. var  chai      =  require('chai')    ,  expect  =  chai.expect    ,  should  =  chai.should();
  99. 99. assertionChains same as expect style* except for existence
  100. 100. expect(foo).to.not.exist; expect(bar).to.exist; should.not.exist(foo); should.exist(foo);
  101. 101. skippedTests
  102. 102. skipSyntax 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        }); });
  103. 103. 1 describe('Assertions', function() { describe('of truthiness', function() { 2 it('should pass on truthiness', function() { 3 assert.isTrue(true); 4 }); 5 it('should pass on falsiness', function() { 6 assert.isFalse(false); 7 }); 8 }); 9 describe('of type', function() { 10 it.skip('should pass on number', function() { 11 assert.isNumber(5); 12 }); 13 it('should pass on object'); 14 }); 15 16 }); 17 18
  104. 104. $  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 $  _
  105. 105. Skip is available in bdd only
  106. 106. mockingObjects
  107. 107. JavaScript ships with a mocking framework ...it’s called JavaScript
  108. 108. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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();
  109. 109. nock HTTP Mocking Library
  110. 110. nock github: flatiron/nock install: npm install nock
  111. 111. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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();
  112. 112. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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();
  113. 113. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 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();
  114. 114. nock nock.recorder.rec(  ); nock.recorder.play(  );
  115. 115. Sinon.js Spies, Stubs, & Mocks
  116. 116. Sinon.js github: cjohansen/Sinon.JS install: npm install sinon
  117. 117. browserTesting
  118. 118. Zombie.js Fast, headless browser
  119. 119. Zombie.js github: assaf/zombie install: npm install zombie
  120. 120. loadTesting
  121. 121. nodeload Performance Suite
  122. 122. nodeload github: benschmaus/nodeload install: npm install nodeload
  123. 123. activelyPractice
  124. 124. Color your whole life healthier
  125. 125. jay harris P R E S I D E N T jay@aranasoft.com #testdrivennode @jayharris

×