Skip to content

Commit 77fe672

Browse files
committed
Add several methods to set up default behaviors. These methods can be placed anywhere in the mocking set up, including after other calledWith setups, unlike previous "default" behavior using when(fn).mockReturnValue(z). You can now do: when(fn).calledWith(x).mockReturnValue(y).defaultReturnValue(z).
1 parent a6dc74d commit 77fe672

3 files changed

Lines changed: 334 additions & 126 deletions

File tree

README.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,23 @@ fn(2); // Will throw a helpful jest assertion error with args diff
284284

285285
#### Supports default behavior
286286

287-
Use any of `mockReturnValue`, `mockResolvedValue`, `mockRejectedValue`, `mockImplementation` directly on the object
287+
Use any of `defaultReturnValue`, `defaultResolvedValue`, `defaultRejectedValue`, `defaultImplementation`
288288
to set up a default behavior, which will serve as fallback if no matcher fits.
289289

290290
```javascript
291+
when(fn)
292+
.calledWith('foo').mockReturnValue('special')
293+
.defaultReturnValue('default') // This line can be placed anywhere, doesn't have to be at the end
294+
295+
expect(fn('foo')).toEqual('special')
296+
expect(fn('bar')).toEqual('default')
297+
```
298+
299+
Or if you use any of `mockReturnValue`, `mockResolvedValue`, `mockRejectedValue`, `mockImplementation` directly on the object
300+
before using `calledWith` it will also behave as a default fallback.
301+
302+
```javascript
303+
// Same as above example
291304
when(fn)
292305
.mockReturnValue('default')
293306
.calledWith('foo').mockReturnValue('special')
@@ -299,15 +312,15 @@ expect(fn('bar')).toEqual('default')
299312
One idea is to set up a default implementation that throws an error if an improper call is made to the mock.
300313

301314
```javascript
302-
// A default implementation that fails your test
303-
const unsupportedCallError = (...args) => {
304-
throw new Error(`Wrong args: ${JSON.stringify(args, null, 2)}`);
305-
};
306-
307315
when(fn)
308-
.mockImplementation(unsupportedCallError)
309316
.calledWith(correctArgs)
310-
.mockReturnValue(expectedValue);
317+
.mockReturnValue(expectedValue)
318+
.defaultImplementation(unsupportedCallError)
319+
320+
// A default implementation that fails your test
321+
function unsupportedCallError(...args) {
322+
throw new Error(`Wrong args: ${JSON.stringify(args, null, 2)}`);
323+
}
311324
```
312325

313326
#### Supports custom mockImplementation

src/when.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ class WhenMock {
6262
fn.__whenMock__ = this
6363
this.callMocks = []
6464
this._origMock = fn.getMockImplementation()
65-
this.defaultImplementation = null
65+
this._defaultImplementation = null
6666

6767
const _mockImplementation = (matchers, expectCall, once = false) => (mockImplementation) => {
6868
if (matchers[0] === NO_CALLED_WITH_YET) {
69-
this.defaultImplementation = mockImplementation
69+
this._defaultImplementation = mockImplementation
7070
}
7171
// To enable dynamic replacement during a test:
7272
// * call mocks with equal matchers are removed
@@ -108,8 +108,8 @@ class WhenMock {
108108
}
109109
}
110110

111-
if (this.defaultImplementation) {
112-
return this.defaultImplementation(...args)
111+
if (this._defaultImplementation) {
112+
return this._defaultImplementation(...args)
113113
}
114114
if (typeof fn.__whenMock__._origMock === 'function') {
115115
return fn.__whenMock__._origMock(...args)
@@ -131,18 +131,26 @@ class WhenMock {
131131
mockRejectedValue: err => _mockImplementation(matchers, expectCall)(() => Promise.reject(err)),
132132
mockRejectedValueOnce: err => _mockImplementation(matchers, expectCall, true)(() => Promise.reject(err)),
133133
mockImplementation: implementation => _mockImplementation(matchers, expectCall)(implementation),
134-
mockImplementationOnce: implementation => _mockImplementation(matchers, expectCall, true)(implementation)
134+
mockImplementationOnce: implementation => _mockImplementation(matchers, expectCall, true)(implementation),
135+
defaultImplementation: implementation => this.defaultImplementation(implementation),
136+
defaultReturnValue: returnValue => this.defaultReturnValue(returnValue),
137+
defaultResolvedValue: returnValue => this.defaultResolvedValue(returnValue),
138+
defaultRejectedValue: err => this.defaultRejectedValue(err)
135139
})
136140

137141
// These four functions are only used when the dev has not used `.calledWith` before calling one of the mock return functions
138-
this.mockImplementation = mockImplementation => {
142+
this.defaultImplementation = mockImplementation => {
139143
// Set up an implementation with a special matcher that can never be matched because it uses a private symbol
140144
// Additionally the symbols existence can be checked to see if a calledWith was omitted.
141145
return _mockImplementation([NO_CALLED_WITH_YET], false)(mockImplementation)
142146
}
143-
this.mockReturnValue = returnValue => this.mockImplementation(() => returnValue)
144-
this.mockResolvedValue = returnValue => this.mockReturnValue(Promise.resolve(returnValue))
145-
this.mockRejectedValue = err => this.mockReturnValue(Promise.reject(err))
147+
this.defaultReturnValue = returnValue => this.defaultImplementation(() => returnValue)
148+
this.defaultResolvedValue = returnValue => this.defaultReturnValue(Promise.resolve(returnValue))
149+
this.defaultRejectedValue = err => this.defaultResolvedValue(Promise.reject(err))
150+
this.mockImplementation = this.defaultImplementation
151+
this.mockReturnValue = this.defaultReturnValue
152+
this.mockResolvedValue = this.defaultResolvedValue
153+
this.mockRejectedValue = this.defaultRejectedValue
146154

147155
this.calledWith = (...matchers) => ({ ...mockFunctions(matchers, false) })
148156
this.expectCalledWith = (...matchers) => ({ ...mockFunctions(matchers, true) })

0 commit comments

Comments
 (0)