A tour of the Chai assertion library

The Chai test library provides a robust set of assertions and helpers to aid the legibility and organization of tests.

Chai's own unit tests for expect (at https://github.com/chaijs/chai/blob/master/test/expect.js) provide a great starting point from which the API can be explored. Chai conveniently uses Mocha for its test framework (configured with the TDD interface). So the entire test suite should feel very familiar.

In this section, we will use a series of assertions to introduce most of the Chai BDD APIs. The assertion examples are accumulated into a single driver file for this chapter—chapters/03/test/test-chai.html.

Note

Chai provides aliases for many of the assertions that we will discuss in this section. For example, the deep equality assertion eql is also available as eqls and deep.equal. See the Chai API documentation for a full listing of aliases.

Chaining objects and assertions

Chai's BDD interface exposes objects that can be chained together to make test assertions more comprehensible. We will walk through some basic examples available in the file chapters/03/test/js/spec/chai-chains.spec.js.

As an introductory example, the assertion expect("foo").to.be.a("string") uses the chain objects to and be, which simply proxy through the eventual assertion. In this manner, Chai allows us to add any of the chain objects to, be, been, is, that, and, have, with, at, and of to form more naturally readable assertion statements. The statements a and an are available as both assertion chains and comparison functions.

We can use these statements to create language chain assertions such as:

expect("foo").a("string");
expect("foo").to.be.a("string");
expect("foo").to.have.been.a("string");
expect("foo").that.is.a("string");

// Chains can be repeated (or be nonsensical).
expect("foo").to.to.to.to.a("string");
expect("foo").and.with.at.of.a("string");

As all of the statements are equivalent, the highlighted language chains in the previous code demonstrate the many different ways of stating the same assertion.

Chai also provides language chain objects that do modify the ultimate assertion:

  • not: This negates any following assertions. For example:
    expect("foo").to.not.equal("bar");
    
    // Let's get literary.
    expect("Hamlet").to.be.not.to.be.an("object");
  • deep: This sets the deep flag for equality checks. A raw equality check performs an identity test, which asserts that the two variables are the same object in the process memory. With the deep flag, Chai instead asserts that the two variables have the same property values, even if they are different underlying objects. For example, expect({foo: "bar"}).to.equal({foo: "bar"}) fails an object identity test while expect({foo: "bar"}).to.deep.equal({foo: "bar"}) succeeds.

Finally, most other Chai BDD assertion statements are chainable. The following example groups several assertion chains together with the and helper:

expect("foo")
  .to.be.a("string").and
  .to.equal("foo").and
  .to.have.length(3).and
  .to.match(/f[o]{2}/);

With these basic language chains and helpers, we have a foundation for writing readable assertion statements.

Tip

It is a good practice to chain assertions together when it makes sense for the purpose of developer comprehension and writing concise and terse test code. At the same time, separate expect() statements can often be more appropriate.

Basic value assertions

