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.
We'll cover the following
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 intd.when(fake.remoteInit(anything()))
, which matches anything. We do have to match the number of arguments, so in this casefake.remoteInit(12)
matches, butfake.remoteInit(12, 13)
does not. -
The
contains(<something>)
behavior: This matches if the argument contains the argument to the eventual test double tocontains
. 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 intd.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.