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