Chai provides various assertion properties to check input values (see chapters/03/test/js/spec/chai-values.spec.js):

  • ok: Value is truthy (for a quick introduction to JavaScript's permissive notion of conditional truth and equality, see http://www.sitepoint.com/javascript-truthy-falsy/)
    expect("foo").to.be.ok;
    expect(true).to.be.ok;
    expect(false).to.not.be.ok;
  • exist: Value is neither null nor undefined
    expect(false).to.exist;
    expect(null).to.not.exist;
    expect(undefined).to.not.exist;
  • true: Value is exactly true
    expect("foo").to.not.be.true;
    expect(true).to.be.true;
  • false: Value is exactly false
    expect("").to.not.be.false;
    expect(false).to.be.false;
  • null: Value is exactly null.
    expect(null).to.be.null;
  • undefined: Value is exactly undefined
    expect(undefined).to.be.undefined;
    expect(null).to.not.be.undefined;
  • arguments: Value is the special JavaScript arguments object, which contains a list of parameters for the current function
    expect(arguments).to.be.arguments;
    expect([]).to.not.be.arguments;

Comparing values

Chai has a diverse array of comparison functions to assess input values (see chapters/03/test/js/spec/chai-comparisons.spec.js):

  • equal: Strict (===) equality
    expect("foo").to.equal("foo");
    expect({foo: "bar"}).to.not.equal({foo: "bar"});
  • eql: Deep equality—equivalent to deep.equal
    expect("foo").to.eql("foo");
    expect({foo: "bar"}).to.eql({foo: "bar"});
  • above: The actual value is greater than the expected value
    expect(1).to.not.be.above(1);
    expect(5).to.be.above(2);
  • least: The actual value is greater than or equal to the expected value
    expect(1).to.be.at.least(1);
    expect(5).to.be.at.least(2);
  • below: The actual value is less than the expected value
    expect(1).to.not.be.below(1);
    expect(1).to.be.below(2);
  • most: The actual value is less than or equal to the expected value
    expect(1).to.be.at.most(1);
    expect(1).to.be.at.most(2);
  • within: The actual value is within the range of the expected values
    expect(1).to.be.within(0, 2);
  • closeTo: The actual value is within the delta of the expected value
    expect(1.2).to.be.closeTo(1, 0.2);
    expect(1.2).to.not.be.closeTo(1, 0.0);
  • match: The actual string value is matched by the expected regular expression
    expect("foo").to.match(/^f[o]+/);
  • string: The actual string value contains the expected substring
    expect("foo bar").to.have.string("foo");
  • satisfy: The evaluator function takes the actual value as a parameter and returns true if the assertion should pass
    expect(42).to.satisfy(function (value) {
      return value === 6 * 7;
    });

Object and array validation

Chai provides some useful assertions tailored to objects and arrays (see chapters/03/test/js/spec/chai-objects.spec.js):

  • a: When called as a function, this checks the object type building on JavaScript's native typeof test, with additional support for correctly inferring objects and arrays. Note that when a (or an) is used as an object property, it acts as a language chain instead.
    expect("foo").is.a("string");
    expect("foo").is.not.a("number");
    expect({foo: "bar"}).is.an("object");
  • instanceof: Checks whether the object is an instance of an expected constructor.
    var Foo = function () {},
      Bar = function () {};
    
    expect(new Foo()).is.an.instanceof(Foo);
    expect(new Bar()).is.not.an.instanceof(Foo);
  • property: Checks if an expected property exists in an object and, optionally, if the property's value matches an expected value. When used in conjunction with the deep language chain, an object structure can be navigated via the dot or array notation.
    expect({foo: "bar"}).to.have.property("foo", "bar");
    
    // Deep checking - object, and array.
    expect({foo: {bar: "baz"}})
      .to.have.deep.property("foo.bar", "baz");
    expect({foo: ["bar", "baz"]})
      .to.have.deep.property("foo[1]", "baz");
  • ownProperty: Checks for the presence of a direct property on an object using the JavaScript hasOwnProperty test, without looking up the object's prototype chain for an inherited property.
    expect({foo: "bar"}).to.have.ownProperty("foo");
  • length: Checks the length property of an array or object (such as a string).
    expect(["bar", "baz"]).to.have.length(2);
    expect("foo").to.have.length(3);
  • contain: Checks for the presence of an object in an array or a substring within a string. Note that contain (and include) can alternatively be used as a language chain with keys.
    expect(["bar", "baz"]).to.contain("bar");
    expect("foo").to.contain("f");
  • keys: Checks that an object includes all of the expected property names. The assertion verifies only a subset of expected property names when combined with the include or contain language chains.
    // Exact matching of all keys.
    expect({foo: 1, bar: 2}).to.have.keys(["foo", "bar"]);
    
    // Exclusion of any keys.
    expect({foo: 1, bar: 2}).to.not.have.keys(["baz"]);
    
    // Inclusion of some keys.
    expect({foo: 1, bar: 2}).to.include.keys(["foo"]);
    expect({foo: 1, bar: 2}).to.contain.keys(["bar"]);

Errors

Chai can also check abnormal code functionality, notably trapping and verifying program exceptions.

The throw assertion takes a function as an input that is expected to throw an exception when called. The resulting error is then matched against a constructor class (for example, Error) or a message string/regular expression (for example, /message/). Note that a function reference (for example, bad) is passed to the assertion rather than a called function (for example, bad()). This enables Chai to call the function internally, trap any exceptions, and verify the results:

var bad = function () {
  throw new Error("My error message");
};

expect(bad)
  .to.throw(Error).and
  .to.throw(/message/).and
  .not.to.throw("no message match");
..................Content has been hidden....................

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