Welcome!

Eclipse Authors: JP Morgenthal, Yeshim Deniz, Liz McMillan, Elizabeth White, XebiaLabs Blog

Blog Feed Post

Testing your Frontend JavaScript code using Mocha, Chai and Sinon

Testing your frontend JavaScript code using mocha, chai, and sinon by Nicolas Perriault

This is a republished blog post by Nicolas Perriault. When I first read it I immediately thought it would fit perfectly on our Codeship blog. You may know Nicolas for his awesome CasperJS (an open source navigation scripting & testing utility written in Javascript for PhantomJS) or from various of his talks. We highly recommend following Nicolas on twitter and checking out his website.


As rich Web application complexity grows, if you want to keep your sanity, you need to unit test your frontend JavaScript code.

For the 4 past months, I’ve been working for Mozilla on some big project where such testing strategy was involved. While I wish we could use CasperJS in this perspective, Firefox wasn’t supported at the time and we needed to ensure proper compatibility with its JavaScript engine. So we went with using Mocha, Chai and Sinon and they have proven to be a great workflow for us so far.

The mocha testing framework and the chai expectation library

Mocha is a test framework while Chai is an expectation one. Let’s say Mocha sets up and describes test suites and Chai provides convenient helpers to perform all kinds of assertions against your JavaScript code.

So let’s say we have a Cow object we want to unit test:

// cow.js
(function(exports) {
  "use strict";

  function Cow(name) {
    this.name = name || "Anon cow";
  }
  exports.Cow = Cow;

  Cow.prototype = {
    greets: function(target) {
      if (!target)
        throw new Error("missing target");
      return this.name + " greets " + target;
    }
  };
})(this);

Nothing fancy, but we want to unit test this one.

Both Mocha and Chai can be used in a Node environment as well as within the browser; in the latter case, you’ll have to setup a test HTML page and use special builds of those libraries:

My advice is to store these files in a vendor subfolder. Let’s create a HTML file to test our lib:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Cow tests</title>
  <link rel="stylesheet" media="all" href="vendor/mocha.css">
</head>
<body>
  <div id="mocha"><p><a href=".">Index</a></p></div>
  <div id="messages"></div>
  <div id="fixtures"></div>
  <script src="vendor/mocha.js"></script>
  <script src="vendor/chai.js"></script>
  <script src="cow.js"></script>
  <script>mocha.setup('bdd')</script>
  <script src="cow_test.js"></script>
  <script>mocha.run();</script>
</body>
</html>

Please note we’ll be using Chai’s BDD Expect API, hence the mocha.setup('bdd') call here.

Now let’s write a simple test suite for our Cow object constructor in cow_test.js:

var expect = chai.expect;

describe("Cow", function() {
  describe("constructor", function() {
    it("should have a default name", function() {
      var cow = new Cow();
      expect(cow.name).to.equal("Anon cow");
    });

    it("should set cow's name if provided", function() {
      var cow = new Cow("Kate");
      expect(cow.name).to.equal("Kate");
    });
  });

  describe("#greets", function() {
    it("should throw if no target is passed in", function() {
      expect(function() {
        (new Cow()).greets();
      }).to.throw(Error);
    });

    it("should greet passed target", function() {
      var greetings = (new Cow("Kate")).greets("Baby");
      expect(greetings).to.equal("Kate greets Baby");
    });
  });
});

Tests should be passing, so if you open the HTML document in your browser, you should get something like:

Testing your frontend JavaScript code using mocha, chai, and sinon

If any of these expectations fails, you’ll be notified in the test results, eg. if we change the implementation of greets as below:

Cow.prototype = {
    greets: function(target) {
      if (!target)
        throw new Error("missing target");
      return this.name + " greets " + target + "!";
    }
  };<img src="http://blog.codeship.io/wp-content/uploads/2014/01/2_n1k0_cow-tests-ko.jpg" alt="Testing your frontend JavaScript code using mocha, chai, and sinon" width="885" height="382" class="alignnone size-full wp-image-670" />

You’ll get this instead:

Testing your frontend JavaScript code using mocha, chai, and sinon

How about testing asynchronous stuff?

Now imagine we implement a Cow#lateGreets method so the greetings come with a delay of one second:

