Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing with Node.js

Testing with Node.js

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.

Tags:
casper, jscoverage, make, makefile, mocha, node, node.js, spooky, testing

Jonathan Waller

November 09, 2012
Tweet

More Decks by Jonathan Waller

Other Decks in Programming

Transcript

  1. Content What is Node.js? Testing the code Checking test coverage

    Testing the user interface Pulling it all together 3
  2. What is Node.js? $  node  example.js Server  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
  3. Node is non-blocking Blocking code var  fileContents1  =  fs.readFileSync('file1.txt'); console.log(fileContents1);

    var  fileContents2  =  fs.readFileSync('file2.txt'); console.log(fileContents2); 6 0s 5s 10s
  4. Node is non-blocking Non-blocking code var  callback  =  function(err,  fileContents){

      console.log(fileContents); } fs.readFile('file1.txt',  callback); fs.readFile('file2.txt',  callback); 7 0s 5s 10s
  5. 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()  {       ...
  6. 11

  7. 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
  8. 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
  9. 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
  10. 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
  11. 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); =
  12. 19

  13. 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
  14. UI testing SpookyJS is a scriptable web testing framework for

    Mocha Wrapper for CasperJs and PhantomJS Uses WebKit, so supports client-side Javascript 23 WEBKIT CasperJS SpookyJS
  15. 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
  16. 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
  17. Make: Folder structure 28 testbackend:   @mocha  $$(find  test/backend  -­‐name

     "*.js")  -­‐r  expect.js  -­‐R  spec ...ͳͲ
  18. Testing the backend Makefile Run tests $  make  testbackend 29

    testbackend:   @./node_modules/.bin/mocha  $$(find  test/backend  -­‐name  "*.js")  -­‐r  expect.js  -­‐R  spec
  19. Checking test coverage Makefile 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.'
  20. 31

  21. 32 Run tests $  make  testfrontend Makefile Testing the frontend

    test:   @./node_modules/.bin/mocha  $$(find  test/frontend  -­‐name  "*.js")  -­‐r  expect.js  -­‐R  spec
  22. 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
  23. 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
  24. References Node.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