Specifying Test Double Behavior

Learn how to specify and add the behavior for test doubles. Learn test double instance methods, and how to use them with multiple matchers.

Once we have a testdouble.js function, we need to specify some behavior, analogous to how RSpec’s test doubles use allow and expect with chained methods like to_receive.

Adding behavior

In testdouble.js, we add behavior with the when method, and the API to it is a little unusual. Here’s a basic usage:

const fake = td.object["remoteInit"]
td.when(fake.remoteInit(1)).thenReturn(7)

There are two parts to this when invocation:

  • The call to when itself.
  • The chained method afterward that defines the behavior.

The argument to when is meant to be the entire call to the test double, potentially with arguments specified. The call demonstrates that the test expects to be made by the code under test.

In this case, we expect the test to make the call fake.remoteInit(1) to cause the test double to return the value 7. If we make the call fake.remoteInit(2), changing the argument, the specified behavior is not triggered, and the result of that call is undefined.

This interface for creating test-double behavior has the huge advantage of making the most common action, returning a value when a function is called, very simple. The library does get more complicated from there.

Test doubles instance method

First, if we want to access an instance method of a test double created with td.constructor, the nature of JavaScript objects requires us to invoke that method via the prototype object, as in td.when(FakeLoader.prototype.load()). As in RSpec, we can pass in argument matchers in place of literal arguments if we want the test double to match against a wider array of behavior.

Note: We can see the full list of stubbing behavior here.

Here are some of the most useful ones:

  • The anything behavior: As in td.when(fake.remoteInit(anything())), which matches anything. We do have to match the number of arguments, so in this case fake.remoteInit(12) matches, but fake.remoteInit(12, 13) does not.

  • The contains(<something>) behavior: This matches if the argument contains the argument to the eventual test double to contains. The argument can be a string, a regular expression, an array, or an object.

  • The isA() behavior: This matches based on type: td.when(fake.remoteInit(isA(Number))). This works for built-in types, ES6 classes, or older-style objects that define constructors.

  • The not() behavior: This matches if a particular value doesn’t match, as in td.when(fake.remoteInit (not(12))).

Multiple matchers

If we specify multiple potential matches for a value, the last one defined wins. If we define the following:

Get hands-on with 1400+ tech skills courses.