Skip to content

Commit 5a5554b

Browse files
committed
clackclack
1 parent 7826b1d commit 5a5554b

File tree

1 file changed

+114
-13
lines changed

1 file changed

+114
-13
lines changed

README.md

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ terse, clear, and easy-to-understand tests. There's an awful lot to cover, so
1414
please take some time and enjoy our documentation, which is designed to show you
1515
how to make the most out of test doubles in your tests.
1616

17-
This library was designed to work for both Node.js and browser interpeters and
18-
to be test-framework agnostic, so you can plop it into a codebase using Jasmine,
17+
This library was designed to work for both Node.js and browser interpeters. It's
18+
also test-framework agnostic, so you can plop it into a codebase using Jasmine,
1919
Mocha, Tape, Jest, or our own
2020
[teenytest](https://github.com/testdouble/teenytest).
2121

2222
## Install
2323

24-
```js
25-
npm install -D testdouble
24+
```
25+
$ npm install -D testdouble
2626
```
2727

28-
If you just want to fetch the browser distribution, you can also get it from
29-
[unpkg](https://unpkg.com/testdouble/dist/)
28+
If you just want to fetch the browser distribution, you can also curl it from
29+
[unpkg](https://unpkg.com/testdouble/dist/).
3030

3131
We recommend requiring the library in a test helper and setting it globally for
3232
convenience to the shorthand `td`:
@@ -38,13 +38,13 @@ global.td = require('testdouble') // Node.js; `window.td` for browsers
3838
(You may need to declare the global in order to make your linter handy.
3939
Instructions:
4040
[eslint](https://eslint.org/docs/user-guide/configuring#specifying-globals),
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).)
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).)
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 that is designed to encourage only productive
47-
use has been a real challenge. Here are a few paths to getting started:
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:
4848

4949
* The [API section of this README](#api) to get an at-a-glance view of
5050
the API so you can get started stubbing and verifying right away
@@ -60,6 +60,10 @@ use has been a real challenge. Here are a few paths to getting started:
6060
the various faetures of testdouble.js. Its outline is at the [bottom of this
6161
README](#docs)
6262

63+
Of course, if you're unsure of how to approach writing an isolated test with
64+
testdouble.js, we welcome you to [open a issue on GitHub to ask a
65+
question](https://github.com/testdouble/testdouble.js/issues/new).
66+
6367
## API
6468

6569
### `td.replace()` for replacing dependencies
@@ -69,10 +73,16 @@ the production dependencies of your [subject under
6973
test](https://github.com/testdouble/contributing-tests/wiki/Subject) with fake
7074
ones created by the library.
7175

72-
testdouble.js provides a top-level method called `td.replace()`
76+
We provide a top-level method called `td.replace()` that operates in two
77+
different modes: CommonJS module replacement and object-property replacement.
78+
Both modes will, by default, perform a deep clone the real dependency but
79+
replace all of its functions with fake test double functions that you can
80+
configure and observe.
7381

7482
#### Module replacement with Node.js
7583

84+
**`td.replace('../path/to/module'[, customReplacement])`**
85+
7686
If you're using Node.js and don't mind using the CommonJS `require` method in
7787
your tests (you can still use `import`/`export` in your production code,
7888
assuming you're compiling it down for consumption by your tests), testdouble.js
@@ -95,10 +105,20 @@ module.exports = {
95105
subject = require('../src/index')
96106
}
97107
//
108+
afterEach: function () { td.reset() }
98109
}
99110
```
100111

101-
Things to remember about replacing Node.js modules:
112+
In the above example, at the point when `src/index` is required, the module
113+
cache will be bypassed, and if `index` goes on to subsequently require any of
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:
102122

103123
* The test must `td.replace` and `require` everything in a before-each hook,
104124
in order to bypass Node's module cache and avoid test pollution
@@ -108,12 +128,93 @@ in order to bypass Node's module cache and avoid test pollution
108128
* The test suite (usually in a global after-each hook) must call `td.reset()` to
109129
avoid test pollution
110130

131+
##### default exports with ES modules
132+
133+
If your production code is written in ES module syntax and specifies a default
134+
export, just remember that you'll need to reference `.default` when translating
135+
to CJS syntax. That means:
136+
137+
```js
138+
loadsPurchases = td.replace('../src/loads-purchases')
139+
```
140+
141+
Probably needs to be written as:
142+
143+
```js
144+
loadsPurchases = td.replace('../src/loads-purchases').default
145+
```
146+
111147
#### Property replacement
112148

149+
**`td.replace(containingObject, nameOfPropertyToReplace[, customReplacement])`**
150+
113151
If you're running tests outside Node.js or otherwise injecting dependencies
114152
manually (or with a DI tool like
115-
[dependable](https://github.com/testdouble/dependable)), then you can still use
116-
`td.replace` to automatically replace and imitate
153+
[dependable](https://github.com/testdouble/dependable)), then you may still use
154+
`td.replace` to automatically replace things if they're addressable as
155+
properties on an object.
156+
157+
To illustrate, suppose our subject depends on `app.signup` below:
158+
159+
``` js
160+
app.signup = {
161+
onSubmit: function () {},
162+
onCancel: function () {}
163+
}
164+
```
165+
166+
If our goal is to replace `app.signup` so during a test of `app.user.create(),
167+
our test setup might look like this:
168+
169+
```js
170+
let signup, subject
171+
module.exports = {
172+
beforeEach: function () {
173+
signup = td.replace(app, 'signup')
174+
subject = app.user
175+
}
176+
//
177+
afterEach: function () { td.reset() }
178+
}
179+
```
180+
181+
`td.replace()` will always return the newly-created fake imitation, even though
182+
in this case it's obviously still referenceable by the test and subject alike
183+
with `app.signup`. If we had wanted to only replace the `onCancel` function for
184+
whatever reason (though in this case, that would smell like a [partial
185+
mock](https://github.com/testdouble/contributing-tests/wiki/Partial-Mock)), we
186+
could have called `td.replace(app.signup, 'onCancel)`, instead.
187+
188+
Remember, calling `td.reset()` in an after-each hook (preferably globally so one
189+
doesn't have to remember in each-and-every test) so that testdouble.js can
190+
replace the original is crucial to avoiding test pollution!
191+
192+
#### Specifying a custom replacement
193+
194+
The library's [imitation
195+
feature](https://github.com/testdouble/testdouble.js/blob/updte-readme/src/imitate/index.js)
196+
is pretty sophisticated, but it's not perfect. It's also going to be pretty slow
197+
on large, complex objects. If you'd like to specify exactly what to replace a
198+
real dependency with, you can do so in either of the above modes by providing a
199+
final optional argument.
200+
201+
When replacing a Node.js module:
202+
203+
```js
204+
generatesInvoice = td.replace('../generates-invoice', {
205+
generate: td.func('a generate function'),
206+
name: 'fake invoices'
207+
})
208+
```
209+
210+
When replacing a property:
211+
212+
```js
213+
signup = td.replace(app, 'signup', {
214+
onSubmit: td.func('fake submit handler'),
215+
onCancel: function () { throw Error('do not call me') }
216+
})
217+
```
117218

118219
### Stubbing return values for functions
119220

0 commit comments

Comments
 (0)