Spicing up Chai with the Sinon.JS plugin

One of the primary motivations for using the Chai library is the natural language syntax of its chained assertions. Another strong point of Chai is that it produces clear error messages on assertion failures.

Unfortunately, Sinon.JS spies create some assertion challenges within our test framework. To illustrate the issue, let's focus on the previous example that asserts that the obj.multiply() method (wrapped in a spy) was called with the parameters 5 and 2.

At this point, we have encountered two ways of making assertions on Sinon.JS spies—with Chai assertions and with Sinon.JS built-in spy assertions. Starting with the first method, we can write a Chai assertion on the spy as follows:

expect(obj.multiply.calledWith(5, 2)).to.be.true;

However, a drawback of this statement is that if the assertion fails, Chai will produce the unhelpful error message expected false to be true.

We can get a much better error message, AssertError: expected multiply to be called with arguments 5, 2, if we use the Sinon.JS assert version:

sinon.assert.calledWith(obj.multiply, 5, 2);

But we then lose the naturally readable Chai dot-notation syntax.

What we really want is a code that fails with an error message resembling expected multiply to have been called with arguments 5, 2 and an assertion that reads as follows:

expect(obj.multiply).to.be.calledWith(5, 2);

Fortunately, we can get the best of both the worlds—readable Chai assertions with informative library-specific failure messages—by using Chai's plugin capabilities.

Introducing and installing Chai plugins

Chai supports plugins (http://chaijs.com/plugins) that modify and extend the Chai assertion API with contextually useful changes and failure messages. In this section, we will introduce and install the Sinon.JS adapter for Chai, giving us much more concise and ultimately useful assertions for our test doubles.

Note

The use of the Sinon.JS adapter is recommended but entirely optional. Although we will use the plugin in many examples throughout the rest of the book, all of our test assertions could be rewritten in equivalent statements using native Chai.

The Sinon-Chai plugin (http://chaijs.com/plugins/sinon-chai) can be downloaded from GitHub at https://raw.github.com/domenic/sinon-chai/2.4.0/lib/sinon-chai.js. At present we are using version 2.4.0. The file should be placed in the same directory (test/js/lib/) as our other test libraries and included along with the other libraries in the test driver web page:

<!-- JavaScript Test Libraries. -->
<script src="js/lib/mocha.js"></script>
<script src="js/lib/chai.js"></script>
<script src="js/lib/sinon-chai.js"></script>
<script src="js/lib/sinon.js"></script>

Sinon-Chai must be included after Chai and can be included before or after the Sinon.JS library. With this extra include, we are ready to start writing more readable and informative Chai assertions.

Tip

Other Chai plugins that may also be useful for Backbone.js application tests include the adapters for Backbone.js (http://chaijs.com/plugins/chai-backbone) and jQuery (http://chaijs.com/plugins/chai-jquery). The Backbone.js plugin adds assertions for Backbone.js-specific constructs such as trigger (for events) and routes.to (for routing). The jQuery plugin proxies various jQuery functions into Chai assertions, enabling statements such as expect($text).to.have.html("<em>Edit your note!</em>").

The Sinon.JS plugin

The Sinon-Chai plugin extends Chai with several spy-related assertions, including the following:

  • Call occurrences: expect(spy).to.have.been.called, expect(spy).to.have.been.calledOnce, expect(spy).to.have.been.calledTwice, and expect(spy).to.have.been.calledThrice
  • Call order: expect(spy1).to.have.been.calledAfter(spy2) and expect(spy1).to.have.been.calledBefore(spy2)
  • Call arguments: expect(spy).to.have.been.calledWithNew, expect(spy).to.have.been.calledOn(context), expect(spy).to.have.been.calledWith(arg1, arg2, ...), expect(spy).to.have.been.calledWithExactly(arg1, arg2, ...), and expect(spy).to.have.been.calledWithMatch(arg1, arg2, ...)
  • Return values: expect(spy).to.have.returned(returnVal)
  • Errors: expect(spy).to.have.thrown()

The plugin also adds a new assertion flag:

  • always: It signals that the spied function must pass the assertion for every function call and not just one or more function call. For example, we can convert any of the following assertions that check any function call into ones that check every call:
    expect(spy).to.always.have.been.calledWith(arg1, arg2, ...);
    expect(spy).to.have.always.returned(returnVal);
    expect(spy).to.have.always.thrown();

Now, we can rewrite one of our earlier test examples with Sinon-Chai assertions to read as follows:

it("calls spy with chai plugin", sinon.test(function () {
  this.spy(obj, "multiply");
  this.spy(obj, "error");

  expect(obj.multiply(5, 2)).to.equal(10);
  expect(obj.multiply).to.have.been.calledWith(5, 2);
  expect(obj.multiply).to.have.returned(10);

  try { obj.error("Foo"); } catch (e) {}
  expect(obj.error).to.have.thrown("Error");
}));

Any spy assertion failures in the previous refactored test will produce informative messages such as expected multiply to have been called with arguments 5, 2. Thus, the Sinon-Chai plugin allows us to keep the spy assertions in Chai's chained dot-notation format while also producing helpful failure messages.

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

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