Don't let your mocks lie to you!

Published: 2016-03-20 by Lars  guidetesting

This post introduces a new JavaScript testing library that works with Sinon.JS to vastly improve the speed of your integration tests and also improve the quality of some of your unit tests.

Liar

When we are writing code for some kind of client (could be a website), we usually want to test the layer of the code that interacts with the server. We could write those tests as integration tests, actually spinning up a server. But integration tests are slow and fragile, so we often prefer to stick to unit testing that layer. We typically do that by mocking the server interaction, using something like the Fake Server in Sinon.JS. Here is an example from the Sinon.JS documentation:

{
    setUp: function () {
        this.server = sinon.fakeServer.create();
    },

    tearDown: function () {
        this.server.restore();
    },

    "test should fetch comments from server" : function () {
        this.server.respondWith("GET", "/some/article/comments.json",
            [200, { "Content-Type": "application/json" },
             '[{ "id": 12, "comment": "Hey there" }]']);

        var callback = sinon.spy();
        myLib.getCommentsFor("/some/article", callback);
        this.server.respond();

        sinon.assert.calledWith(callback, [{ id: 12, comment: "Hey there" }]);
    }
}

Note how this server mocking is hand crafted: We have hard coded the specific URL as /some/article/comments.json and the specific response as [{id: 12, comment: 'Hey there'}]. That is a source of problems though. We have basically made a hard-coded assumption about how the server expects its request to look like and what its corresponding response will be. This assumption may change over time, causing the mocking code to grow stale if not plain wrong. At that point, the hand crafted mocking code is essentially lying to you: postulating a server request/response that is no longer truthful. I have written a more detailed blog post about this situation here: Unit test your service integration layer.

As that blog post suggested, we can do better. Assume that we already have a file containing a long list of valid request-response pairs. Then we can use that file to automatically mock the server interaction in the client tests. Using the new sinon-har-server library, the code sample above can be simplified to this:

{
    setUp: function () {
        this.server = sinon.fakeServer.create();
        sinonHarServer.load(this.server, harFile);
    },

    tearDown: function () {
        this.server.restore();
    },

    "test should fetch comments from server" : function () {
        var callback = sinon.spy();
        myLib.getCommentsFor("/some/article", callback);
        sinon.assert.calledWith(callback, [{ id: 12, comment: "Hey there" }]);
    }
}

The sinon-har-server module understands the standard Http Archive file format that is already used by browser debuggers such as Chrome and FireBug.

We can produce such a .har file by instrumenting our server side unit tests, those for the outermost layer. By capturing all the traffic to our server code during those unit tests into a .har file, we automatically get a list of guaranteed valid request/response pairs. If you happen to use Node.JS for your server, you can use the new request-har-capture module to do the recording. Similar instrumentation can be implemented for other back-end technologies, like C# or Java.

I have created a sample project, http-auto-mock-demo,that uses both of these two modules to demonstrate that we can now run around 50-100 integration tests per second and never have our mocks lie to us.

Enjoy!

Discuss on Twitter