@@ -46,19 +46,19 @@ Mocking libraries are more often abused than used effectively, so figuring out
46
46
how to document a mocking library in such a way as to only encourage healthy
47
47
use has proven to be a real challenge. Here are a few paths to getting started:
48
48
49
- * The [ API section of this README] ( #api ) to get an at-a-glance view of
50
- the API so you can get started stubbing and verifying right away
49
+ * The [ API section of this README] ( #api ) to get an at-a-glance view of the API
50
+ so you can get started stubbing and verifying right away
51
51
* A [ 20-minute
52
52
video] ( http://blog.testdouble.com/posts/2016-06-05-happier-tdd-with-testdouble-js )
53
53
overview of the library, its goals, and the basics of its API
54
54
* A [ comparison between testdouble.js and
55
55
Sinon.js] ( http://blog.testdouble.com/posts/2016-03-13-testdouble-vs-sinon.html ) ,
56
56
in case you've already got experience working with Sinon and you're looking
57
57
for a high-level overview of the differences
58
- * The full testdouble.js [ documentation] ( /docs ) , which is quite lengthy , but
58
+ * The full testdouble.js [ documentation] ( /docs ) , which is lengthier , but
59
59
will do a thorough job to explain when to (and when not to) take advantage of
60
- the various faetures of testdouble.js. Its outline is at the [ bottom of this
61
- README] ( # docs)
60
+ the various faetures of testdouble.js. Its outline is in
61
+ [ docs/ README.md ] ( / docs#readme )
62
62
63
63
Of course, if you're unsure of how to approach writing an isolated test with
64
64
testdouble.js, we welcome you to [ open a issue on GitHub to ask a
@@ -301,37 +301,204 @@ const subject = function (SomeConstructor) {
301
301
}
302
302
```
303
303
304
- ---
305
- # old docs:
306
- ---
304
+ ### ` td.when() ` for stubbing responses
307
305
308
- ### Stubbing return values for functions
306
+ ** ` td.when(__rehearsal__[, options]) ` **
307
+
308
+ Once you have your subject's dependencies replaced with test double functions,
309
+ you'll want to be able to to stub return values (and other sorts of responses)
310
+ when the subject invokes the test double in the way that the test expects.
311
+
312
+ To make stubbing configuration easy to read and grep, ` td.when() ` 's first
313
+ argument isn't an argument at all, but rather a placeholder to demonstrate the
314
+ way you're expecting the test double to be invoked by the subject, like so:
315
+
316
+ ``` js
317
+ const increment = td .func ()
318
+ td .when (increment (5 )).thenReturn (6 )
319
+ ```
320
+
321
+ We would say that ` increment(5) ` is "rehearsing the invocation". Note that by
322
+ default, a stubbing is only satisfied when the subject calls the test double
323
+ exactly as it was rehearsed. This can be customized with [ argument
324
+ matchers] ( /docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers ) ,
325
+ which allow for rehearsals that do things like
326
+ ` increment(td.matchers.isA(Number)) ` or ` save(td.matchers.contains({age: 21})) ` .
327
+
328
+ Also note that, ` td.when() ` takes an [ optional configuration
329
+ object] ( /docs/5-stubbing-results.md#configuring-stubbings ) as a second
330
+ parameter, which enables advanced usage like ignoring extraneous arguments and
331
+ limiting the number of times a stubbing can be satisfied.
332
+
333
+ Calling ` td.when() ` returns an object of functions that each represent
334
+ the type of outcome you want to configure whenever the test double is invoked as
335
+ demonstrated by your rehearsal, which we'll describe below, beginning with
336
+ ` thenReturn ` .
337
+
338
+ #### ` td.when().thenReturn() `
339
+
340
+ ** ` td.when(__rehearsal__[, options]).thenReturn('some value'[, more, values]) ` **
341
+
342
+ The simplest example is when you want to return a specific value in exchange for
343
+ a known argument, like so:
309
344
310
345
``` js
311
- var td = require (' testdouble' );
346
+ const loadsPurchases = td .replace (' ../src/loads-purchases' )
347
+ td .when (loadsPurchases (2018 , 7 )).thenReturn ([' a purchase' , ' another' ])
348
+ ```
312
349
313
- var fetch = td .function ();
314
- td .when (fetch (42 )).thenReturn (' Jane User' );
350
+ Then, in the hands of your subject under test:
315
351
316
- fetch (42 ); // -> 'Jane User'
352
+ ``` js
353
+ loadsPurchases (2018 , 8 ) // returns `['a purchase', 'another']`
354
+ loadsPurchases (2018 , 7 ) // returns undefined, since no stubbing matched
317
355
```
318
356
319
- ### Verifying a function was invoked
357
+ If you're not used to stubbing, it may seem contrived to think a test will know
358
+ exactly what argument to pass in and expect back from a dependency, but in an
359
+ isolated unit test this is not only feasible but entirely normal and expected!
360
+ It can help the test author ensure the test remains minimal and obvious to
361
+ future readers.
362
+
363
+ Note as well that subsequence instances of matching invocations can be stubbed
364
+ by passing additional arguments to ` thenReturn() ` , such that:
320
365
321
366
``` js
322
- var td = require (' testdouble' );
323
-
324
- var save = td .function (' .save' );
325
- save (41 , ' Jane' );
326
-
327
- td .verify (save (41 , ' Jill' ));
328
- //
329
- // Error: Unsatisfied verification on test double `.save`.
330
- //
331
- // Wanted:
332
- // - called with `(41, "Jill")`.
333
- //
334
- // But was actually called:
335
- // - called with `(41, "Jane")`.
367
+ const hitCounter = td .func ()
368
+ td .when (hitCounter ()).thenReturn (1 , 2 , 3 , 4 )
369
+
370
+ hitCounter () // 1
371
+ hitCounter () // 2
372
+ hitCounter () // 3
373
+ hitCounter () // 4
374
+ hitCounter () // 5
336
375
```
337
376
377
+ #### ` td.when().thenResolve() ` and ` td.when().thenReject() `
378
+
379
+ ** ` td.when(__rehearsal__[, options]).thenResolve('some value'[, more, values]) ` **
380
+ ** ` td.when(__rehearsal__[, options]).thenReject('some value'[, more, values]) ` **
381
+
382
+ The ` thenResolve() ` and ` thenReject() ` stubbings will take whatever value is
383
+ passed to them and wrap it in an immediately resolved or rejected promise,
384
+ respectively. By default testdouble.js will use whatever ` Promise ` is globally
385
+ defined, but you can specify your own like this:
386
+
387
+ ``` js
388
+ td .config ({promiseConstructor: require (' bluebird' )})`
389
+ ` ` `
390
+
391
+ Because the Promise spec indicates that all promises must tick the event loop,
392
+ keep in mind that any stubbing configured with ` thenResolve` or ` thenReject`
393
+ must be managed as an asynchronous test (consult your test framework' s
394
+ documentation if you' re not sure).
395
+
396
+ #### ` td.when().thenCallback()`
397
+
398
+ ** ` td.when(__rehearsal__[, options]).thenCallback('some value'[,other,
399
+ args])` **
400
+
401
+ The ` thenCallback()` stubbing will assume that the rehearsed invocation has an
402
+ additional final argument that takes a function . When invoked by the subject,
403
+ testdouble.js will invoke that function and pass whatever arguments were sent to
404
+ `thenCallback ()`.
405
+
406
+ To illustrate, consider this stubbing:
407
+
408
+ ```js
409
+ const readFile = td.replace('../src/read-file')
410
+ td.when(readFile('my-secret-doc.txt')).thenCallback(null, 'secrets!')
411
+ ```
412
+
413
+ Then, the subject might invoke readFile and pass an anonymous function:
414
+
415
+ ```js
416
+ readFile('my-secret-doc.txt', function (er, contents) {
417
+ console .log (contents) // will print 'secrets!'
418
+ })
419
+ ```
420
+
421
+ If the callback isn't in the final position, or if the test double also needs to
422
+ return something, it can be configured using the
423
+ [ td.callback] ( /docs/5-stubbing-results.md#callback-apis-with-a-callback-argument-at-an-arbitrary-position )
424
+ argument matcher.
425
+
426
+ #### ` td.when().thenThrow() `
427
+
428
+ ** ` td.when(__rehearsal__[, options]).thenThrow(someError) ` **
429
+
430
+ The ` thenThrow() ` function does exactly what it says on the tin. Once this
431
+ stubbing is configured, any matching invocations will throw the specified error.
432
+
433
+ Note that because "rehearsal" calls invoke the test double function, that it's
434
+ possible to configure ` thenThrow ` and then find that subsequent stubbings or
435
+ verifications can't be configured without also ` catch ` 'ing the error. This ought
436
+ to be a rarely encountered edge case.
437
+
438
+ #### ` td.when().thenDo() `
439
+
440
+ ** ` td.when(__rehearsal__[, options]).thenDo(function (arg1, arg2) {}) ` **
441
+
442
+ For everything else, there is ` thenDo() ` . ` thenDo ` takes a function which, for
443
+ matching rehearsals, testdouble.js will invoke and forward along all arguments
444
+ passed as well as bind the ` this ` context the test double function was invoked
445
+ with. This callback is useful for covering tricky cases not handled elsewhere,
446
+ and may be a useful extension point for building on top of the library's
447
+ stubbing capabilities.
448
+
449
+ ### ` td.verify() ` for verifying interactions
450
+
451
+ ** ` td.verify(__demonstration__[, options]) ` **
452
+
453
+ If you've learned how to stub responses with ` td.when() ` then you already know
454
+ how to verify an invocation took place with ` td.verify() ` ! We've gone out of our
455
+ way to make the two as symmetrical as possible. You'll find that they have
456
+ matching function signatures, support the same argument matchers, and take the
457
+ same options.
458
+
459
+ The difference, then, is their purpose. While stubbings are meant to facilitate
460
+ some behavior we want to exercise in our subject, verifications are meant to
461
+ ensure a dependency was called in a particular expected way. Since ` td.verify() `
462
+ is an assertion step, it goes [ at the
463
+ end] ( https://github.com/testdouble/contributing-tests/wiki/Arrange-Act-Assert )
464
+ of our test after we've invoked the subject under test.
465
+
466
+ A trivial example might be:
467
+
468
+ ``` js
469
+ module .exports = function shouldSaveThings () {
470
+ const save = td .replace (' ../src/save' )
471
+ const subject = require (' ../src/index' )
472
+
473
+ subject ({name: ' dataz' , data: ' 010101' })
474
+
475
+ td .verify (save (' dataz' , ' 010101' ))
476
+ }
477
+ ```
478
+
479
+ The above will verify that ` save ` was called with the two specified arguments.
480
+ Just like with ` td.when() ` , more complex cases can be covered with [ argument
481
+ matchers] ( /docs/6-verifying-invocations.md#relaxing-verifications-with-argument-matchers )
482
+ and [ configuration
483
+ options] ( /docs/6-verifying-invocations.md#configuring-verifications ) .
484
+
485
+ A word of caution: when you verify a function was called, as opposed to what it
486
+ returns, you're asserting that your code has a desired side effect. Code with
487
+ lots of side effects is bad, so mocking libraries are often abused to make
488
+ side-effect heavy code easier to test. In these cases, refactoring each
489
+ dependency to return values instead is almost always the better design approach.
490
+ Sometimes in the interest of completeness, people will attempt to verify an
491
+ invocation that already satisfies a stub, but this is almost [ provably
492
+ unnecessary] ( /docs/B-frequently-asked-questions.md#why-shouldnt-i-call-both-tdwhen-and-tdverify-for-a-single-interaction-with-a-test-double ) .
493
+
494
+ ### Other functions
495
+
496
+ For other top-level features in the testdouble.js API, consult the [ docs] ( /docs )
497
+ directory:
498
+
499
+ * [ td.explain()] ( /docs/9-debugging.md#tdexplainsometestdouble )
500
+ * [ td.config()] ( /docs/C-configuration.md#tdconfig )
501
+ * [ td.reset()] ( /docs/1-installation.md#resetting-state-between-test-runs )
502
+ * [ td.matchers] ( /docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers )
503
+ (and [ custom matchers] ( /docs/8-custom-matchers.md#custom-argument-matchers ) )
504
+
0 commit comments