Skip to content

Commit bfa7edc

Browse files
committed
hackahachakc
1 parent 0c745d5 commit bfa7edc

File tree

1 file changed

+72
-53
lines changed

1 file changed

+72
-53
lines changed

README.md

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,17 @@ convenience to the shorthand `td`:
3535
global.td = require('testdouble') // Node.js; `window.td` for browsers
3636
```
3737

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.
3939
Instructions:
4040
[eslint](https://eslint.org/docs/user-guide/configuring#specifying-globals),
4141
[standard](https://github.com/standard/standard/#i-use-a-library-that-pollutes-the-global-namespace-how-do-i-prevent-variable-is-not-defined-errors).)
4242

4343
## Getting started
4444

4545
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:
4849

4950
* The [API section of this README](#api) to get an at-a-glance view of the API
5051
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,
8889
assuming you're compiling it down for consumption by your tests), testdouble.js
8990
uses a library we wrote called [quibble](https://github.com/testdouble/quibble)
9091
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
9293
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.
9495

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:
9797

9898
```js
9999
let loadsPurchases, generatesInvoice, sendsInvoice, subject
@@ -112,21 +112,28 @@ module.exports = {
112112
In the above example, at the point when `src/index` is required, the module
113113
cache will be bypassed, and if `index` goes on to subsequently require any of
114114
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
128134
* 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.
130137

131138
##### Default exports with ES modules
132139

@@ -187,7 +194,7 @@ whatever reason (though in this case, that would smell like a [partial
187194
mock](https://github.com/testdouble/contributing-tests/wiki/Partial-Mock)), we
188195
could have called `td.replace(app.signup, 'onCancel')`, instead.
189196

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
191198
doesn't have to remember to do so in each-and-every test) so that testdouble.js
192199
can replace the original is crucial to avoiding hard-to-debug test pollution!
193200

@@ -242,7 +249,7 @@ double function and can be called in three modes:
242249
* **`td.func()`** - returns an anonymous test double function that can be used
243250
for stubbing and verifying any calls against it, but whose error messages and
244251
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',
246253
which will appear in any error messages as well as the debug info returned by
247254
passing the returned test double into `td.explain()`
248255

@@ -255,13 +262,13 @@ and supports three types of invocations:
255262
[imitation](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
256263
the passed object, where each function is replaced with a test double function
257264
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'`)
260267
* **`td.object(['add', 'subtract'])`** - returns a plain JavaScript object
261268
containing two properties `add` and `subtract` that are both assigned to test
262269
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
265272
Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
266273
The proxy will automatically intercept any call made to it and shunt in a test
267274
double that can be used for stubbing or verification. More details can be
@@ -277,18 +284,19 @@ well.
277284
be verified and whose `prototype` functions have all been replaced with test
278285
double functions using the same
279286
[imitation](https://github.com/testdouble/testdouble.js/blob/master/src/imitate/index.js)
287+
mechanism described above
280288
* **`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
283291

284292
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:
288295

289296
```js
290297
const FakeConstructor = td.constructor(RealConstructor)
291298
td.when(FakeConstructor.prototype.doStuff()).thenReturn('ok')
299+
292300
subject(FakeConstructor)
293301
```
294302

@@ -351,17 +359,17 @@ Then, in the hands of your subject under test:
351359

352360
```js
353361
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
355363
```
356364

357365
If you're not used to stubbing, it may seem contrived to think a test will know
358366
exactly what argument to pass in and expect back from a dependency, but in an
359367
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
361369
future readers.
362370

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:
365373

366374
```js
367375
const hitCounter = td.func()
@@ -377,6 +385,7 @@ hitCounter() // 5
377385
#### `td.when().thenResolve()` and `td.when().thenReject()`
378386

379387
**`td.when(__rehearsal__[, options]).thenResolve('some value'[, more, values])`**
388+
380389
**`td.when(__rehearsal__[, options]).thenReject('some value'[, more, values])`**
381390

382391
The `thenResolve()` and `thenReject()` stubbings will take whatever value is
@@ -419,21 +428,22 @@ readFile('my-secret-doc.txt', function (er, contents) {
419428
```
420429

421430
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
423432
[td.callback](/docs/5-stubbing-results.md#callback-apis-with-a-callback-argument-at-an-arbitrary-position)
424433
argument matcher.
425434

426435
#### `td.when().thenThrow()`
427436

428-
**`td.when(__rehearsal__[, options]).thenThrow(someError)`**
437+
**`td.when(__rehearsal__[, options]).thenThrow(new Error('boom'))`**
429438

430439
The `thenThrow()` function does exactly what it says on the tin. Once this
431440
stubbing is configured, any matching invocations will throw the specified error.
432441

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.
437447

438448
#### `td.when().thenDo()`
439449

@@ -477,28 +487,37 @@ module.exports = function shouldSaveThings () {
477487
```
478488

479489
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+
480494
Just like with `td.when()`, more complex cases can be covered with [argument
481495
matchers](/docs/6-verifying-invocations.md#relaxing-verifications-with-argument-matchers)
482496
and [configuration
483497
options](/docs/6-verifying-invocations.md#configuring-verifications).
484498

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
492507
unnecessary](/docs/B-frequently-asked-questions.md#why-shouldnt-i-call-both-tdwhen-and-tdverify-for-a-single-interaction-with-a-test-double).
493508

494509
### Other functions
495510

496511
For other top-level features in the testdouble.js API, consult the [docs](/docs)
497512
directory:
498513

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
502520
* [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
504523

0 commit comments

Comments
 (0)