From 2dc4291e62f62fd6c2bbd07dfd9372fc66f7f2e2 Mon Sep 17 00:00:00 2001 From: Stephen Cook Date: Tue, 22 May 2018 12:22:53 +0100 Subject: [PATCH] Improve the documentation of `mockClear`, `mockReset`, and `mockRestore` (especially surrounding `spyOn` behaviour) --- CHANGELOG.md | 2 + docs/JestObjectAPI.md | 17 +- docs/MockFunctionAPI.md | 12 +- .../jest-mock/src/__tests__/jest_mock.test.js | 5 - packages/jest-mock/src/index.js | 9 +- .../version-22.4/JestObjectAPI.md | 17 +- .../version-22.4/MockFunctionAPI.md | 326 ++++++++++++++++++ 7 files changed, 357 insertions(+), 31 deletions(-) create mode 100644 website/versioned_docs/version-22.4/MockFunctionAPI.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 857c70202ca0..84caf44258f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,6 +169,8 @@ ### Chore & Maintenance +* `[docs]` Improve documentation of `mockClear`, `mockReset`, and `mockRestore` + ([#6227](https://github.com/facebook/jest/pull/6227/files)) * `[jest-cli]` Use yargs's built-in `version` instead of rolling our own ([#6215](https://github.com/facebook/jest/pull/6215)) * `[docs]` Add explanation on how to mock methods not implemented in JSDOM diff --git a/docs/JestObjectAPI.md b/docs/JestObjectAPI.md index 2d0ee6c9487d..671d39d0be11 100644 --- a/docs/JestObjectAPI.md +++ b/docs/JestObjectAPI.md @@ -301,23 +301,25 @@ Returns the `jest` object for chaining. ### `jest.clearAllMocks()` Clears the `mock.calls` and `mock.instances` properties of all mocks. Equivalent -to calling `.mockClear()` on every mocked function. +to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked +function. Returns the `jest` object for chaining. ### `jest.resetAllMocks()` -Resets the state of all mocks. Equivalent to calling `.mockReset()` on every -mocked function. +Resets the state of all mocks. Equivalent to calling +[`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. Returns the `jest` object for chaining. ### `jest.restoreAllMocks()` Restores all mocks back to their original value. Equivalent to calling -`.mockRestore` on every mocked function. Beware that `jest.restoreAllMocks()` -only works when mock was created with `jest.spyOn`; other mocks will require you -to manually restore them. +`.mockRestore` on every mocked function. Beware that +[`jest.restoreAllMocks()`](MockFunctionAPI.md#mockfnmockrestore) only works when +mock was created with `jest.spyOn`; other mocks will require you to manually +restore them. ### `jest.resetModules()` @@ -495,7 +497,6 @@ test('plays video', () => { expect(spy).toHaveBeenCalled(); expect(isPlaying).toBe(true); - spy.mockReset(); spy.mockRestore(); }); ``` @@ -544,7 +545,6 @@ test('plays video', () => { expect(spy).toHaveBeenCalled(); expect(isPlaying).toBe(true); - spy.mockReset(); spy.mockRestore(); }); @@ -557,7 +557,6 @@ test('plays audio', () => { expect(spy).toHaveBeenCalled(); expect(audio.volume).toBe(100); - spy.mockReset(); spy.mockRestore(); }); ``` diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 2fd51e5f9767..dd4e31fe484a 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -102,11 +102,12 @@ is available to clear mocks automatically between tests. ### `mockFn.mockReset()` -Resets all information stored in the mock, including any initial implementation -and mock name given. +Does everything that [`mockFn.mockClear()`](#mockfnmockclear) does, and also +removes any mocked return values. -This is useful when you want to completely restore a mock back to its initial -state. +This is useful when you want to completely restore a _mock_ back to its initial +state. (Note that resetting a _spy_ will result in a function with no return +value). Beware that `mockReset` will replace `mockFn.mock`, not just [`mockFn.mock.calls`](#mockfn-mock-calls) and @@ -116,7 +117,8 @@ don't access stale data. ### `mockFn.mockRestore()` -Removes the mock and restores the initial implementation. +Does everything that [`mockFn.mockReset()`](#mockfnmockreset) does, and also +restores the original (non-mocked) implementation. This is useful when you want to mock functions in certain test cases and restore the original implementation in others. diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index 7964bd22a235..55df23d615e0 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -782,7 +782,6 @@ describe('moduleMocker', () => { isOriginalCalled = false; originalCallThis = null; originalCallArguments = null; - spy.mockReset(); spy.mockRestore(); obj.method.call(thisArg, firstArg, secondArg); expect(isOriginalCalled).toBe(true); @@ -873,7 +872,6 @@ describe('moduleMocker', () => { isOriginalCalled = false; originalCallThis = null; originalCallArguments = null; - spy.mockReset(); spy.mockRestore(); obj.method.call(thisArg, firstArg, secondArg); expect(isOriginalCalled).toBe(true); @@ -900,7 +898,6 @@ describe('moduleMocker', () => { expect(spy).toHaveBeenCalled(); expect(obj.property).toBe(true); obj.property = false; - spy.mockReset(); spy.mockRestore(); obj.property = true; expect(spy).not.toHaveBeenCalled(); @@ -990,7 +987,6 @@ describe('moduleMocker', () => { isOriginalCalled = false; originalCallThis = null; originalCallArguments = null; - spy.mockReset(); spy.mockRestore(); obj.method.call(thisArg, firstArg, secondArg); expect(isOriginalCalled).toBe(true); @@ -1018,7 +1014,6 @@ describe('moduleMocker', () => { expect(spy).toHaveBeenCalled(); expect(obj.property).toBe(true); obj.property = false; - spy.mockReset(); spy.mockRestore(); obj.property = true; expect(spy).not.toHaveBeenCalled(); diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.js index db2e2134802f..9b679c73e4c3 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -456,11 +456,16 @@ class ModuleMockerClass { }; f.mockReset = () => { - this._mockState.delete(f); + f.mockClear(); this._mockConfigRegistry.delete(f); return f; }; + f.mockRestore = () => { + f.mockReset(); + return restore ? restore() : undefined; + }; + f.mockReturnValueOnce = value => { // next function call will return this value or default return value const mockConfig = this._ensureMockConfig(f); @@ -528,8 +533,6 @@ class ModuleMockerClass { f.mockImplementation(metadata.mockImpl); } - f.mockRestore = restore ? restore : () => {}; - return f; } else { const unknownType = metadata.type || 'undefined type'; diff --git a/website/versioned_docs/version-22.4/JestObjectAPI.md b/website/versioned_docs/version-22.4/JestObjectAPI.md index 688a60aa7ccb..3ee4c35195ce 100644 --- a/website/versioned_docs/version-22.4/JestObjectAPI.md +++ b/website/versioned_docs/version-22.4/JestObjectAPI.md @@ -302,23 +302,25 @@ Returns the `jest` object for chaining. ### `jest.clearAllMocks()` Clears the `mock.calls` and `mock.instances` properties of all mocks. Equivalent -to calling `.mockClear()` on every mocked function. +to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked +function. Returns the `jest` object for chaining. ### `jest.resetAllMocks()` -Resets the state of all mocks. Equivalent to calling `.mockReset()` on every -mocked function. +Resets the state of all mocks. Equivalent to calling +[`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. Returns the `jest` object for chaining. ### `jest.restoreAllMocks()` Restores all mocks back to their original value. Equivalent to calling -`.mockRestore` on every mocked function. Beware that `jest.restoreAllMocks()` -only works when mock was created with `jest.spyOn`; other mocks will require you -to manually restore them. +`.mockRestore` on every mocked function. Beware that +[`jest.restoreAllMocks()`](MockFunctionAPI.md#mockfnmockrestore) only works when +mock was created with `jest.spyOn`; other mocks will require you to manually +restore them. ### `jest.resetModules()` @@ -496,7 +498,6 @@ test('plays video', () => { expect(spy).toHaveBeenCalled(); expect(isPlaying).toBe(true); - spy.mockReset(); spy.mockRestore(); }); ``` @@ -545,7 +546,6 @@ test('plays video', () => { expect(spy).toHaveBeenCalled(); expect(isPlaying).toBe(true); - spy.mockReset(); spy.mockRestore(); }); @@ -556,7 +556,6 @@ test('plays audio', () => { expect(spy).toHaveBeenCalled(); expect(video.volume).toBe(100); - spy.mockReset(); spy.mockRestore(); }); ``` diff --git a/website/versioned_docs/version-22.4/MockFunctionAPI.md b/website/versioned_docs/version-22.4/MockFunctionAPI.md new file mode 100644 index 000000000000..173b0f8e93ab --- /dev/null +++ b/website/versioned_docs/version-22.4/MockFunctionAPI.md @@ -0,0 +1,326 @@ +--- +id: version-22.4-mock-function-api +title: Mock Functions +original_id: mock-function-api +--- + +Mock functions are also known as "spies", because they let you spy on the +behavior of a function that is called indirectly by some other code, rather than +just testing the output. You can create a mock function with `jest.fn()`. If no +implementation is given, the mock function will return `undefined` when invoked. + +## Methods + + + +--- + +## Reference + +### `mockFn.getMockName()` + +Returns the mock name string set by calling `mockFn.mockName(value)`. + +### `mockFn.mock.calls` + +An array that represents all calls that have been made into this mock function. +Each call is represented by an array of arguments that were passed during the +call. + +For example: A mock function `f` that has been called twice, with the arguments +`f('arg1', 'arg2')`, and then with the arguments `f('arg3', 'arg4')` would have +a `mock.calls` array that looks like this: + +```js +[['arg1', 'arg2'], ['arg3', 'arg4']]; +``` + +### `mockFn.mock.instances` + +An array that contains all the object instances that have been instantiated from +this mock function using `new`. + +For example: A mock function that has been instantiated twice would have the +following `mock.instances` array: + +```js +const mockFn = jest.fn(); + +const a = new mockFn(); +const b = new mockFn(); + +mockFn.mock.instances[0] === a; // true +mockFn.mock.instances[1] === b; // true +``` + +### `mockFn.mockClear()` + +Resets all information stored in the [`mockFn.mock.calls`](#mockfn-mock-calls) +and [`mockFn.mock.instances`](#mockfn-mock-instances) arrays. + +Often this is useful when you want to clean up a mock's usage data between two +assertions. + +Beware that `mockClear` will replace `mockFn.mock`, not just +[`mockFn.mock.calls`](#mockfn-mock-calls) and +[`mockFn.mock.instances`](#mockfn-mock-instances). You should therefore avoid +assigning `mockFn.mock` to other variables, temporary or not, to make sure you +don't access stale data. + +The [`clearMocks`](configuration.html#clearmocks-boolean) configuration option +is available to clear mocks automatically between tests. + +### `mockFn.mockReset()` + +Does everything that [`mockFn.mockClear()`](#mockfnmockclear) does, and also +removes any mocked return values. + +This is useful when you want to completely restore a _mock_ back to its initial +state. (Note that resetting a _spy_ will result in a function with no return +value). + +Beware that `mockReset` will replace `mockFn.mock`, not just +[`mockFn.mock.calls`](#mockfn-mock-calls) and +[`mockFn.mock.instances`](#mockfn-mock-instances). You should therefore avoid +assigning `mockFn.mock` to other variables, temporary or not, to make sure you +don't access stale data. + +### `mockFn.mockRestore()` + +Does everything that [`mockFn.mockReset()`](#mockfnmockreset) does, and also +restores the original (non-mocked) implementation. + +This is useful when you want to mock functions in certain test cases and restore +the original implementation in others. + +Beware that `mockFn.mockRestore` only works when mock was created with +`jest.spyOn`. Thus you have to take care of restoration yourself when manually +assigning `jest.fn()`. + +The [`restoreMocks`](configuration.html#restoremocks-boolean) configuration +option is available to restore mocks automatically between tests. + +### `mockFn.mockImplementation(fn)` + +Accepts a function that should be used as the implementation of the mock. The +mock itself will still record all calls that go into and instances that come +from itself – the only difference is that the implementation will also be +executed when the mock is called. + +_Note: `jest.fn(implementation)` is a shorthand for +`jest.fn().mockImplementation(implementation)`._ + +For example: + +```js +const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar); +// or: jest.fn(scalar => 42 + scalar); + +const a = mockFn(0); +const b = mockFn(1); + +a === 42; // true +b === 43; // true + +mockFn.mock.calls[0][0] === 0; // true +mockFn.mock.calls[1][0] === 1; // true +``` + +`mockImplementation` can also be used to mock class constructors: + +```js +// SomeClass.js +module.exports = class SomeClass { + m(a, b) {} +}; + +// OtherModule.test.js +jest.mock('./SomeClass'); // this happens automatically with automocking +const SomeClass = require('./SomeClass'); +const mMock = jest.fn(); +SomeClass.mockImplementation(() => { + return { + m: mMock, + }; +}); + +const some = new SomeClass(); +some.m('a', 'b'); +console.log('Calls to m: ', mMock.mock.calls); +``` + +### `mockFn.mockImplementationOnce(fn)` + +Accepts a function that will be used as an implementation of the mock for one +call to the mocked function. Can be chained so that multiple function calls +produce different results. + +```js +const myMockFn = jest + .fn() + .mockImplementationOnce(cb => cb(null, true)) + .mockImplementationOnce(cb => cb(null, false)); + +myMockFn((err, val) => console.log(val)); // true + +myMockFn((err, val) => console.log(val)); // false +``` + +When the mocked function runs out of implementations defined with +mockImplementationOnce, it will execute the default implementation set with +`jest.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if +they were called: + +```js +const myMockFn = jest + .fn(() => 'default') + .mockImplementationOnce(() => 'first call') + .mockImplementationOnce(() => 'second call'); + +// 'first call', 'second call', 'default', 'default' +console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); +``` + +### `mockFn.mockName(value)` + +Accepts a string to use in test result output in place of "jest.fn()" to +indicate which mock function is being referenced. + +For example: + +```js +const mockFn = jest.fn().mockName('mockedFunction'); +// mockFn(); +expect(mockFn).toHaveBeenCalled(); +``` + +Will result in this error: + +```bash + expect(mockedFunction).toHaveBeenCalled() + + Expected mock function to have been called. +``` + +### `mockFn.mockReturnThis()` + +Just a simple sugar function for: + +```js +jest.fn(function() { + return this; +}); +``` + +### `mockFn.mockReturnValue(value)` + +Accepts a value that will be returned whenever the mock function is called. + +```js +const mock = jest.fn(); +mock.mockReturnValue(42); +mock(); // 42 +mock.mockReturnValue(43); +mock(); // 43 +``` + +### `mockFn.mockReturnValueOnce(value)` + +Accepts a value that will be returned for one call to the mock function. Can be +chained so that successive calls to the mock function return different values. +When there are no more `mockReturnValueOnce` values to use, calls will return a +value specified by `mockReturnValue`. + +```js +const myMockFn = jest + .fn() + .mockReturnValue('default') + .mockReturnValueOnce('first call') + .mockReturnValueOnce('second call'); + +// 'first call', 'second call', 'default', 'default' +console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); +``` + +### `mockFn.mockResolvedValue(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValue(Promise.resolve(value)); +``` + +Useful to mock async functions in async tests: + +```js +test('async test', async () => { + const asyncMock = jest.fn().mockResolvedValue(43); + + await asyncMock(); // 43 +}); +``` + +### `mockFn.mockResolvedValueOnce(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValueOnce(Promise.resolve(value)); +``` + +Useful to resolve different values over multiple async calls: + +```js +test('async test', async () => { + const asyncMock = jest + .fn() + .mockResolvedValue('default') + .mockResolvedValueOnce('first call') + .mockResolvedValueOnce('second call'); + + await asyncMock(); // first call + await asyncMock(); // second call + await asyncMock(); // default + await asyncMock(); // default +}); +``` + +### `mockFn.mockRejectedValue(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValue(Promise.reject(value)); +``` + +Useful to create async mock functions that will always reject: + +```js +test('async test', async () => { + const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); + + await asyncMock(); // throws "Async error" +}); +``` + +### `mockFn.mockRejectedValueOnce(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValueOnce(Promise.reject(value)); +``` + +Example usage: + +```js +test('async test', async () => { + const asyncMock = jest + .fn() + .mockResolvedValueOnce('first call') + .mockRejectedValueOnce(new Error('Async error')); + + await asyncMock(); // first call + await asyncMock(); // throws "Async error" +}); +```