Cow.prototype = {
    greets: function(target) {
      if (!target)
        throw new Error("missing target");
      return this.name + " greets " + target + "!";
    },

    lateGreets: function(target, cb) {
      setTimeout(function(self) {
        try {
          cb(null, self.greets(target));
        } catch (err) {
          cb(err);
        }
      }, 1000, this);
    }
  };

We need to test this one as well, and Mocha helps us with its optional done callback for tests:

describe("#lateGreets", function() {
    it("should pass an error if no target is passed", function(done) {
      (new Cow()).lateGreets(null, function(err, greetings) {
        expect(err).to.be.an.instanceof(Error);
        done();
      });
    });

    it("should greet passed target after one second", function(done) {
      (new Cow("Kate")).lateGreets("Baby", function(err, greetings) {<img src="http://blog.codeship.io/wp-content/uploads/2014/01/3_n1k0_cow-async-tests-ok.jpg" alt="Testing your frontend JavaScript code using mocha, chai, and sinon – timed out" width="885" height="402" class="alignnone size-full wp-image-671" />
        expect(greetings).to.equal("Kate greets Baby");
        done();
      });
    });
  });

Conveniently, Mocha will highlight any suspiciously long operation with red pills in case it wasn’t really expected:

Testing your frontend JavaScript code using mocha, chai, and sinon – timed out

Using Sinon for faking the environment

When you do unit testing, you don’t want to depend on stuff external to the unit of code under test. And while avoiding your functions to have side effects is usually a good practice, in Web development it’s not always easy task (think DOM, Ajax, native browser APIs, etc.)

Sinon is a great JavaScript library for stubbing and mocking such external dependencies and to keep control on side effects against them.

As an example, let’s imagine that our Cow#greets method wouldn’t return a string but rather directly log them onto the browser console:

// cow.js
(function(exports) {
  "use strict";

  function Cow(name) {
    this.name = name || "Anon cow";
  }
  exports.Cow = Cow;

  Cow.prototype = {
    greets: function(target) {
      if (!target)
        return console.error("missing target");
      console.log(this.name + " greets " + target);
    }
  };
})(this);

How to unit test this? Well, Sinon to the rescue! First, let’s add the Sinon script to our HTML test file:

<!-- ... -->
<script src="vendor/mocha.js"></script>
<script src="vendor/chai.js"></script>
<script src="vendor/sinon-1.7.1.js"></script>

We’ll stub the console object’s log and error methods so we can check they’re called and what’s passed to them:

var expect = chai.expect;

describe("Cow", function() {
  var sandbox;

  beforeEach(function() {
    // create a sandbox
    sandbox = sinon.sandbox.create();

    // stub some console methods
    sandbox.stub(window.console, "log");
    sandbox.stub(window.console, "error");
  });

  afterEach(function() {
    // restore the environment as it was before
    sandbox.restore();
  });

  // ...

  describe("#greets", function() {
    it("should log an error if no target is passed in", function() {
      (new Cow()).greets();

      sinon.assert.notCalled(console.log);
      sinon.assert.calledOnce(console.error);
      sinon.assert.calledWithExactly(console.error, "missing target")
    });

    it("should log greetings", function() {
      var greetings = (new Cow("Kate")).greets("Baby");

      sinon.assert.notCalled(console.error);
      sinon.assert.calledOnce(console.log);
      sinon.assert.calledWithExactly(console.log, "Kate greets Baby")
    });
  });
});

Several things to be noticed here:

  • beforeEach and afterEach are part of the Mocha API and allow to define setup and tear down operations for each test;
  • Sinon provides sandboxing, basically allowing to define and attach a set of stubs to a sandbox object you’ll be able to restore at some point;
  • When stubbed, real functions are not called at all, so here obviously nothing will be printed onto the browser console;
  • Sinon ships with its own assertion library, hence the sinon.assert calls; a sinon-chai plugin exists for Chai, you may want to have a look at it.

There are many cool other aspects of Mocha, Chai and Sinon I couldn’t cover in this blog post, but I hope it opened your appetite for investigating more about them. Happy testing!

Set up Continuous Integration and Deployment for your JavaScript projects. The Codeship supports GitHub and BitBucket projects and about any techstack you can imagine.

