Introducing Leche: A JavaScript Testing Utility for Mocha and Sinon

Over the past year, we’ve been steadily working to increase our JavaScript tooling at Box. Our codebase grew from a single PHP application with client-side JavaScript to include two Node.js web applications. With that change came a desire to standardize the way we write JavaScript tests so that the same tools and patterns could be used for both client-side and server-side JavaScript.

As shared in an earlier post, this process involved us eventually deciding to replace our existing QUnit-based testing stack with a Mocha-based one. We are big users of Sinon for mocking and decided to continue using it due to its excellent compatibility with browsers and Node.js. While we had been happy with the tooling change, we quickly came across functionality gaps that led to our writing a lot of boilerplate code. The result of filling those gaps is Leche, a utility designed for use with Mocha and Sinon.

Data Providers in Mocha

The first issue we came across was the desire for data providers in Mocha. Put simply, data providers allow you to run the same test over a collection of data. We use data providers frequently at Box, both in PHPUnit using its built-in data providers, and in Python, using Genty. We wanted the same thing in JavaScript and were unable to find an existing solution that satisfied our requirements. So we did what any good engineers would do: we created one.

The leche.withData() provider for Mocha is designed to be as flexible as possible while allowing you to continue using everything that Mocha offers, including nesting and asynchronous tests. We went through the trouble to ensure that failure messages point to the data that caused the failure as well as allow you to manually specify a label for the datasets you use. Here’s a simple example:

describe('MyObject', function() {

    leche.withData({
        label1: [1, 2],
        label2: [3, 4]
    }, function(first, second) {

        it('should say the values are equal when they are passed', function() {
            assert.equal(first, second);
        });
    });
});

Mock Creation

The next gap we encountered was with the creation of mocks. Sinon does a great job of mocking out individual methods on objects, allowing you to set expectations about the function arguments and return value. It does this by overwriting just one method at a time, which means that any methods you don’t explicitly mock or stub will be left with their regular functionality in place. We ran into several circumstances where unexpected calls to methods were made and caused invalid test results.

To fill this gap we wrote leche.fake(),which creates an object that mimics the interface of a given object. The newly-created object uses prototypal inheritance so instanceof checks continue to work as expected and every existing method on the original object is implemented to throw an error. You can then mock or stub any method on the fake object knowing that any unexpected function calls will throw an error. For example:

function Person() {}
Person.prototype.sayName = function() {
    console.log('Box');
};
Person.prototype.sayHi = function() {
    console.log('Hi');
};

// create fake object
var fakePersonMock = leche.fake(Person.prototype);
sinon.mock(fakePersonMock).expects('sayName');

fakePersonMock.sayName();    // no error - Sinon has mocked it out
fakePersonMock.sayHi();      // throws an error

After we started using fake objects, we found our unit tests were a lot easier to manage and problems were a lot easier to track down.

Today we are open sourcing Leche to share the work we’ve done with the rest of the world. It’s not the biggest library, nor is it the most powerful, but Leche does fill in a few of the gaps we found in Mocha and Sinon. Over the past six months, Leche has become an important part of our JavaScript tooling and we hope you find it as useful as we do.

You can install Leche using npm by typing npm i leche --save-dev and start using it today: https://github.com/box/leche.

BoxOpensource_logo_cyan+navy