/se17b-hw4-learn-tdd-sabagsapir

se17b-hw4-learn-tdd-sabagsapir created by GitHub Classroom

Primary LanguageHTML

Learn Mocha Build Status Test Coverage Code Climate Issue Count devDependencies Status

A Quick Guide to mocha.js: Test Driven Development (TDD) in node.js and other Vsersion Control and Continuous Integration (CI) Tools

Note: This tutorial is an intro to Testing with Mocha. It is a fork of https://github.com/dwyl/learn-mocha adapted as an excercise for a software engineering course.

For students, follow these steps:

  • Using the classroom invitation, make your own copy of this toutorial
  • Clone localy (if working on a public machine make sure to git config your usename and email)
  • Follow this tutorial, while commiting your work right after every step.
  • In the commit messages use a prefix of "RED", "GREEN", or "REFACTOR" according to the step, e.g. "RED: a failing test for a missing module"
  • There are colored circle hints following the various steps: RED GREEN REFACTOR
  • Finally, complete your details below, commit and push back to your github tutorial repository

My details:

  • Excercise: HW4 - TDD
  • Name: Sapir Sabag
  • ID#: 308388636
  • github username: sabagsapir
  • Estimation of hours I worked on it: 3 and a helf hours

If you have an improvement suggestion or a bug fix for this tutorial, please open an issue here or send a PR with a fix.

If you are looking for a more detailed Test Driven Development (TDD) Tutorial see: https://github.com/dwyl/learn-tdd

The real meme :) 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" ...


Pre-requisites

  • Instalaion of node and npm
  • A bash shell

Installation

Assuming you're in the cloned repository directory (cd learn-tdd-username..)

npm install mocha --save-dev

(Alterntively, you can install mocha globally, by: sh npm install mocha -g --save-dev)

You should see some output confirming it is installed:

Mocha Installed

More info: http://mochajs.org/#installation

Tip: avoid installing node.js modules using sudo
see: http://stackoverflow.com/questions/16151018/npm-throws-error-without-sudo

First Tests

Try Running Tests

By typing the command npm test in your terminal (or mocha if you installed it globally, or running it directly from ./node_modules/mocha/bin/mocha) the mocha comand line program will look for a /test directory and run any .js files it contains:

npm test

You should see a failure message, since no tests were found (this is actually a very first failing/RED phase).

Mocha 0 Test Run

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 in your text editor

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 contains:

npm test

Mocha 1 Test Passes

(We're not committing this test, since it is just an example, soon to be replaced)

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, remove the describe block and 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:

npm test

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.

For the sake of this tutorial this is already a failure which should be documented as a failure step.

RED

Create the Module File

Create a new file for our cash register cash.js (in the root directory):

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

GREEN

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

RED

Write Just Enough Code to Make the Test Pass

Add the following to cash.js:

var C = {};                    // C Object simplifies exporting the module
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

GREEN

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 (indise the inner describe function):

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

RED

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

GREEN

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

RED

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 (so we won't even commit this solution!) . 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;
};
module.exports = C;            // export the module with a single method

GREEN

Add one more test to ensure we are fully exercising our method, especially if you also suggested a different implementation:

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

GREEN

Test Code Refactoring

Our tests are also code and are starting to get messy, suggest an improvement to the test code (comments, meaningful names, remove redundant tests, separating into classes of inputs, etc.)

REFACTOR


Other Tools

Code Coverage

We are using istanbul for code coverage. For more details concerning istanbul check out the other brief tutorial: https://github.com/dwyl/learn-istanbul

Install istanbul:

npm install istanbul --save-dev

(or globally, with npm install istanbul -g)

Run the following command to get a coverage report:

npm run cover

(or directly ./node_modules/.bin/istanbul cover _mocha -- -R spec, or globally, with istanbul cover _mocha -- -R spec) Note: on Windows paths maybe slightly different, e.g. istanbul cover node_modules/mocha/bin/_mocha, see here.

You should see:

Istanbul Coverage

or if you prefer the lcov-report (open index.html file at coverege/lcov-report):

Istanbul Coverage Report

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

Commit the report, as well.

REFACTOR

Continuous Integration with Travis

For more details concerning Travis CI check out the tutorial: https://github.com/dwyl/learn-travis

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

Travis Enabled

There are other tool that can be chained, see for example the badges in the begining of this page.

Update the link below in the README file, to point to your (e.g.: .com/jce-il-ex/learn-mocha-my-name repository status (as well as in the beginning). Note: if your repository is private the travis-ci sufix is .com.

Travis Build Status

REFACTOR

Pull request

Finally, use the github interface of your own repository to fork it into your account. Then make another development step, e.g. another test or refactoring (or at least updating your submission details section), commit it to the new repository and then open a pull request to the original repository.

REFACTOR

Open PR

See more about forks and pull requests in the documentation.

Since the original repository has a travis integration defined already, your request will be automatically checked to being covered by tests!

Use the github interface to merge this commit and close the pull request.

PR Status

This mechanism is used for collabiration with a repository owners, here we just demonstrate it's use.

Submission

Push all your commit steps back to the excecise repository on github - make sure also that your pull request is valid and merged.

git push

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://mochajs.org)
  • 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. :-)