{ Introduction to Testing with Mocha and Chai. }

Objectives:

By the end of this chapter, you should be able to:

  • Explain the benefits of testing code
  • Explain what a test runner is
  • Write unit tests with Mocha and Chai

Writing Tests: The Why

As you begin writing more complicated functions and larger applications, you're bound to make mistakes. Everyone does it, even professional programmers. When your programs grow they can become more difficult to reason about, and as hard as you may try it's impossible to predict every bug in your program. Fixing bugs also has a cost, as it can be quite easy for one fix to introduce bugs in other parts of your application.

Is there any way to avoid our programs becoming more brittle and difficult to maintain as they grow in complexity? Yes! The solution to this problem lies in testing our code as thoroughly as possible. In this chapter, you'll learn how to write tests in JavaScript that you can run automatically to verify that the code you're writing does what you expect. This makes it easier to protect against bugs, and to ensure that you don't introduce new bugs in your code as you add new features or rewrite old ones.

It might be difficult to appreciate the value of writing tests now, but it's a critical skill to have when you're working in a large codebase with a team of other developers. Are you down with TDD (Test Driven Development)? By the end of this chapter, hopefully you will be.

Writing Tests: The How

Before we see some code, let's define some technologies and functions we are going to be using.

mocha - this is our test runner and we will be using it to run all of our tests. A test runner is a tool that is responsible for running tests that you write and logging the results of the tests for you to see. You can read more about it here.

chai - this is our expectation/assertion library. chai provides additional ways for you to write tests; in particular, it lets you write tests so that they are quite straightforward to read. This isn't a necessary tool to use if you're writing tests with mocha, but you'll very often see them paired together. For now, you can simply think of chai as a way to enhance your tests and make them more readable. You can read more about chai here.

When we write our tests, there are a few functions that we'll be using quite frequently. Here are three of the most important ones:

describe - this function is given to us by mocha and it is what we use to organize our tests. You can think of a describe function like talking to someone and telling them "let me describe ____ to you." Very often when you're writing unit tests, you'll have one describe block per function you're testing (this will make more sense once you've seen some examples).

it - this function lives inside of describe functions. Inside of these it functions we make our expectations. Each it function corresponds to a test; if one of our expectations inside of the it function isn't met, the test fails.

This might all sound a little strange, so before we get into a JavaScript-specific example, let's just look at an example written in plain old English. Here's how you might scaffold some tests to check whether a planet in our solar system is Earth:

  • describe "Earth"
    • it "is round"
    • it "is the third planet from the sun"
    • it "is the densest of all the planets"

expect - this is a function given to us by chai. chai has a few different styles (expect/should/assert); all let you write readable tests, so which style you use is up to you. We'll use expect for the tests we write. You can read more about it here. When combined with describe and it, we can write tests that looks something like this:

  • describe "Earth"
    • it "is round"
      • expect (earth.isRound).to.equal(true)
    • it "is the third planet from the sun"
      • expect(earth.numberFromSun).to.equal(3)
    • it "is the densest of all the planets"
      • expect(earth.density).to.be.at.least(5.51)

Now that we have see this pseudo-code, let's look at some actual test code! Here's an example of test code written in JavaScript using mocha and chai:

var earth = {
    isRound: true,
    numberFromSun: 3,
    density: 5.51
};

describe("Earth", function(){

  it("is round", function(){
    expect(earth.isRound).to.equal(true);
  });

  it("is the third planet from the sun", function(){
    expect(earth.numberFromSun).to.equal(3);
  });

  it("is the densest of all the planets", function(){
    expect(earth.density).to.be.at.least(5.51);
  });

});

Note the syntax here: both describe and it take a string as their first parameter, and a callback as the second. The callback to a describe typically consists of several it functions. Inside of each it function is where we write our expectations.

Running tests in the browser

Writing tests is all well and good, but how do we actually run these tests? If you google around, you will see that most of the tests we write are in the terminal. For now, though, we are going to be using the browser (mocha.js, chai.js and mocha.css) to run our tests.

To get started, we'll create a basic HTML page, and include the relevant JavaScript and CSS files:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Our First Mocha Tests</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.css" rel="stylesheet" />
</head>
<body>
  <div id="mocha"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.js"></script>
  <script src="http://chaijs.com/chai.js"></script>
</body>
</html>

Note that we include a div with an id of mocha in the body. When we run the tests, mocha will display the results in this div; if you don't have an element with an id of mocha on the page, an error will show up when you try to run the tests.

Before including our test code, we'll also need to do a bit of setup for mocha. For now, let's just create a script tag in our HTML and throw some JavaScript inside of it:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Our First Mocha Tests</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.css" rel="stylesheet" />
</head>
<body>
  <div id="mocha"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.js"></script>
  <script src="http://chaijs.com/chai.js"></script>
  <script>
    mocha.setup('bdd'); // This sets up mocha and makes the describe function available to us
    var expect = chai.expect; // This makes the expect function available to us

    // PUT TESTS HERE!

    mocha.checkLeaks(); // checks to be sure no variables are leaked to the global namespace during execution of the tests
    mocha.run(); // runs the tests!
  </script>
</body>
</html>

Next, put the tests where the comment is, and refresh the page:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Our First Mocha Tests</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.css" rel="stylesheet" />
</head>
<body>
  <div id="mocha"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.js"></script>
  <script src="http://chaijs.com/chai.js"></script>
  <script>
    mocha.setup('bdd'); // This sets up mocha and makes the describe function available to us
    var expect = chai.expect; // This makes the expect function available to us

    var earth = {
        isRound: true,
        numberFromSun: 3,
        density: 5.51
    };

    describe("Earth", function(){

        it("is round", function(){
            expect(earth.isRound).to.equal(true);
        });

        it("is the third planet from the sun", function(){
            expect(earth.numberFromSun).to.equal(3);
        });

        it("is the densest of all the planets", function(){
            expect(earth.density).to.be.at.least(5.51);
        });

    });

    mocha.checkLeaks(); // checks to be sure no variables are leaked to the global namespace during execution of the tests
    mocha.run(); // runs the tests!
  </script>
</body>
</html>

You should see information on three passing tests in your browser! To see what failed tests look like, try setting earth.isRound to false and running the tests again.

In practice, you won't be writing JavaScript inside of your HTML files. Instead, you'll typically have a couple of external JavaScript files. One of them is called a spec file (short for specification), and will contain all of your tests. The other will be the code that you're actually testing. So a more standard way to write the HTML might look like this:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Our First Mocha Tests</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.css" rel="stylesheet" />
</head>
<body>
  <div id="mocha"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.0/mocha.js"></script>
  <script src="http://chaijs.com/chai.js"></script>
  <script>mocha.setup('bdd');</script>
  <!-- Spec file (contains all tests): -->
  <script src="yourFunctionsSpec.js"></script>
  <!-- JavaScript being tested: -->
  <script src="yourFunctions.js"></script>
  <script>
    mocha.checkLeaks();
    mocha.run();
  </script>
</body>
</html>

When you're ready, move on to Testing Continued

Continue