1 | # testdouble.js (AKA td.js)
|
2 |
|
3 | [![Build Status](https://circleci.com/gh/testdouble/testdouble.js/tree/master.svg?style=svg)](https://circleci.com/gh/testdouble/testdouble.js/tree/master)
|
4 | [![npmjs](https://img.shields.io/badge/npm-testdouble-red.svg)](https://www.npmjs.com/package/testdouble)
|
5 | [![unpkg](https://img.shields.io/badge/unpkg-download-blue.svg)](https://unpkg.com/testdouble/dist/)
|
6 | [![Test Coverage](https://codeclimate.com/github/testdouble/testdouble.js/badges/coverage.svg)](https://codeclimate.com/github/testdouble/testdouble.js/coverage)
|
7 |
|
8 | Welcome! Are you writing JavaScript tests and in the market for a mocking
|
9 | library to fake out real things for you? testdouble.js is an opinionated,
|
10 | carefully-designed test double library maintained by, oddly enough, a software
|
11 | agency that's also named [Test Double](http://testdouble.com).
|
12 |
|
13 | If you practice test-driven development, testdouble.js was designed to promote
|
14 | terse, clear, and easy-to-understand tests. There's an awful lot to cover, so
|
15 | please take some time and enjoy our documentation, which is designed to show you
|
16 | how to make the most out of test doubles in your tests.
|
17 |
|
18 | This library was designed to work for both Node.js and browser interpeters. It's
|
19 | also test-framework agnostic, so you can plop it into a codebase using Jasmine,
|
20 | Mocha, Tape, Jest, or our own
|
21 | [teenytest](https://github.com/testdouble/teenytest).
|
22 |
|
23 | ## Install
|
24 |
|
25 | ```
|
26 | $ npm install -D testdouble
|
27 | ```
|
28 |
|
29 | If you just want to fetch the browser distribution, you can also curl it from
|
30 | [unpkg](https://unpkg.com/testdouble/dist/).
|
31 |
|
32 | We recommend requiring the library in a test helper and setting it globally for
|
33 | convenience to the shorthand `td`:
|
34 |
|
35 | ```js
|
36 | global.td = require('testdouble') // Node.js; `window.td` for browsers
|
37 | ```
|
38 |
|
39 | (You may need to configure your linter to ignore the `td` global.
|
40 | Instructions:
|
41 | [eslint](https://eslint.org/docs/user-guide/configuring#specifying-globals),
|
42 | [standard](https://github.com/standard/standard/#i-use-a-library-that-pollutes-the-global-namespace-how-do-i-prevent-variable-is-not-defined-errors).)
|
43 |
|
44 | If you're using testdouble.js in conjunction with another test framework, you
|
45 | may also want to check out one of these extensions:
|
46 |
|
47 | * [testdouble-jest](https://github.com/testdouble/testdouble-jest)
|
48 | * [testdouble-chai](https://github.com/basecase/testdouble-chai)
|
49 | * [testdouble-jasmine](https://github.com/BrianGenisio/testdouble-jasmine)
|
50 | * [testdouble-qunit](https://github.com/alexlafroscia/testdouble-qunit/tree/master/packages/testdouble-qunit)
|
51 |
|
52 | ## Getting started
|
53 |
|
54 | Mocking libraries are more often abused than used effectively, so figuring out
|
55 | how to document a mocking library so as to only encourage healthy uses has
|
56 | proven to be a real challenge. Here are a few paths we've prepared for getting
|
57 | started with testdouble.js:
|
58 |
|
59 | * The [API section of this README](#api) so you can get started stubbing and
|
60 | verifying right away
|
61 | * A [20-minute
|
62 | video](http://blog.testdouble.com/posts/2016-06-05-happier-tdd-with-testdouble-js)
|
63 | overview of the library, its goals, and basic usage
|
64 | * A [comparison between testdouble.js and
|
65 | Sinon.js](http://blog.testdouble.com/posts/2016-03-13-testdouble-vs-sinon.html),
|
66 | in case you've already got experience working with Sinon and you're looking
|
67 | for a high-level overview of how they differ
|
68 | * The full testdouble.js [documentation](/docs), which describes at length how
|
69 | to (and how not to) take advantage of the various features of testdouble.js.
|
70 | Its outline is in [docs/README.md](/docs#readme)
|
71 |
|
72 | Of course, if you're unsure of how to approach writing an isolated test with
|
73 | testdouble.js, we welcome you to [open an issue on GitHub to ask a
|
74 | question](https://github.com/testdouble/testdouble.js/issues/new).
|
75 |
|
76 | ## API
|
77 |
|
78 | ### `td.replace()` and `td.replaceEsm()` for replacing dependencies
|
79 |
|
80 | The first thing a test double library needs to do is give you a way to replace
|
81 | the production dependencies of your [subject under
|
82 | test](https://github.com/testdouble/contributing-tests/wiki/Subject) with fake
|
83 | ones controlled by your test.
|
84 |
|
85 | We provide a top-level function called `td.replace()` that operates in two
|
86 | different modes: CommonJS module replacement and object-property replacement.
|
87 | Both modes will, by default, perform a deep clone of the real dependency which
|
88 | replaces all functions it encounters with fake test double functions which can,
|
89 | in turn, be configured by your test to either stub responses or assert
|
90 | invocations.
|
91 |
|
92 | Additionally, if you're using Node 13 or newer, you can specify testdouble as a
|
93 | module loader and replace native ES modules with `td.replaceEsm()`. More details
|
94 | [here](docs/7-replacing-dependencies.md#how-module-replacement-works-for-es-modules-using-import).
|
95 |
|
96 | #### Module replacement with Node.js
|
97 |
|
98 | **`td.replace('../path/to/module'[, customReplacement])`**
|
99 |
|
100 | If you're using Node.js and don't mind using the CommonJS `require()` function
|
101 | in your tests (you can still use `import`/`export` in your production code,
|
102 | assuming you're compiling it down for consumption by your tests), testdouble.js
|
103 | uses a library we wrote called [quibble](https://github.com/testdouble/quibble)
|
104 | to monkey-patch `require()` so that your subject will automatically receive your
|
105 | faked dependencies simply by requiring them. This approach may be familiar if you've used something like
|
106 | [proxyquire](https://github.com/thlorenz/proxyquire), but our focus was to
|
107 | enable an even more minimal test setup.
|
108 |
|
109 | Here's an example of using `td.replace()` in a Node.js test's setup:
|
110 |
|
111 | ```js
|
112 | let loadsPurchases, generatesInvoice, sendsInvoice, subject
|
113 | module.exports = {
|
114 | beforeEach: () => {
|
115 | loadsPurchases = td.replace('../src/loads-purchases')
|
116 | generatesInvoice = td.replace('../src/generates-invoice')
|
117 | sendsInvoice = td.replace('../src/sends-invoice')
|
118 | subject = require('../src/index')
|
119 | }
|
120 | //…
|
121 | afterEach: function () { td.reset() }
|
122 | }
|
123 | ```
|
124 |
|
125 | In the above example, at the point when `src/index` is required, the module
|
126 | cache will be bypassed as `index` is loaded. If `index` goes on to subsequently
|
127 | require any of the `td.replace()`'d dependencies, it will receive a reference to
|
128 | the same fake dependencies that were returned to the test.
|
129 |
|
130 | Because `td.replace()` first loads the actual file, it will do its best to
|
131 | return a fake that is shaped just like the real thing. That means that if
|
132 | `loads-purchases` exports a function, a test double function will be created and
|
133 | returned. If `generates-invoice` exports a constructor, a constructor test
|
134 | double will be returned, complete with test doubles for all of the original's
|
135 | static functions and instance methods. If `sends-invoice` exports a plain
|
136 | object of function properties, an object will be returned with test double
|
137 | functions in place of the originals' function properties. In every case, any
|
138 | non-function properties will be deep-cloned.
|
139 |
|
140 | There are a few important things to keep in mind about replacing Node.js modules
|
141 | using `td.replace()`:
|
142 |
|
143 | * The test must `td.replace()` and `require()` everything in a before-each hook,
|
144 | in order to bypass the Node.js module cache and to avoid pollution between
|
145 | tests
|
146 | * Any relative paths passed to `td.replace()` are relative *from the test to the
|
147 | dependency*. This runs counter to how some other tools do it, but we feel it
|
148 | makes more sense
|
149 | * The test suite (usually in a global after-each hook) must call `td.reset()` to
|
150 | ensure the real `require()` function and dependency modules are restored after
|
151 | each test case.
|
152 |
|
153 | ##### Default exports with ES modules
|
154 |
|
155 | If your modules are written in the ES module syntax and they specify default
|
156 | exports (e.g. `export default function loadsPurchases()`), but are actually
|
157 | transpiled to CommonJS, just remember that you'll need to reference `.default`
|
158 | when translating to the CJS module format.
|
159 |
|
160 | That means instead of this:
|
161 |
|
162 | ```js
|
163 | loadsPurchases = td.replace('../src/loads-purchases')
|
164 | ```
|
165 |
|
166 | You probably want to assign the fake like this:
|
167 |
|
168 | ```js
|
169 | loadsPurchases = td.replace('../src/loads-purchases').default
|
170 | ```
|
171 |
|
172 | #### Property replacement
|
173 |
|
174 | **`td.replace(containingObject, nameOfPropertyToReplace[, customReplacement])`**
|
175 |
|
176 | If you're running tests outside Node.js or otherwise injecting dependencies
|
177 | manually (or with a DI tool like
|
178 | [dependable](https://github.com/testdouble/dependable)), then you may still use
|
179 | `td.replace` to automatically replace things if they're referenceable as
|
180 | properties on an object.
|
181 |
|
182 | To illustrate, suppose our subject depends on `app.signup` below:
|
183 |
|
184 | ``` js
|
185 | app.signup = {
|
186 | onSubmit: function () {},
|
187 | onCancel: function () {}
|
188 | }
|
189 | ```
|
190 |
|
191 | If our goal is to replace `app.signup` during a test of `app.user.create()`,
|
192 | our test setup might look like this:
|
193 |
|
194 | ```js
|
195 | let signup, subject
|
196 | module.exports = {
|
197 | beforeEach: function () {
|
198 | signup = td.replace(app, 'signup')
|
199 | subject = app.user
|
200 | }
|
201 | // …
|
202 | afterEach: function () { td.reset() }
|
203 | }
|
204 | ```
|
205 |
|
206 | `td.replace()` will always return the newly-created fake imitation, even though
|
207 | in this case it's obviously still referenceable by the test and subject alike
|
208 | with `app.signup`. If we had wanted to only replace the `onCancel` function for
|
209 | whatever reason (though in this case, that would smell like a [partial
|
210 | mock](https://github.com/testdouble/contributing-tests/wiki/Partial-Mock)), we
|
211 | could have called `td.replace(app.signup, 'onCancel')`, instead.
|
212 |
|
213 | Remember to call `td.reset()` in an after-each hook (preferably globally so one
|
214 | doesn't have to remember to do so in each and every test) so that testdouble.js
|
215 | can replace the original. This is crucial to avoiding hard-to-debug test
|
216 | pollution!
|
217 |
|
218 | #### Specifying a custom replacement
|
219 |
|
220 | The library's [imitation
|
221 | feature](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
|
222 | is pretty sophisticated, but it's not perfect. It's also going to be pretty slow
|
223 | on large, complex objects. If you'd like to specify exactly what to replace a
|
224 | real dependency with, you can do so in either of the above modes by providing a
|
225 | final optional argument.
|
226 |
|
227 | When replacing a Node.js module:
|
228 |
|
229 | ```js
|
230 | generatesInvoice = td.replace('../generates-invoice', {
|
231 | generate: td.func('a generate function'),
|
232 | name: 'fake invoices'
|
233 | })
|
234 | ```
|
235 |
|
236 | When replacing a property:
|
237 |
|
238 | ```js
|
239 | signup = td.replace(app, 'signup', {
|
240 | onSubmit: td.func('fake submit handler'),
|
241 | onCancel: function () { throw Error('do not call me') }
|
242 | })
|
243 | ```
|
244 |
|
245 | ### `td.func()`, `td.object()`, `td.constructor()`, `td.instance()` and `td.imitate()` to create test doubles
|
246 |
|
247 | `td.replace()`'s imitation and injection convenience is great when your
|
248 | project's build configuration allows for it, but in many cases you'll want or
|
249 | need the control to create fake things directly. Each creation function can
|
250 | either imitate a real thing or be specified by passing a bit of configuration.
|
251 |
|
252 | Each test double creation function is very flexible and can take a variety of
|
253 | inputs. What gets returned generally depends on the number and type of configuration
|
254 | parameters passed in, so we'll highlight each supported usage separately with an
|
255 | example invocation:
|
256 |
|
257 | #### `td.func()`
|
258 |
|
259 | The `td.func()` function (also available as `td.function()`) returns a test
|
260 | double function and can be called in three modes:
|
261 |
|
262 | * **`td.func(someRealFunction)`** - returns a test double function of the same
|
263 | `name`, including a deep
|
264 | [imitation](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
|
265 | of all of its custom properties
|
266 | * **`td.func()`** - returns an anonymous test double function that can be used
|
267 | for stubbing and verifying any calls against it, but whose error messages and
|
268 | debugging output won't have a name to trace back to it
|
269 | * **`td.func('some name')`** - returns a test double function named `'some
|
270 | name'`, which will appear in any error messages as well as the debug info
|
271 | returned by passing the returned test double into
|
272 | [td.explain()](/docs/9-debugging.md#tdexplainsometestdouble)
|
273 | * **`td.func<Type>()`** - returns a test double function imitating the passed type.
|
274 | Examples and more details can be found in [using with TypeScript](/docs/10-using-with-typescript.md)
|
275 |
|
276 | #### `td.object()`
|
277 |
|
278 | The `td.object()` function returns an object containing test double functions,
|
279 | and supports three types of invocations:
|
280 |
|
281 | * **`td.object(realObject)`** - returns a deep
|
282 | [imitation](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
|
283 | of the passed object, where each function is replaced with a test double function
|
284 | named for the property path (e.g. If `realObject.invoices.send()` was a
|
285 | function, the returned object would have property `invoices.send` set to a
|
286 | test double named `'.invoices.send'`)
|
287 | * **`td.object(['add', 'subtract'])`** - returns a plain JavaScript object
|
288 | containing two properties `add` and `subtract` that are both assigned to test
|
289 | double functions named `'.add'` and `'.subtract'`, respectively
|
290 | * **`td.object('a Person'[, {excludeMethods: ['then']})`** - when passed with no
|
291 | args or with a string name as the first argument, returns an [ES
|
292 | Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
|
293 | The proxy will automatically intercept any call made to it and shunt in a test
|
294 | double that can be used for stubbing or verification. More details can be
|
295 | found in [our full docs](/docs/4-creating-test-doubles.md#objectobjectname)
|
296 | * **`td.object<Interface>()`** - returns an object with methods exposed as test doubles
|
297 | that are typed according to the passed interface. Examples and more details can be found in
|
298 | [using with TypeScript](/docs/10-using-with-typescript.md)
|
299 |
|
300 | #### `td.constructor()`
|
301 |
|
302 | If your code depends on ES classes or functions intended to be called with
|
303 | `new`, then the `td.constructor()` function can replace those dependencies as
|
304 | well.
|
305 |
|
306 | * **`td.constructor(RealConstructor)`** - returns a constructor whose calls can
|
307 | be verified and whose static and `prototype` functions have all been replaced
|
308 | with test double functions using the same
|
309 | [imitation](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
|
310 | mechanism as `td.func(realFunction)` and `td.object(realObject)`
|
311 | * **`td.constructor(['select', 'save'])`** - returns a constructor with `select`
|
312 | and `save` properties on its `prototype` object set to test double functions
|
313 | named `'#select'` and `'#save'`, respectively
|
314 |
|
315 | When replacing a constructor, typically the test will configure stubbing &
|
316 | verification by directly addressing its prototype functions. To illustrate, that
|
317 | means in your test you might write:
|
318 |
|
319 | ```js
|
320 | const FakeConstructor = td.constructor(RealConstructor)
|
321 | td.when(FakeConstructor.prototype.doStuff()).thenReturn('ok')
|
322 |
|
323 | subject(FakeConstructor)
|
324 | ```
|
325 |
|
326 | So that in your production code you can:
|
327 |
|
328 | ```js
|
329 | const subject = function (SomeConstructor) {
|
330 | const thing = new SomeConstructor()
|
331 | return thing.doStuff() // returns "ok"
|
332 | }
|
333 | ```
|
334 |
|
335 | #### `td.instance()`
|
336 |
|
337 | As a shorthand convenience, `td.instance()` function will call
|
338 | `td.constructor()` and return a `new` instance of the fake constructor function
|
339 | it returns.
|
340 |
|
341 | The following code snippets are functionally equivalent:
|
342 |
|
343 | ```js
|
344 | const fakeObject = td.instance(RealConstructor)
|
345 | ```
|
346 |
|
347 | ```js
|
348 | const FakeConstructor = td.constructor(RealConstructor)
|
349 | const fakeObject = new FakeConstructor()
|
350 | ```
|
351 |
|
352 | #### `td.imitate()`
|
353 |
|
354 | **`td.imitate(realThing[, name])`**
|
355 |
|
356 | If you know you want to imitate something, but don't know (or care) whether it's
|
357 | a function, object, or constructor, you can also just pass it to `td.imitate()`
|
358 | with an optional name parameter.
|
359 |
|
360 | ### `td.when()` for stubbing responses
|
361 |
|
362 | **`td.when(__rehearsal__[, options])`**
|
363 |
|
364 | Once you have your subject's dependencies replaced with test double functions,
|
365 | you'll want to be able to stub return values (and other sorts of responses)
|
366 | when the subject invokes the test double in the way that the test expects.
|
367 |
|
368 | To make stubbing configuration easy to read and grep, `td.when()`'s first
|
369 | argument isn't an argument at all, but rather a placeholder to demonstrate the
|
370 | way you're expecting the test double to be invoked by the subject, like so:
|
371 |
|
372 | ```js
|
373 | const increment = td.func()
|
374 | td.when(increment(5)).thenReturn(6)
|
375 | ```
|
376 |
|
377 | We would say that `increment(5)` is "rehearsing the invocation". Note that by
|
378 | default, a stubbing is only satisfied when the subject calls the test double
|
379 | exactly as it was rehearsed. This can be customized with [argument
|
380 | matchers](/docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers),
|
381 | which allow for rehearsals that do things like
|
382 | `increment(td.matchers.isA(Number))` or `save(td.matchers.contains({age: 21}))`.
|
383 |
|
384 | Also note that, `td.when()` takes an [optional configuration
|
385 | object](/docs/5-stubbing-results.md#configuring-stubbings) as a second
|
386 | parameter, which enables advanced usage like ignoring extraneous arguments and
|
387 | limiting the number of times a stubbing can be satisfied.
|
388 |
|
389 | Calling `td.when()` returns a number of functions that allow you to specify your
|
390 | desired outcome when the test double is invoked as demonstrated by your
|
391 | rehearsal. We'll begin with the most common of these: `thenReturn`.
|
392 |
|
393 | #### `td.when().thenReturn()`
|
394 |
|
395 | **`td.when(__rehearsal__[, options]).thenReturn('some value'[, more, values])`**
|
396 |
|
397 | The simplest example is when you want to return a specific value in exchange for
|
398 | a known argument, like so:
|
399 |
|
400 | ```js
|
401 | const loadsPurchases = td.replace('../src/loads-purchases')
|
402 | td.when(loadsPurchases(2018, 8)).thenReturn(['a purchase', 'another'])
|
403 | ```
|
404 |
|
405 | Then, in the hands of your subject under test:
|
406 |
|
407 | ```js
|
408 | loadsPurchases(2018, 8) // returns `['a purchase', 'another']`
|
409 | loadsPurchases(2018, 7) // returns undefined, since no stubbing was satisfied
|
410 | ```
|
411 |
|
412 | If you're not used to stubbing, it may seem contrived to think a test will know
|
413 | exactly what argument to pass in and expect back from a dependency, but in an
|
414 | isolated unit test this is not only feasible but entirely normal and expected!
|
415 | Doing so helps the author ensure the test remains minimal and obvious to
|
416 | future readers.
|
417 |
|
418 | Note as well that subsequent matching invocations can be stubbed by passing
|
419 | additional arguments to `thenReturn()`, like this:
|
420 |
|
421 | ```js
|
422 | const hitCounter = td.func()
|
423 | td.when(hitCounter()).thenReturn(1, 2, 3, 4)
|
424 |
|
425 | hitCounter() // 1
|
426 | hitCounter() // 2
|
427 | hitCounter() // 3
|
428 | hitCounter() // 4
|
429 | hitCounter() // 4
|
430 | ```
|
431 |
|
432 | #### `td.when().thenResolve()` and `td.when().thenReject()`
|
433 |
|
434 | **`td.when(__rehearsal__[, options]).thenResolve('some value'[, more, values])`**
|
435 |
|
436 | **`td.when(__rehearsal__[, options]).thenReject('some value'[, more, values])`**
|
437 |
|
438 | The `thenResolve()` and `thenReject()` stubbings will take whatever value is
|
439 | passed to them and wrap it in an immediately resolved or rejected promise,
|
440 | respectively. By default testdouble.js will use whatever `Promise` is globally
|
441 | defined, but you can specify your own like this:
|
442 |
|
443 | ```js
|
444 | td.config({promiseConstructor: require('bluebird')})`
|
445 | ```
|
446 |
|
447 | Because the Promise spec indicates that all promises must tick the event loop,
|
448 | keep in mind that any stubbing configured with `thenResolve` or `thenReject`
|
449 | must be managed as an asynchronous test (consult your test framework's
|
450 | documentation if you're not sure).
|
451 |
|
452 | #### `td.when().thenCallback()`
|
453 |
|
454 | **`td.when(__rehearsal__[, options]).thenCallback('some value'[,other,
|
455 | args])`**
|
456 |
|
457 | The `thenCallback()` stubbing will assume that the rehearsed invocation has an
|
458 | additional final argument that takes a callback function. When this stubbing is
|
459 | satisfied, testdouble.js will invoke that callback function and pass in whatever
|
460 | arguments were sent to `thenCallback()`.
|
461 |
|
462 | To illustrate, consider this stubbing:
|
463 |
|
464 | ```js
|
465 | const readFile = td.replace('../src/read-file')
|
466 | td.when(readFile('my-secret-doc.txt')).thenCallback(null, 'secrets!')
|
467 | ```
|
468 |
|
469 | Then, the subject might invoke readFile and pass an anonymous function:
|
470 |
|
471 | ```js
|
472 | readFile('my-secret-doc.txt', function (err, contents) {
|
473 | console.log(contents) // will print 'secrets!'
|
474 | })
|
475 | ```
|
476 |
|
477 | If the callback isn't in the final position, or if the test double also needs to
|
478 | return something, callbacks can be configured using the
|
479 | [td.callback](/docs/5-stubbing-results.md#callback-apis-with-a-callback-argument-at-an-arbitrary-position)
|
480 | argument matcher.
|
481 |
|
482 | On one hand, `thenCallback()` can be a great way to write fast and clear
|
483 | synchronous isolated unit tests of production code that's actually asynchronous.
|
484 | On the other hand, if it's necessary to verify the subject behaves correctly
|
485 | over multiple ticks of the event loop, you can control this with the [`defer`
|
486 | and `delay` options](/docs/5-stubbing-results.md#defer).
|
487 |
|
488 | #### `td.when().thenThrow()`
|
489 |
|
490 | **`td.when(__rehearsal__[, options]).thenThrow(new Error('boom'))`**
|
491 |
|
492 | The `thenThrow()` function does exactly what it says on the tin. Once this
|
493 | stubbing is configured, any matching invocations will throw the specified error.
|
494 |
|
495 | Note that because rehearsal calls invoke the test double function, it's possible
|
496 | to configure a `thenThrow` stubbing and then accidentally trigger it when you
|
497 | attempt to configure subsequent stubbings or verifications. In these cases,
|
498 | you'll need to work around it by re-ordering your configurations or `catch`'ing
|
499 | the error.
|
500 |
|
501 | #### `td.when().thenDo()`
|
502 |
|
503 | **`td.when(__rehearsal__[, options]).thenDo(function (arg1, arg2) {})`**
|
504 |
|
505 | For everything else, there is `thenDo()`. `thenDo` takes a function which will
|
506 | be invoked whenever satisfied with all the arguments and bound to the same
|
507 | `this` context that the test double function was actually invoked with. Whatever
|
508 | your `thenDo` function returns will be returned by the test double when the
|
509 | stubbing is satisfied. This configuration is useful for covering tricky cases
|
510 | not handled elsewhere, and may be a potential extension point for building on
|
511 | top of the library's stubbing capabilities.
|
512 |
|
513 | ### `td.verify()` for verifying interactions
|
514 |
|
515 | **`td.verify(__demonstration__[, options])`**
|
516 |
|
517 | If you've learned how to stub responses with `td.when()` then you already know
|
518 | how to verify an invocation took place with `td.verify()`! We've gone out of our
|
519 | way to make the two as symmetrical as possible. You'll find that they have
|
520 | matching function signatures, support the same argument matchers, and take the
|
521 | same options.
|
522 |
|
523 | The difference, then, is their purpose. While stubbings are meant to facilitate
|
524 | some behavior we want to exercise in our subject, verifications are meant to
|
525 | ensure a dependency was called in a particular expected way. Since `td.verify()`
|
526 | is an assertion step, it goes [at the
|
527 | end](https://github.com/testdouble/contributing-tests/wiki/Arrange-Act-Assert)
|
528 | of our test after we've invoked the subject under test.
|
529 |
|
530 | A trivial example might be:
|
531 |
|
532 | ```js
|
533 | module.exports = function shouldSaveThings () {
|
534 | const save = td.replace('../src/save')
|
535 | const subject = require('../src/index')
|
536 |
|
537 | subject({name: 'dataz', data: '010101'})
|
538 |
|
539 | td.verify(save('dataz', '010101'))
|
540 | }
|
541 | ```
|
542 |
|
543 | The above will verify that `save` was called with the two specified arguments.
|
544 | If the verification fails (say it passed `'010100'` instead), testdouble.js will
|
545 | throw a nice long error message to explain how the test double function was
|
546 | actually called, hopefully helping you spot the error.
|
547 |
|
548 | Just like with `td.when()`, more complex cases can be covered with [argument
|
549 | matchers](/docs/6-verifying-invocations.md#relaxing-verifications-with-argument-matchers)
|
550 | and [configuration
|
551 | options](/docs/6-verifying-invocations.md#configuring-verifications).
|
552 |
|
553 | A word of caution: `td.verify()` should be needed only sparingly. When you
|
554 | verify a function was called (as opposed to relying on what it returns) you're
|
555 | asserting that your subject has a side effect. Code with lots of side effects is
|
556 | bad, so mocking libraries are often abused to make side-effect heavy code easier
|
557 | to proliferate. In these cases, refactoring each dependency to return values
|
558 | instead is almost always the better design approach. A separate test smell with
|
559 | verifying calls is that sometimes—perhaps in the interest of maximal
|
560 | completeness—a test will verify an invocation that already satisfied a stubbing,
|
561 | but this is almost [provably
|
562 | unnecessary](/docs/B-frequently-asked-questions.md#why-shouldnt-i-call-both-tdwhen-and-tdverify-for-a-single-interaction-with-a-test-double).
|
563 |
|
564 | ### Other functions
|
565 |
|
566 | For other top-level features in the testdouble.js API, consult the [docs](/docs)
|
567 | directory:
|
568 |
|
569 | * [td.explain()](/docs/9-debugging.md#tdexplainsometestdouble) - for help
|
570 | debugging and introspecting test doubles
|
571 | * [td.config()](/docs/C-configuration.md#tdconfig) - for changing globally
|
572 | configurable options
|
573 | * [td.reset()](/docs/1-installation.md#resetting-state-between-test-runs) - for
|
574 | resetting testdouble.js state between tests
|
575 | * [td.matchers](/docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers)
|
576 | and [custom matchers](/docs/8-custom-matchers.md#custom-argument-matchers) for
|
577 | configuring more advanced stubbings and verifications
|
578 |
|
579 |
|