Git Product home page Git Product logo

node_hands_on-unit_testing's Introduction

Node Hands On!

##Unit Testing with Mocha, MockRequire, and Make

What we're gonna do:

  • Learn what Mocha is
  • Learn how to get Mocha
  • Learn how to use Mocha
  • Learn what MockRequire is
  • Learn how to get MockRequire
  • Learn how to use MockRequire with Mocha
  • Learn how to make it all easier with... make

What is Mocha?

From http://visionmedia.github.com/mocha/ :

Mocha is a feature-rich JavaScript test framework running on node and the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases

It's (IMHO) the most fully featured, easiest to use testing framework for Node. It rocks.

Get yoself some Mocha!

install it

npm install mocha -g

create a project & package.json

MacBookPro-MWW:node_hands_on-unit_testing matt$ vim package.json 

add it to the project

...
"dependencies": {},
  "devDependencies": {
    "mocha": "*",
    "should": "*"
  },
...

install your dependencies

MacBookPro-MWW:node_hands_on-unit_testing matt$ npm install
npm http GET https://registry.npmjs.org/should
npm http GET https://registry.npmjs.org/mocha
npm http 304 https://registry.npmjs.org/mocha
npm http GET https://registry.npmjs.org/mocha/-/mocha-1.3.2.tgz
npm http 200 https://registry.npmjs.org/should
npm http GET https://registry.npmjs.org/should/-/should-1.1.0.tgz
npm http 200 https://registry.npmjs.org/mocha/-/mocha-1.3.2.tgz
npm http 200 https://registry.npmjs.org/should/-/should-1.1.0.tgz
npm http GET https://registry.npmjs.org/commander/0.6.1
npm http GET https://registry.npmjs.org/growl
npm http GET https://registry.npmjs.org/jade/0.26.3
npm http GET https://registry.npmjs.org/diff/1.0.2
npm http GET https://registry.npmjs.org/debug
npm http 304 https://registry.npmjs.org/debug
npm http 304 https://registry.npmjs.org/commander/0.6.1
npm http 304 https://registry.npmjs.org/growl
npm http 304 https://registry.npmjs.org/diff/1.0.2
npm http 200 https://registry.npmjs.org/jade/0.26.3
npm http GET https://registry.npmjs.org/jade/-/jade-0.26.3.tgz
npm http 200 https://registry.npmjs.org/jade/-/jade-0.26.3.tgz
npm http GET https://registry.npmjs.org/mkdirp/0.3.0
npm http 304 https://registry.npmjs.org/mkdirp/0.3.0
[email protected] ./node_modules/should 
[email protected] ./node_modules/mocha 
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

and you're ready to rock. we'll be using both mocha and should in our tests.

MacBookPro-MWW:node_hands_on-unit_testing matt$ ls node_modules/
mocha  should

So, what do we do with this thing?

We teeeeeeeeeeest

  1. Note Mocha assumes all your tests live under a ./test directory. Be sure to 'mkdir test' and place your test .js files here

###simple:

Create a file called ./test/simple.js and place the following in it:

require('should');
describe('Array', function () {
  var arr = [1,2,3];
  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      arr.indexOf(5).should.equal(-1);
      arr.indexOf(0).should.equal(-1);
    });
  });
});

Next, run mocha. You should see the following output:

$ mocha

  ....

  ✔ 1 test complete (1ms)

###So, what did we just do?

####describe

mocha's describe method creates the function scope under which we're going to test some code. We're testing an Array. We're also testing the indexOf() function.

####Should