We want to thank Nicolas for granting us permission to republish his original blog post. If you have any questions please let us know in the comments.

Read the original blog entry...

More Stories By Manuel Weiss

I am the cofounder of Codeship – a hosted Continuous Integration and Deployment platform for web applications. On the Codeship blog we love to write about Software Testing, Continuos Integration and Deployment. Also check out our weekly screencast series 'Testing Tuesday'!

@ThingsExpo Stories
"We build IoT infrastructure products - when you have to integrate different devices, different systems and cloud you have to build an application to do that but we eliminate the need to build an application. Our products can integrate any device, any system, any cloud regardless of protocol," explained Peter Jung, Chief Product Officer at Pulzze Systems, in this SYS-CON.tv interview at @ThingsExpo, held November 1-3, 2016, at the Santa Clara Convention Center in Santa Clara, CA.
The cloud promises new levels of agility and cost-savings for Big Data, data warehousing and analytics. But it’s challenging to understand all the options – from IaaS and PaaS to newer services like HaaS (Hadoop as a Service) and BDaaS (Big Data as a Service). In her session at @BigDataExpo at @ThingsExpo, Hannah Smalltree, a director at Cazena, provided an educational overview of emerging “as-a-service” options for Big Data in the cloud. This is critical background for IT and data professionals...
Internet of @ThingsExpo has announced today that Chris Matthieu has been named tech chair of Internet of @ThingsExpo 2017 New York The 7th Internet of @ThingsExpo will take place on June 6-8, 2017, at the Javits Center in New York City, New York. Chris Matthieu is the co-founder and CTO of Octoblu, a revolutionary real-time IoT platform recently acquired by Citrix. Octoblu connects things, systems, people and clouds to a global mesh network allowing users to automate and control design flo...
The WebRTC Summit New York, to be held June 6-8, 2017, at the Javits Center in New York City, NY, announces that its Call for Papers is now open. Topics include all aspects of improving IT delivery by eliminating waste through automated business models leveraging cloud technologies. WebRTC Summit is co-located with 20th International Cloud Expo and @ThingsExpo. WebRTC is the future of browser-to-browser communications, and continues to make inroads into the traditional, difficult, plug-in web co...
Amazon has gradually rolled out parts of its IoT offerings, but these are just the tip of the iceberg. In addition to optimizing their backend AWS offerings, Amazon is laying the ground work to be a major force in IoT - especially in the connected home and office. In his session at @ThingsExpo, Chris Kocher, founder and managing director of Grey Heron, explained how Amazon is extending its reach to become a major force in IoT by building on its dominant cloud IoT platform, its Dash Button strat...
Complete Internet of Things (IoT) embedded device security is not just about the device but involves the entire product’s identity, data and control integrity, and services traversing the cloud. A device can no longer be looked at as an island; it is a part of a system. In fact, given the cross-domain interactions enabled by IoT it could be a part of many systems. Also, depending on where the device is deployed, for example, in the office building versus a factory floor or oil field, security ha...
In addition to all the benefits, IoT is also bringing new kind of customer experience challenges - cars that unlock themselves, thermostats turning houses into saunas and baby video monitors broadcasting over the internet. This list can only increase because while IoT services should be intuitive and simple to use, the delivery ecosystem is a myriad of potential problems as IoT explodes complexity. So finding a performance issue is like finding the proverbial needle in the haystack.
The idea of comparing data in motion (at the sensor level) to data at rest (in a Big Data server warehouse) with predictive analytics in the cloud is very appealing to the industrial IoT sector. The problem Big Data vendors have, however, is access to that data in motion at the sensor location. In his session at @ThingsExpo, Scott Allen, CMO of FreeWave, discussed how as IoT is increasingly adopted by industrial markets, there is going to be an increased demand for sensor data from the outermos...
Data is the fuel that drives the machine learning algorithmic engines and ultimately provides the business value. In his session at 20th Cloud Expo, Ed Featherston, director/senior enterprise architect at Collaborative Consulting, will discuss the key considerations around quality, volume, timeliness, and pedigree that must be dealt with in order to properly fuel that engine.
In his general session at 19th Cloud Expo, Manish Dixit, VP of Product and Engineering at Dice, discussed how Dice leverages data insights and tools to help both tech professionals and recruiters better understand how skills relate to each other and which skills are in high demand using interactive visualizations and salary indicator tools to maximize earning potential. Manish Dixit is VP of Product and Engineering at Dice. As the leader of the Product, Engineering and Data Sciences team at D...
SYS-CON Events has announced today that Roger Strukhoff has been named conference chair of Cloud Expo and @ThingsExpo 2017 New York. The 20th Cloud Expo and 7th @ThingsExpo will take place on June 6-8, 2017, at the Javits Center in New York City, NY. "The Internet of Things brings trillions of dollars of opportunity to developers and enterprise IT, no matter how you measure it," stated Roger Strukhoff. "More importantly, it leverages the power of devices and the Internet to enable us all to im...
Whether your IoT service is connecting cars, homes, appliances, wearable, cameras or other devices, one question hangs in the balance – how do you actually make money from this service? The ability to turn your IoT service into profit requires the ability to create a monetization strategy that is flexible, scalable and working for you in real-time. It must be a transparent, smoothly implemented strategy that all stakeholders – from customers to the board – will be able to understand and comprehe...
"Once customers get a year into their IoT deployments, they start to realize that they may have been shortsighted in the ways they built out their deployment and the key thing I see a lot of people looking at is - how can I take equipment data, pull it back in an IoT solution and show it in a dashboard," stated Dave McCarthy, Director of Products at Bsquare Corporation, in this SYS-CON.tv interview at @ThingsExpo, held November 1-3, 2016, at the Santa Clara Convention Center in Santa Clara, CA.
What happens when the different parts of a vehicle become smarter than the vehicle itself? As we move toward the era of smart everything, hundreds of entities in a vehicle that communicate with each other, the vehicle and external systems create a need for identity orchestration so that all entities work as a conglomerate. Much like an orchestra without a conductor, without the ability to secure, control, and connect the link between a vehicle’s head unit, devices, and systems and to manage the ...
Everyone knows that truly innovative companies learn as they go along, pushing boundaries in response to market changes and demands. What's more of a mystery is how to balance innovation on a fresh platform built from scratch with the legacy tech stack, product suite and customers that continue to serve as the business' foundation. In his General Session at 19th Cloud Expo, Michael Chambliss, Head of Engineering at ReadyTalk, discussed why and how ReadyTalk diverted from healthy revenue and mor...
As data explodes in quantity, importance and from new sources, the need for managing and protecting data residing across physical, virtual, and cloud environments grow with it. Managing data includes protecting it, indexing and classifying it for true, long-term management, compliance and E-Discovery. Commvault can ensure this with a single pane of glass solution – whether in a private cloud, a Service Provider delivered public cloud or a hybrid cloud environment – across the heterogeneous enter...
You have great SaaS business app ideas. You want to turn your idea quickly into a functional and engaging proof of concept. You need to be able to modify it to meet customers' needs, and you need to deliver a complete and secure SaaS application. How could you achieve all the above and yet avoid unforeseen IT requirements that add unnecessary cost and complexity? You also want your app to be responsive in any device at any time. In his session at 19th Cloud Expo, Mark Allen, General Manager of...
Financial Technology has become a topic of intense interest throughout the cloud developer and enterprise IT communities. Accordingly, attendees at the upcoming 20th Cloud Expo at the Javits Center in New York, June 6-8, 2017, will find fresh new content in a new track called FinTech.
The 20th International Cloud Expo has announced that its Call for Papers is open. Cloud Expo, to be held June 6-8, 2017, at the Javits Center in New York City, brings together Cloud Computing, Big Data, Internet of Things, DevOps, Containers, Microservices and WebRTC to one location. With cloud computing driving a higher percentage of enterprise IT budgets every year, it becomes increasingly important to plant your flag in this fast-expanding business opportunity. Submit your speaking proposal ...
Bert Loomis was a visionary. This general session will highlight how Bert Loomis and people like him inspire us to build great things with small inventions. In their general session at 19th Cloud Expo, Harold Hannon, Architect at IBM Bluemix, and Michael O'Neill, Strategic Business Development at Nvidia, discussed the accelerating pace of AI development and how IBM Cloud and NVIDIA are partnering to bring AI capabilities to "every day," on-demand. They also reviewed two "free infrastructure" pr...