Introducing mocks

As long as the code we're testing remains relatively simple, Mocha and Expect.js are all that we need to test it. However, the code rarely remains simple, and there are two complications in particular that can make us need another tool, known as a mocking library, to let us create fake versions or mocks of our objects.

First, let's imagine we want to test the following method of an imaginary ExampleModel class:

foo: function() {
    this.bar += 1;
    this.baz();
}

Now, we'll want to test whether our foo method calls the baz method. However, at the same time, we will (presumably) already have a separate test of the baz method itself. This leaves us with a dilemma: how can we test that foo calls baz without repeating the test code for baz inside the test code for foo?

Alternatively, let's consider another imaginary method that we might want to test:

fetchThenDoFoo: function() {
    this.fetch().done(this.foo);
}

In this case, we want to test whether foo is called after the fetch operation completes, but to truly test this, we'd need to actually fetch a Model class from the server by using AJAX. This, in turn, would make our tests require an active server, making them significantly slower and opening up the possibility of the tests causing side effects on our server.

The solution to both of these problems is to use a mocking library such as Sinon.js. To do this, simply download sinon.js from http://sinonjs.org/ and then include this file (via a script tag) on your test running the HTML page.

Once Sinon is available, we can use it to solve our testing problems. First, let's use it to create a special kind of mock called stub for a test of our foo method as follows:

describe('foo', function() {
    var bazStub,
        example;
    beforeEach(function() {
        example = new ExampleModel();
        // Replace the real "baz" with a fake one that does nothing
        bazStub = sinon.stub(example, 'baz'),
    });
    it('calls baz', function() {
        example.foo();
        expect(bazStub.calledOnce).to.be(true); // did foo call baz?
    });
    afterEach(function() {
        // Restore the original baz (in case another test uses it)
        baz.restore();
    });
});

As you can see in the preceding code, we were able to use Sinon to create stub that replaced the normal baz method. While this stub didn't actually do anything, it did keep track of how many times it was called (as well as which arguments were used, although we didn't test this), which let us write a test that ensured that foo would call baz without having to repeat any of the test code for baz.

For our second problem, that of testing an AJAX method, we could use an AJAX-specific mock tool such as MockJax. However, Sinon is so powerful that we really don't need to use anything else; consider the following test:

describe('fetchThenDoFoo', function() {

    var fetchStub,
        fooStub,
        example;
    beforeEach(function() {
        example = new ExampleModel();
        // Replace the real "fetch" with a fake one that returns an
        // already-resolved $.Deferred
        var deferred = new $.Deferred().resolve();
        fetchStub = sinon.stub(example, 'fetch').returns(deferred);
        // Since we only want to test whether or not foo was called,
        // we can also use stub for it
        fooStub = sinon.stub(example, 'foo'),
    });
    it('calls foo after fetch completes', function() {
        example.fetchThenDoFoo();
        expect(fooStub.calledOnce).to.be(true);
    });
    afterEach(function() {
        // Restore the original versions of our stub functions
        fetchStub.restore();
        fooStub.restore();
    });
});

In this example, we used two stub functions. We used the fooStub function in a manner similar to how we used the bazstub function in the previous example, to check whether or not foo got called, but our fetchStub served a different purpose. By chaining a returns method call off of our stub's creation, we created a stub function that (unlike the previous stub functions) actually did something: it returned our resolved deferred function. Since jQuery treats a resolved deferred function the same way as it treats a completed AJAX call (by invoking any done code for the call), we simulated the return of an AJAX call without involving an actual server.

Sinon has many other useful methods related to stub, as well as other types of mocking functions such as spy and mock, all of which are very well documented on their website. Sinon also has a feature known as sandbox, which can allow you to eliminate all of the baz.restore() code by automatically activating it after every test run (when sandbox is cleaned up). Again, you can find all the details of this feature on Sinon's website.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.53.119