Giter Site home page Giter Site logo

learn-mocha's Introduction

Learn Mocha Build Status Coverage Status Code Climate Dependencies

Quick Guide to mocha.js Test Driven Development (TDD) / Behaviour Driven Development (BDD) in node.js

Cowboy Coder

We all know Cowboy Coders. (If you don't, its you!)

The "I just get things done" developer who writes "quick fixes" and maintains "I don't have time to write tests" or "Writing tests for my code takes longer" and then acts surprised when everything starts breaking ... "it was working this morning" ... indeed!


Installation

sudo npm install -g mocha

You should see some output confirming it installed:

Mocha Installed

More info: http://visionmedia.github.io/mocha/#installation

First Tests

Create Test Directory

In your project create a new /test directory to hold your tests:

mkdir test

Create test.js File

Now create a new file ./test/test.js:

vi ./test/test.js

and write/paste the following code:

var assert = require("assert"); // node.js core module

describe('Array', function(){
  describe('#indexOf()', function(){
    it('should return -1 when the value is not present', function(){
      assert.equal(-1, [1,2,3].indexOf(4)); // 4 is not present in this array so indexOf returns -1
    })
  })
})

Run Test

By typing the command mocha in your terminal the mocha comand line program will look for a /test directory and run any .js files it conains:

mocha

Mocha 1 Test Passes

A More Useful TDD Example (Cash Register Mini Project)

While I'm the first to agree that cash-less payments are the future, paying with cash is something everyone can relate to and is therefore a good example to use. (think of better TDD example? tell me!)

Basic Requirements

Given a Total Payable and Cash From Customer Return: Change To Customer (notes and coins).

Essentially we are building a simple calculator that only does subtraction (Total - Cash = Change), but also splits the result into the various notes & coins.

In the UK we have the following Notes & Coins:

GBP Notes GBP Coins

see: http://en.wikipedia.org/wiki/Banknotes_of_the_pound_sterling (technically there are also £100 and even £100,000,000 notes, but these aren't common so we can leave them out. ;-)

If we use the penny as the unit (i.e. 100 pennies in a pound) the notes and coins can be represented as:

  • 5000 (£50)
  • 2000 (£20)
  • 1000 (£10)
  • 500 (£5)
  • 200 (£2)
  • 100 (£1)
  • 50 (50p)
  • 20 (20p)
  • 10 (10p)
  • 5 (5p)
  • 2 (2p)
  • 1 (1p)

this can be represented as an Array:

var coins = [5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1]

Note: the same can be done for any other cash system ($ ¥ €) simply use the cent, sen or rin as the unit and scale up notes.

The First Test

If you are totally new to TDD I recommend reading this intro article by Scott Ambler (especially the diagrams) otherwise this (test-fail-code-pass) process may seem strange ...

In Test First Development (TFD) we write a test first and then write the code that makes the test pass.

so, back in our ./test/test.js file add the following line:

var C = require('../cash.js');  // our module

Watch it Fail

Back in your terminal window, re-run the mocha command and watch it fail:

mocha

Mocha TFD Fail

This error ("Cannot find module '../cash.js'") is pretty self explanatory. We haven't created the file yet so test.js is requesting a non-existent file!

Q: Why deliberately write a test we know is going to fail...?
A: To get used to the idea of only writing the code required to pass the current (failing) test.

Create the Module File

Create a new file for our cash register cash.js:

touch cash.js

Note: We are not going to add any code to it just yet.

Re-run the mocha command in terminal, it will pass (zero tests)

Mocha Pass 0 Tests

Lets add a test to ./test/test.js and watch it fail again:

var assert = require("assert"); // core module
var C = require('../cash.js');  // our module

describe('Cash Register', function(){
  describe('Module C', function(){
    it('should have a getChange Method', function(){
      assert.equal(typeof C, 'object');
      assert.equal(typeof C.getChange, 'function');
    })
  })
})  

Re-run mocha:

Mocha 1 Test Failing

Write Just Enough Code to Make the Test Pass

Add the following to cash.js:

var C = {};                    // C Object simplifies exporting the moduel
C.getChange = function () {    // enough to satisfy the test
    'use strict';              
    return true;               // also passes JSLint
};
module.exports = C;            // export the module with a single method

Re-run mocha (now it passes):

Mocha 1 Test Passes

Write A Real Test

Going back to the requirements, we need our getChange method to accept two arguments/parameters (totalPayable and cashPaid) and return an array containing the coins equal to the difference:

e.g:

totalPayable = 210         // £2.10
cashPaid     = 300         // £3.00
dfference    =  90         // 90p
change       = [50,20,20]  // 50p, 20p, 20p

Add the following test to ./test/test.js:

it('getChange(210,300) should equal [50,20,20]', function(){
    assert.deepEqual(C.getChange(210,300), [50,20,20]);
})

Note: use assert.deepEqual for arrays see: http://stackoverflow.com/questions/13225274/

Mocha Assertion Error

Write the Method to Pass the Test

What if I cheat?

C.getChange = function (totalPayable, cashPaid) {
    'use strict';
    return [50, 20, 20];    // just enough to pass :-)
};

This will pass:

Mocha Passing

This only works once. When the Spec (Test) Writer writes the next test, the method will need to be re-written to satisfy it.

Lets try it. Work out what you expect:

totalPayable = 486           // £4.86
cashPaid     = 1000          // £10.00
dfference    = 514           // £5.14
change       = [500,10,2,2]  // £5, 10p, 2p, 2p

Add the following test to ./test/test.js and re-run mocha:

it('getChange(486,1000) should equal [500, 10, 2, 2]', function(){
    assert.deepEqual(C.getChange(486,1000), [500, 10, 2, 2]);
})

As expected, our lazy method fails:

Mocha 3 Test Fails

Keep Cheating or Solve the Problem?

We could keep cheating by writing a series of if statements:

C.getChange = function (totalPayable, cashPaid) {
    'use strict';
    if(totalPayable == 486 && cashPaid == 1000)
        return [500, 10, 2, 2]; 
    else if(totalPayable == 210 && cashPaid == 300)
        return [50, 20, 20]; 
};

The Arthur Andersen Approach gets results:

Mocha 3 Passing

But its arguably more work than simply solving the problem. Lets do that instead. (Note: this is the readable version of the solution! feel free to suggest a more compact algorithm)

var C = {};     // C Object simplifies exporting the module
C.coins = [5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1]
C.getChange = function (totalPayable, cashPaid) {
    'use strict';
    var change = [];
    var length = C.coins.length;
    var remaining = cashPaid - totalPayable;          // we reduce this below

    for (var i = 0; i < length; i++) { // loop through array of notes & coins:
        var coin = C.coins[i];

        if(remaining/coin >= 1) { // check coin fits into the remaining amount
            var times = Math.floor(remaining/coin);        // no partial coins

            for(var j = 0; j < times; j++) {     // add coin to change x times
                change.push(coin);            
                remaining = remaining - coin;  // subtract coin from remaining
            }
        }
    }
    return change
};

Add one more test to ensure we are fully exercising our method:

totalPayable = 1487                                 // £14.87  (fourteen pounds and eighty-seven pence)
cashPaid     = 10000                                // £100.00 (one hundred pounds)
dfference    = 8513                                 // £85.13
change       = [5000, 2000, 1000, 500, 10, 2, 1 ]   // £50, £20, £10, £5, 10p, 2p, 1p
it('getChange(1487,10000) should equal [5000, 2000, 1000, 500, 10, 2, 1 ]', function(){
    assert.deepEqual(C.getChange(1487,10000), [5000, 2000, 1000, 500, 10, 2, 1 ]);
})

Mocha 4 Passing


Bonus Level

Code Coverage

We are using istanbul for code coverage. If you are new to istanbul check out my brief tutorial: https://github.com/nelsonic/learn-istanbul

Install istanbul:

npm install istanbul -g

Run the following command to get a coverage report:

istanbul cover _mocha -- -R spec

You should see:

Istanbul Coverage

or if you prefer the lcov-report:

Istanbul Coverage Report

100% Coverage for Statements, Branches, Functions and Lines.

Travis

If you are new to Travis CI check out my tutorial: https://github.com/nelsonic/learn-travis

Visit: https://travis-ci.org/profile Enable Travis for learn-travis project

Travis Enabled

Build Status

Travis Build Pass

Done.


Background

What is Mocha?

Mocha is a JavaScript test framework running on node.js and the browser.

Mocha Logo

Made by TJ Holowaychuk creator of Express (by far the most popular node.js web framework), Mocha is TJ's answer to the problem of testing JavaScript.

Why Mocha?

At last count there were 83 testing frameworks listed on the node.js modules page: https://github.com/joyent/node/wiki/modules#wiki-testing this is both a problem (too much choice can be overwhelming) and good thing (diversity means new ideas and innovative solutions can flourish).

There's no hard+fast rule for "which testing framework is the best one?"

Over the past 3 years I've tried: Assert (Core Module), Cucumber, Expresso Jasmine, Mocha, Nodeunit, Should, and Vows

My criteria for chosing a testing framework:

  • Simplicity (one of TJ's stated aims)
  • Elegance (especially when written in CoffeeScript)
  • Speed (Mocha is Fast. 300+ tests run in under a second)
  • Documentation (plenty of real-world examples: http://visionmedia.github.io/mocha/)
  • Maturity (Battle-tested by thousands of developers!)

Advanced:

  • Easy to Trouble-shoot (Plenty of Answered Questions on stackoverflow)
  • Automatic Test Running when File Changes (using Watchr/Grunt)
  • Detailed reports of test execution (extensible reports!)

Notes

Other Mocha Tutorials/Background

Test Driven Development (TDD) Background/Philosophy

Further Reading


Trying to think of a good example for TDD ...

Rant

Code without tests is like a building without a foundation!

Building Collapse

Its only a matter of time before it all comes crashing down ...

Is Test Driven Development (TDD) a silver bullet for all my software development woes? Short answer: No. There is a lot more that goes into writing great software than just having tests. But without tests reliability is impossible.

If you are not doing TDD in your projects I'm probably not going to be the one to change your mind by evangelizing about it. I know plenty of people calling themesleves "developers" who stubbornly cling to the idea that testing is for "QA" or "That's why we have testers" and wish them nothing but the best of luck! I just cant't work with you or use your "product", no hard feelings. :-)

learn-mocha's People

Contributors

nkamc avatar

Watchers

 avatar  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.