@@ -35,16 +35,17 @@ convenience to the shorthand `td`:
35
35
global .td = require (' testdouble' ) // Node.js; `window.td` for browsers
36
36
```
37
37
38
- (You may need to declare the global in order to make your linter handy .
38
+ (You may need to configure your linter to ingore the ` td ` global .
39
39
Instructions:
40
40
[ eslint] ( https://eslint.org/docs/user-guide/configuring#specifying-globals ) ,
41
41
[ standard] ( https://github.com/standard/standard/#i-use-a-library-that-pollutes-the-global-namespace-how-do-i-prevent-variable-is-not-defined-errors ) .)
42
42
43
43
## Getting started
44
44
45
45
Mocking libraries are more often abused than used effectively, so figuring out
46
- how to document a mocking library in such a way as to only encourage healthy
47
- use has proven to be a real challenge. Here are a few paths to getting started:
46
+ how to document a mocking library so as to only encourage healthy uses has
47
+ proven to be a real challenge. Here are a few paths we've prepared for getting
48
+ started with testdouble.js:
48
49
49
50
* The [ API section of this README] ( #api ) to get an at-a-glance view of the API
50
51
so you can get started stubbing and verifying right away
@@ -88,12 +89,11 @@ your tests (you can still use `import`/`export` in your production code,
88
89
assuming you're compiling it down for consumption by your tests), testdouble.js
89
90
uses a library we wrote called [ quibble] ( https://github.com/testdouble/quibble )
90
91
to monkey-patch the ` require() ` feature so that your subject will automatically
91
- receive your faked dependencies simply by requiring them. ( If you've used
92
+ receive your faked dependencies simply by requiring them. If you've used
92
93
something like [ proxyquire] ( https://github.com/thlorenz/proxyquire ) , this is
93
- like a slightly terser form of that.)
94
+ like a slightly terser form of that.
94
95
95
- Here's an example of using ` td.replace ` in the setup of a test of a Node.js
96
- module:
96
+ Here's an example of using ` td.replace() ` in a Node.js test's setup:
97
97
98
98
``` js
99
99
let loadsPurchases, generatesInvoice, sendsInvoice, subject
@@ -112,21 +112,28 @@ module.exports = {
112
112
In the above example, at the point when ` src/index ` is required, the module
113
113
cache will be bypassed, and if ` index ` goes on to subsequently require any of
114
114
the ` td.replace() ` 'd dependencies, it will receive a reference to the same fake
115
- dependency returned to the test. If ` loads-purchases ` exports a function, a test
116
- double function will be created to imitate it. If ` generates-invoice ` exports a
117
- constructor, the constructor and all of its instance methods will also be
118
- imitated. If ` sends-invoice ` exports a plain object of function properties, each
119
- function will be replaced with a test double (and the other values cloned).
120
-
121
- To repeat, important things to remember about replacing Node.js modules:
122
-
123
- * The test must ` td.replace ` and ` require ` everything in a before-each hook,
124
- in order to bypass Node's module cache and avoid test pollution
125
- * Relative paths to each replaced dependency are relative * from the test listing
126
- to the dependency* . This runs counter to how some other tools do it, but we
127
- feel it makes more sense
115
+ dependencies that were returned to the test.
116
+
117
+ Because ` td.replace() ` first loads the actual file, it will do its best to
118
+ return a fake that is shaped just like the real thing. That means that if
119
+ ` loads-purchases ` exports a function, a test double function will be created and
120
+ returned. If ` generates-invoice ` exports a constructor, the constructor and all
121
+ of its static and instance methods will be replaced with test double functions.
122
+ If ` sends-invoice ` exports a plain object of function properties, each function
123
+ will be replaced with a test double (and the other values cloned).
124
+
125
+ There are a few important things to keep in mind about replacing Node.js modules
126
+ using ` td.replace() ` :
127
+
128
+ * The test must ` td.replace() ` and ` require() ` everything in a before-each hook,
129
+ in order to bypass the Node.js module cache and to avoid pollution between
130
+ tests
131
+ * Any relative paths passed to ` td.replace() ` are relative * from the test to the
132
+ dependency* . This runs counter to how some other tools do it, but we feel it
133
+ makes more sense
128
134
* The test suite (usually in a global after-each hook) must call ` td.reset() ` to
129
- avoid test pollution
135
+ ensure the real ` require() ` function and dependency modules are restored after
136
+ each test case.
130
137
131
138
##### Default exports with ES modules
132
139
@@ -187,7 +194,7 @@ whatever reason (though in this case, that would smell like a [partial
187
194
mock] ( https://github.com/testdouble/contributing-tests/wiki/Partial-Mock ) ), we
188
195
could have called ` td.replace(app.signup, 'onCancel') ` , instead.
189
196
190
- Remember, calling ` td.reset() ` in an after-each hook (preferably globally so one
197
+ Remember to call ` td.reset() ` in an after-each hook (preferably globally so one
191
198
doesn't have to remember to do so in each-and-every test) so that testdouble.js
192
199
can replace the original is crucial to avoiding hard-to-debug test pollution!
193
200
@@ -242,7 +249,7 @@ double function and can be called in three modes:
242
249
* ** ` td.func() ` ** - returns an anonymous test double function that can be used
243
250
for stubbing and verifying any calls against it, but whose error messages and
244
251
debugging output won't have a name to trace back to it
245
- * ** `td.func('some name')** - returns a test double function named 'some name',
252
+ * ** ` td.func('some name') ` ** - returns a test double function named 'some name',
246
253
which will appear in any error messages as well as the debug info returned by
247
254
passing the returned test double into ` td.explain() `
248
255
@@ -255,13 +262,13 @@ and supports three types of invocations:
255
262
[ imitation] ( https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js )
256
263
the passed object, where each function is replaced with a test double function
257
264
named for the property path (e.g. If ` realObject.invoices.send() ` was a
258
- function, the returned object would have a test double named
259
- ` '.invoices.send' ` )
265
+ function, the returned object would have property ` invoices.send ` set to a
266
+ test double named ` '.invoices.send' ` )
260
267
* ** ` td.object(['add', 'subtract']) ` ** - returns a plain JavaScript object
261
268
containing two properties ` add ` and ` subtract ` that are both assigned to test
262
269
double functions named ` '.add' ` and ` '.subtract' ` , respectively.
263
- * ** ` td.object([ 'a Person', {excludeMethods: ['then']}) ` ** - when passed with no
264
- args or with a string as the first argument, returns an [ ES
270
+ * ** ` td.object('a Person'[ , {excludeMethods: ['then']}) ` ** - when passed with no
271
+ args or with a string name as the first argument, returns an [ ES
265
272
Proxy] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy ) .
266
273
The proxy will automatically intercept any call made to it and shunt in a test
267
274
double that can be used for stubbing or verification. More details can be
@@ -277,18 +284,19 @@ well.
277
284
be verified and whose ` prototype ` functions have all been replaced with test
278
285
double functions using the same
279
286
[ imitation] ( https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js )
287
+ mechanism described above
280
288
* ** ` td.constructor(['select', 'save']) ` ** - returns a constructor with ` select `
281
- and ` save ` properties set to test double functions named ` '#select' ` and
282
- ` '#save ' ` on its ` prototype ` object
289
+ and ` save ` properties on its ` prototype ` object set to test double functions
290
+ named ` '#select ' ` and ` '#save' ` , respectively
283
291
284
292
When replacing a constructor, typically the test will configure stubbing &
285
- verification by directly addressing its prototype functions.
286
-
287
- To illustrate, That means in your test you might write:
293
+ verification by directly addressing its prototype functions. To illustrate, that
294
+ means in your test you might write:
288
295
289
296
``` js
290
297
const FakeConstructor = td .constructor (RealConstructor)
291
298
td .when (FakeConstructor .prototype .doStuff ()).thenReturn (' ok' )
299
+
292
300
subject (FakeConstructor)
293
301
```
294
302
@@ -351,17 +359,17 @@ Then, in the hands of your subject under test:
351
359
352
360
``` js
353
361
loadsPurchases (2018 , 8 ) // returns `['a purchase', 'another']`
354
- loadsPurchases (2018 , 7 ) // returns undefined, since no stubbing matched
362
+ loadsPurchases (2018 , 7 ) // returns undefined, since no stubbing was satisfied
355
363
```
356
364
357
365
If you're not used to stubbing, it may seem contrived to think a test will know
358
366
exactly what argument to pass in and expect back from a dependency, but in an
359
367
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
368
+ Doing so helps the author ensure the test remains minimal and obvious to
361
369
future readers.
362
370
363
- Note as well that subsequence instances of matching invocations can be stubbed
364
- by passing additional arguments to ` thenReturn() ` , such that :
371
+ Note as well that subsequent matching invocations can be stubbed by passing
372
+ additional arguments to ` thenReturn() ` , like this :
365
373
366
374
``` js
367
375
const hitCounter = td .func ()
@@ -377,6 +385,7 @@ hitCounter() // 5
377
385
#### ` td.when().thenResolve() ` and ` td.when().thenReject() `
378
386
379
387
** ` td.when(__rehearsal__[, options]).thenResolve('some value'[, more, values]) ` **
388
+
380
389
** ` td.when(__rehearsal__[, options]).thenReject('some value'[, more, values]) ` **
381
390
382
391
The ` thenResolve() ` and ` thenReject() ` stubbings will take whatever value is
@@ -419,21 +428,22 @@ readFile('my-secret-doc.txt', function (er, contents) {
419
428
```
420
429
421
430
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
431
+ return something, callbacks can be configured using the
423
432
[ td.callback] ( /docs/5-stubbing-results.md#callback-apis-with-a-callback-argument-at-an-arbitrary-position )
424
433
argument matcher.
425
434
426
435
#### ` td.when().thenThrow() `
427
436
428
- ** ` td.when(__rehearsal__[, options]).thenThrow(someError ) ` **
437
+ ** ` td.when(__rehearsal__[, options]).thenThrow(new Error('boom') ) ` **
429
438
430
439
The ` thenThrow() ` function does exactly what it says on the tin. Once this
431
440
stubbing is configured, any matching invocations will throw the specified error.
432
441
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.
442
+ Note that because rehearsal calls invoke the test double function, it's possible
443
+ to configure a ` thenThrow ` stubbing and then accidentally trigger it when you
444
+ attempt to configure subsequent stubbings or verifications. In these cases,
445
+ you'll need to workaround it by re-ordering your configurations or ` catch ` 'ing
446
+ the error.
437
447
438
448
#### ` td.when().thenDo() `
439
449
@@ -477,28 +487,37 @@ module.exports = function shouldSaveThings () {
477
487
```
478
488
479
489
The above will verify that ` save ` was called with the two specified arguments.
490
+ If the verification fails (say it passed ` '010100' ` instead), testdouble.js will
491
+ throw a nice long error message to explain how the test double function was
492
+ actually called, so that you can spot the error.
493
+
480
494
Just like with ` td.when() ` , more complex cases can be covered with [ argument
481
495
matchers] ( /docs/6-verifying-invocations.md#relaxing-verifications-with-argument-matchers )
482
496
and [ configuration
483
497
options] ( /docs/6-verifying-invocations.md#configuring-verifications ) .
484
498
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
499
+ A word of caution: ` td.verify() ` should be needed only sparingly. When you
500
+ verify a function was called (as opposed to what it returns) you're asserting
501
+ that your code has a desired side effect. Code with lots of side effects is bad,
502
+ so mocking libraries are often abused to make side-effect heavy code easier to
503
+ test. In these cases, refactoring each dependency to return values instead is
504
+ almost always the better design approach. Sometimes in the interest of
505
+ completeness, people will attempt to verify an invocation that already satisfies
506
+ a stub, but this is almost [ provably
492
507
unnecessary] ( /docs/B-frequently-asked-questions.md#why-shouldnt-i-call-both-tdwhen-and-tdverify-for-a-single-interaction-with-a-test-double ) .
493
508
494
509
### Other functions
495
510
496
511
For other top-level features in the testdouble.js API, consult the [ docs] ( /docs )
497
512
directory:
498
513
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 )
514
+ * [ td.explain()] ( /docs/9-debugging.md#tdexplainsometestdouble ) - for help
515
+ debugging and introspecting test doubles
516
+ * [ td.config()] ( /docs/C-configuration.md#tdconfig ) - for changing globally
517
+ configurable options
518
+ * [ td.reset()] ( /docs/1-installation.md#resetting-state-between-test-runs ) - for
519
+ resetting testdouble.js state between tests
502
520
* [ td.matchers] ( /docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers )
503
- (and [ custom matchers] ( /docs/8-custom-matchers.md#custom-argument-matchers ) )
521
+ and [ custom matchers] ( /docs/8-custom-matchers.md#custom-argument-matchers ) for
522
+ configuring more advanced stubbings and verifications
504
523
0 commit comments