Andrew Jakubowicz

@spyr1014

Testing for the novice JavaScript developer

“If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.” ~Gerald Weinberg

I love testing. Quite early in my journey of learning to code, I heard of this “Test Driven Development” thing. Around the same time I was starting to expand the scope of my projects, and was running into very tricky bugs. Sometimes things that worked stopped working and I had no idea why. Testing only revealed more problems; my code was a giant tangled mess that couldn’t be tested. Testing my code has fundamentally improved my coding, and I hope this introduction helps you too.

What I hope you’ll gain from this post:

  • Setting up Mocha (a testing framework) and chai (an assertion library).
  • Writing and running your tests.
  • Synchronous tests.
  • Asynchronous tests (callbacks).
  • Some features I’ve used a lot (just so you know they exist).

I’ll expect a very basic understanding of command line and npm.

Setup

First let’s make a new project. Create a folder and enter npm init -y into the command line to initialize the project with npm.

Install mocha with the command below.

npm install mocha -g or sudo npm install mocha -g

And install chai with:

npm install chai --save-dev

Mocha is the framework that is used to set up the tests, run the tests, name the tests and do your tests housekeeping. Chai is an assertion library, meaning it’s the thing that you’ll use to check if things are doing what you expect. This will become more clear with the examples that follow.

In your root directory create a file called index.js. This will be the file we’ll test. Also create a folder called test and put a file inside called index.spec.js. This file is where we will write the tests for index.js.

Your project should look like so:

├── yourAwesomeProject/
│ ├── index.js
│ ├── test
│ │ ├── index.spec.js
│ ├── package.json

Alright! We’ve done nothing, but now is a perfect time to make sure mocha is working. Type mocha test into your command prompt.

Hopefully you get something like this -> 0 passing (3ms)

You just ran the test runner! There are no tests there, but that doesn’t matter. We’ll fill it with tests soon enough.

Writing your first test!

Write the following code into your index.js.

// Stub
exports.addTwoNumbers = function (a, b) {
return 0;
}

This function is called a “stub” function. This code doesn’t have any logic, but it still returns a number. This allows us to write a test for it without the hassle of trying to implement the function logic yet.

Jump over to your index.spec.js file. I’ll show you the code, and then I’ll break it down. Make sure to type it out yourself.

var index = require("../index");
var chai = require("chai");
var expect = chai.expect;
describe("index.js tests", function() {
it("addTwoNumbers returns a number", function() {
expect(index.addTwoNumbers(0, 0)).to.be.a("number");
});
});

To make sure everything is working, once again type in mocha test. You should get this cool snazzy output in your terminal:

index.js tests
✓ addTwoNumbers returns a number
1 passing (11ms)

Everything is working, so let’s understand what we’ve written.

The first two lines import your file index.js which contains the function you want to test and chai which is the assertion library you previously installed using npm. var expect = chai.expect; just gives us a short way to call chai.expect.

describe() — Grouping a collection of individual tests

describe(<Heading of Tests>,testsInCallback);

Describe is like a heading. It’s a way to group a collection of tests together. You’ll notice in the above example, ‘index.js tests’ is the heading, and ‘addTwoNumbers returns a number’ is the individual test (it) grouped with it.

it() — ‘Individual Test’

it(<Name of Test>, testInCallback);

Every test assertion needs its own individual test. For example, to test that our function ‘addTwoNumbers’ can actually add two numbers, we should add at least one other test.

We’re going to add another test:

var index = require("../index");
var chai = require("chai");
var expect = chai.expect;
describe("index.js tests", function() {
it("addTwoNumbers returns a number", function() {
expect(index.addTwoNumbers(0, 0)).to.be.a("number");
});
    it("addTwoNumbers can add 1 + 2", function() {
expect(index.addTwoNumbers(1,2)).to.equal(3);
});
});

We’ve added it to the group “index.js tests’ demarcated by the describe function, and we’ve added it below the previous individual test. You’ll notice that the syntax used by expect is really easy to read.

expect(<your function>).to.equal(<something>); is how you check equality. If you are checking deep equality, for example to check if the fields inside objects are equal you can just add ‘deep’ to the chain like so:

expect(<yourObject>).to.deep.equal(<anotherObject>);

The documentation for chai’s expect is clear and straight forward so I recommend you take a look.

If you run mocha tests now you’ll notice that you get a test failure.

index.js tests
✓ addTwoNumbers returns a number
1) addTwoNumbers can add 1 + 2
1 passing (19ms)
1 failing
1) index.js tests addTwoNumbers can add 1 + 2:
AssertionError: expected 0 to equal 3
+ expected - actual
      -0
+3
at Context.<anonymous> (test/index.spec.js:9:45)

I find this information extremely valuable as it allows you to see what your expect actually returned (in this case a zero).

Now that your test is failing, you can resolve it by fixing the code in your index.js. I would usually write more tests, but you’ve now got a way to make sure what you’re doing works. This doesn’t make your code perfect but will allow you to at least have some peace of mind.

As a quick note, this technique is called “Red, Green, Refactor”. We’ve only covered the “Red, Green” part, but the idea is that you write a test that fails. Then fix your code to pass the test. Finally you can refactor your code with the peace of mind that you’ve got a test to check that your refactor doesn’t break everything.

We’ve just spent the whole time so far with synchronous tests. But what if you need to test callbacks or promises?

Asynchronous tests

Testing asynchronous code is not difficult, but requires one additional piece of code that hasn’t been covered yet called done(). Without done, mocha will dispatch your asynchronous code and continue without waiting for the assertion or execution of your callback. I’ll show you with a concrete example.

describe("Broken Async Code", function() {
it('Async test', function() {
setTimeout(function(){
//failing test
expect(true).to.be.false;
}, 1000);
});
});

The test above passes. This is because it only fails a test if it catches the error from the expect. Because this is asynchronously executed (by a delay of 1 second), mocha skips over this as a passing test. We need to tell Mocha to wait by adding done. done must be added as an argument and after the assertion.

describe("Fixed Async Code", function() {
it('Async test', function(done) { // Done added here.
setTimeout(function(){
//failing test
expect(true).to.be.false;
done(); // Tells mocha to run next test.
}, 1000);
});
});

You’ll notice that when you run mocha test, it’ll pause for 1 second and then give a failure. Sadly this isn’t the really detailed error that mocha gave earlier in the synchronous examples. This is solved by mocha’s built-in promises, but this is another topic and out of scope for this post. If you’re interested I recommend looking at this (it’s a really nice way of writing async tests with detailed responses).

Some super useful features I use

  • To run a single test or group just add .only to the end of describe or it. Example: describe.only("index.js tests", function () { or it.only("addTwoNumbers returns a number", function () {
  • Investigate the following functions that can be used inside a describe block. before(), beforeEach(), after(), afterEach(). These allow things to happen before and after each of your individual tests. (Good for set up and clean up)
  • Mocha can be used in the browser! It loads up a cool web page with a todo list style format with all your tests. (This makes testing web pages far easier).

Good luck. And remember that testing is a skill, so practice will make perfect. :)

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.
To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Andrew Jakubowicz

Topics of interest

More Related Stories