should is a node module that extends Object and adds neat properties and methods to Object.prototype, allowing you to do interesting comparisons and assertions. It helps you test that values are what you want them to be (or not what you don't). To learn more about should, read the README (https://github.com/visionmedia/should.js/blob/master/Readme.md) or just look at the code directly and see how it works (https://github.com/visionmedia/should.js/blob/master/lib/should.js#L302).

We just used it to perform two assertions inside of our describe blocks. When should methods fail a comparison, they throw an error. Had they thrown an error here, out test would have failed.

###What about asynchronous methods?

For testing async methods, mocha lets you pass a 'done' callback variable which you call when your test has completed.

describe('User', function () {
  describe('#save()', function () {
    it('should save without error', function (done) {
      var user = new User('Luna');
      user.save(function (err) {
        if (err) throw err;
        // we done. call it!
        done();
      });
    });
  });
});

The done callback will also throw if you pass it an error as the first param, and will otherwise consider success if you pass it anything else as the second param. As a result, you can just pass it in as a callback variable and let it do its magic.

describe('User', function () {
  describe('#save()', function () {
    it('should save without error', function (done) {
      var user = new User('Luna');
      // we done when the user says so. let him call it!
      user.save(done);
    })
  })
})

###What about functions/classes with dependencies?

On platforms like Java and .NET, dependency injection and Inversion of Control are popular topics. Why? Because by passing in fully instantiated dependencies, we don't get into a scenario where we can't easily mock out some dependency class/function, forcing us to test against a database or API where we'd rather use a stub.

Most node code require()s dependencies atop or within a module. Rarely do you find an IOC style of code used where dependencies are passed into a constructor.

So, how do you unit test code in a way that isolates it from its dependencies? MockRequire can help you. (https://github.com/mateodelnorte/mockrequire)

MockRequire lets you specify a json hash of required dependency names and stubs that you would like executed, instead of what would have otherwise been required.

var mockrequire = require('mockrequire');

describe('somethingInteresting', function () {

  somethingInteresting = mockrequire('../lib/somethingInteresting', {
    './dependency': function (callback) { callback(null, 'test'); }
  });

  describe('#funkytion', function () {
    it('should return what we say, and *like* it!', function (done) {
      somethingInteresting.funkytion(function (err, result) {
        result.should.eql('test KnowwhatI\mtalkinabout?');
        done();
      });
    });
  });
});

In the above example, we load a stub for the 'dependency' and stub out a method to be executed instead of the real one. Imagine that the 'dependency' function made a call against an API that cost money. Now we can keep our pocketbook closed while we test. ;)

###Mmmm. Cool. What about APIs 'n' Integration 'n' Stuff?

Easy answer: require('http'); You can always ping your APIs with http and https (or a module like superagent) requests and assert their responses against what you would expect:

require('should');

var util = require('util');

var superagent = require('superagent');

describe('our_little_api', function () {
  describe('#GET /funk', function () {
    it('should return the funk', function (done) {
      superagent.get('http://localhost:3000/funk', function (res) {
        res.should.have.property('body').eql({ YouGotThe: 'funk' });
        done();
      });
    });
  });
});

###Nice. So I start my API in one terminal, then run the tests in another?

##Heeeeeells no! Let's get Olde Schoole

###Makeing Life Easier

Make was created in 1977 for Bell Labs as a general software build tool. It's 35 years old. Know what that means?

It works.

Make allows us to write simple build targets (bash or other command line statements), like the following:

test: ;@echo "testing ${PROJECT}"; \
  -export NODE_ENV=development; \
	./node_modules/mocha/bin/mocha -R spec

that we can run from the command line like the following:

make test

and stuff just runs:

$ make test
export NODE_ENV=development; \
  ./node_modules/mocha/bin/mocha -R spec;


  somethingInteresting
    #funkytion
      ✓ should return the value from the dependency 

  somethingInteresting
    #funkytion
      ✓ should return what we say, and *like* it! 

  Array
    #indexOf()
      ✓ should return -1 when the value is not present 

  User
    #save()
      ✓ should save without error 

  User
    #save()
      ✓ should save without error 


  ✔ 5 tests complete (4ms)
  
$

All you need to do is create a file called makefile (usually in your project's base folder) and put your build targets within it. (like here: https://github.com/mateodelnorte/node_hands_on-unit_testing/blob/master/makefile)

One neat thing is that build targets (the names of the commands, you put to the left of the colon) can be chained and reused:

start: 
  @echo "starting API"; \
	node ./our_little_api/app.js & echo "$$!" > api.pid; \

stop: 
	-kill -9 `cat api.pid`; 

test: 
  export NODE_ENV=development; \
	./node_modules/mocha/bin/mocha -R spec;

test_integration: start integration stop clean

test_all: test test_integration

Calling make test_all, above, will 1) run our unit tests 2) start our site 3) run our integration tests 4) stop the site and 5) perform and file cleanup the tests need to return us to a clean state.

Dayum. That's nice.

##So, what have we covered?

-We learned what the testing framework mocha is and how we can use it to test synchronous and asynchronous code, as well as APIs -We learned how to use mockrequire to allow us to unit test code that may have dependencies we aren't interested in testing -We learned how to use makefiles to encapsulate run and test routines to make our lives easier when doing all this dev stuff

#Thanks!

@MateoDelNorte on GitHub @MattWalters5 on Twitter

node_hands_on-unit_testing's People

Stargazers

Brad Pillow avatar Emily Dirsh avatar Dan Oved avatar Matt Walters avatar Brandon Aaskov avatar Sebastian Castillo avatar

Watchers

Matt Walters avatar Mike Selender avatar James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.