Test-Driven Development with Mocha: How to
A code kata is an exercise in programming which helps a programmer hone their skills through practice and repetition.
The term was probably first coined by Dave Thomas, co-author of the book The Pragmatic Programmer, in a bow to the Japanese concept of kata in the martial arts.
Katas
- Sound Player
- Hello World
- FizzBuzz
- Rock Paper Scissors
- Primes
Setup
All project dependencies are installed and managed via npm, the Node.js package manager.
npm install
npm test
Alternatively you can use Yarn.
yarn
yarn test
Continuous Integration with travis-ci.org
Travis CI is a hosted, distributed continuous integration service used to build and test software projects hosted at GitHub.
In order to use Travis CI with your JavaScript projects you must use output on console instead of the html.
- Click on
+
sign to add new repository. - Login with your Github credentials.
- Select the repository.
- Update your
package.json
adding:
"scripts": {
"test": "mocha ./**/*.spec.js"
},
- Add a
.travis.yml
file to your repository to tell Travis CI what to build:
language: node_js
node_js:
- "12"
- note:
npm install
andnpm test
are automatically executed by Travis CI. - Commit and push your changes.
That’s it!
Test-driven development
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle:
- first the developer writes an (initially failing) automated test case that defines a desired improvement or new function.
- then produces the minimum amount of code to pass that test.
- finally refactors the new code to acceptable standards.
Toolkit
Mocha is a feature-rich JavaScript test framework running on Node.js and in 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.
Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.
Sinon is a standalone test spies, stubs and mocks for JavaScript. Works with any unit testing framework.
Istanbul instruments your ES5 and ES2015+ JavaScript code with line counters, so that you can track how well your unit-tests exercise your codebase. The nyc command-line-client for Istanbul works well with most JavaScript testing frameworks: tap, mocha, AVA, etc.
Stryker uses one design mentality to implement mutation testing. It’s easy to use and fast to run. Stryker will only mutate your source code, making sure there are no false positives.
Example
Following an example of Test-Driven Development using Mocha and Chai for the most famous application: Hello World
!
Setup is easy, just run an npm
command and change few lines into your package.json
.
npm install -D mocha chai
"scripts": {
"test": "mocha ./**/*.spec.js"
},
Now you are able to run unit tests with npm test
.
If you want to use ES6 syntax with import/export you will need
babel
also. To understand more about it, you can checkout this project, I’m using it 😄
First of all we should create a new file HelloWorld.spec.js
.
Now we can start writing our first test.
//- HelloWorldSpec.js
const expect = require('chai').expect;
const HelloWorld = require('./HelloWorld');
describe('HelloWorld', () => {
it('should exist.', () => {
// given
new HelloWorld();
});
});
❗ RED - Try running test and it will fail.
Create a new file HelloWorld.js
.
The next step is writing some code that would cause the test to pass.
//- HelloWorld.js
function HelloWorld() {
}
module.exports = HelloWorld;
💚 GREEN - Try running test and it will pass.
❔ Need for refactoring?
We have a green bar! Now we can write a new test.
//- HelloWorld.spec.js
...
it('should greet() correcly.', () => {
// given
const helloWorld = new HelloWorld();
// then
expect(helloWorld.greet()).to.equal('Hello world');
});
...
❗ RED - Try running test and it will fail.
Now we can write some code that would cause the test to pass.
//- HelloWorld.js
...
HelloWorld.prototype.greet = function () {
return 'Hello world';
};
💚 GREEN - Try running test and it will pass.
❔ Need for refactoring?
🎉 Done
SPEC - HelloWorld.spec.js
//- HelloWorld.spec.js
const expect = require('chai').expect;
const HelloWorld = require('./HelloWorld');
describe('HelloWorld', () => {
it('should exist.', () => {
// given
new HelloWorld();
});
it('should greet() correcly.', () => {
// given
const helloWorld = new HelloWorld();
// then
expect(helloWorld.greet()).to.equal('Hello world');
});
});
SRC - HelloWorld.js
//- HelloWorld.js
function HelloWorld() {
}
HelloWorld.prototype.greet = function () {
return 'Hello world';
};
module.exports = HelloWorld;
Now if we decide to refactor the application moving from prototype
to class
, we can do it without fear.
So, let’s do this 😎
//- HelloWorld.js
module.exports = class {
greet() {
return 'Hello world';
}
}
💚 GREEN - Try running test and it will pass.
Further readings
- Test Driven Development: By Example (Kent Beck) - Addison-Wesley
- JavaScript Testing with Jasmine (Evan Hahn) - O’Reilly Media
- Jasmine JavaScript Testing (Paulo Ragonha) - Packt Publishing
- JavaScript Unit Testing (Hazem Saleh) - Packt Publishing