From 0ac07e4d279a013d85acf958c5c03977c072d86e Mon Sep 17 00:00:00 2001 From: Allan Baptista Date: Mon, 15 Jan 2018 14:49:32 -0200 Subject: [PATCH 1/5] Adding async utility versions of mockReturnValue & mockreturnValueOnce --- .../jest-mock/src/__tests__/jest_mock.test.js | 49 +++++++++++++++++++ packages/jest-mock/src/index.js | 22 +++++++++ 2 files changed, 71 insertions(+) diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index d298cba993de..a44b321c8c1c 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -391,6 +391,55 @@ describe('moduleMocker', () => { expect(fake(2)).toEqual(4); }); + it('supports mocking resolvable async functions', () => { + const fn = moduleMocker.fn(); + fn.mockResolvedValue('abcd'); + + const promise = fn(); + + expect(promise).toBeInstanceOf(Promise); + + return promise.then(value => expect(value).toBe('abcd')); + }); + + it('supports mocking resolvable async functions only once', () => { + const fn = moduleMocker.fn(); + fn.mockResolvedValue('abcd'); + fn.mockResolvedValueOnce('abcde'); + + const promise1 = fn().then(value => expect(value).toBe('abcde')); + const promise2 = fn().then(value => expect(value).toBe('abcd')); + + return Promise.all([promise1, promise2]); + }); + + it('supports mocking rejectable async functions', () => { + const err = new Error('rejected'); + const fn = moduleMocker.fn(); + fn.mockRejectedValue(err); + + const promise = fn(); + + expect(promise).toBeInstanceOf(Promise); + + return promise.catch(rejection => expect(rejection).toBe(err)); + }); + + it('supports mocking rejectable async functions only once', () => { + const defaultErr = new Error('default rejected'); + const err = new Error('rejected'); + const fn = moduleMocker.fn(); + fn.mockRejectedValue(defaultErr); + fn.mockRejectedValueOnce(err); + + const promise1 = fn().catch(rejection => expect(rejection).toBe(err)); + const promise2 = fn().catch(rejection => + expect(rejection).toBe(defaultErr), + ); + + return Promise.all([promise1, promise2]); + }); + describe('timestamps', () => { const RealDate = Date; diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.js index 7490110d5de1..7d658ee5dab2 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -227,6 +227,17 @@ function getSlots(object?: Object): Array { return Object.keys(slots); } +function wrapAsyncParam( + fn: any => any, + asyncAction: 'resolve' | 'reject', +): any => any { + if (asyncAction === 'reject') { + return value => fn(Promise.reject(value)); + } + + return value => fn(Promise.resolve(value)); +} + class ModuleMockerClass { _environmentGlobal: Global; _mockState: WeakMap; @@ -407,6 +418,13 @@ class ModuleMockerClass { return f; }; + f.mockResolvedValueOnce = wrapAsyncParam( + f.mockReturnValueOnce, + 'resolve', + ); + + f.mockRejectedValueOnce = wrapAsyncParam(f.mockReturnValueOnce, 'reject'); + f.mockReturnValue = value => { // next function call will return specified return value or this one const mockConfig = this._ensureMockConfig(f); @@ -415,6 +433,10 @@ class ModuleMockerClass { return f; }; + f.mockResolvedValue = wrapAsyncParam(f.mockReturnValue, 'resolve'); + + f.mockRejectedValue = wrapAsyncParam(f.mockReturnValue, 'reject'); + f.mockImplementationOnce = fn => { // next function call will use this mock implementation return value // or default mock implementation return value From 03284b0a5f00cd77bb41ff56d2dfb19c873112ef Mon Sep 17 00:00:00 2001 From: Allan Baptista Date: Mon, 15 Jan 2018 15:19:26 -0200 Subject: [PATCH 2/5] Fixing rejection tests --- packages/jest-mock/src/__tests__/jest_mock.test.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index a44b321c8c1c..7f14c9e8953e 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -422,7 +422,9 @@ describe('moduleMocker', () => { expect(promise).toBeInstanceOf(Promise); - return promise.catch(rejection => expect(rejection).toBe(err)); + return promise + .then(() => Promise.reject(new Error('did not reject'))) + .catch(rejection => expect(rejection).toBe(err)); }); it('supports mocking rejectable async functions only once', () => { @@ -432,10 +434,12 @@ describe('moduleMocker', () => { fn.mockRejectedValue(defaultErr); fn.mockRejectedValueOnce(err); - const promise1 = fn().catch(rejection => expect(rejection).toBe(err)); - const promise2 = fn().catch(rejection => - expect(rejection).toBe(defaultErr), - ); + const promise1 = fn() + .then(() => Promise.reject(new Error('did not reject'))) + .catch(rejection => expect(rejection).toBe(err)); + const promise2 = fn() + .then(() => Promise.reject(new Error('did not reject'))) + .catch(rejection => expect(rejection).toBe(defaultErr)); return Promise.all([promise1, promise2]); }); From a54b9e175456ee56bd5ee01b3574b1b6371ccbb6 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 21 Jan 2018 11:12:49 +0100 Subject: [PATCH 3/5] simplify assertions --- .../jest-mock/src/__tests__/jest_mock.test.js | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index 7f14c9e8953e..ee692bc7efc2 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -399,7 +399,7 @@ describe('moduleMocker', () => { expect(promise).toBeInstanceOf(Promise); - return promise.then(value => expect(value).toBe('abcd')); + return expect(promise).resolves.toBe('abcd'); }); it('supports mocking resolvable async functions only once', () => { @@ -407,10 +407,10 @@ describe('moduleMocker', () => { fn.mockResolvedValue('abcd'); fn.mockResolvedValueOnce('abcde'); - const promise1 = fn().then(value => expect(value).toBe('abcde')); - const promise2 = fn().then(value => expect(value).toBe('abcd')); - - return Promise.all([promise1, promise2]); + return Promise.all([ + expect(fn()).resolves.toBe('abcde'), + expect(fn()).resolves.toBe('abcd'), + ]); }); it('supports mocking rejectable async functions', () => { @@ -422,9 +422,7 @@ describe('moduleMocker', () => { expect(promise).toBeInstanceOf(Promise); - return promise - .then(() => Promise.reject(new Error('did not reject'))) - .catch(rejection => expect(rejection).toBe(err)); + return expect(promise).rejects.toBe(err); }); it('supports mocking rejectable async functions only once', () => { @@ -434,14 +432,10 @@ describe('moduleMocker', () => { fn.mockRejectedValue(defaultErr); fn.mockRejectedValueOnce(err); - const promise1 = fn() - .then(() => Promise.reject(new Error('did not reject'))) - .catch(rejection => expect(rejection).toBe(err)); - const promise2 = fn() - .then(() => Promise.reject(new Error('did not reject'))) - .catch(rejection => expect(rejection).toBe(defaultErr)); - - return Promise.all([promise1, promise2]); + return Promise.all([ + expect(fn()).rejects.toBe(err), + expect(fn()).rejects.toBe(defaultErr), + ]); }); describe('timestamps', () => { From 35ae98a09bfe5ce643b7e51b33c57fc8d63aa1e1 Mon Sep 17 00:00:00 2001 From: Allan Baptista Date: Mon, 22 Jan 2018 12:54:31 -0200 Subject: [PATCH 4/5] Adding changes to CHANGELOG and Docs --- CHANGELOG.md | 5 +++ docs/MockFunctionAPI.md | 73 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f88c7a836577..1ea43efcfcf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## master +### features + +* `[jest-mock]` Add util methods to create async functions. + ([#5318](https://github.com/facebook/jest/pull/5318)) + ### Fixes * `[jest]` Add `import-local` to `jest` package. diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 2600505e7d0d..2a0992233ef1 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -241,3 +241,76 @@ const myMockFn = jest.fn() console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); > 'first call', 'second call', 'default', 'default' ``` + +### `mockFn.mockResolvedValue(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValue(Promise.resolve(value)); +``` + +Useful to mock async functions in async tests: + +```js +const asyncMock = jest.fn().mockResolvedValue(43); + +await asyncMock(); // 43 +``` + +### `mockFn.mockRejectedValueOnce(value)` + +Simple sugar function for: + +```js +jest.fn().mockReturnValueOnce(Promise.resolve(value)); +``` + +Useful to resolve different values over multiple async calls: + +``` +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 +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 +const asyncMock = jest.fn() + .mockResolvedValueOnce('first call') + .mockRejectedValueOnce(new Error('Async error')); + +await asyncMock(); // first call +await asyncMock(); // throws "Async error" +``` From 98ed094191abcde9e5312416fa4876aedfaf516b Mon Sep 17 00:00:00 2001 From: Allan Baptista Date: Mon, 22 Jan 2018 13:06:20 -0200 Subject: [PATCH 5/5] Fixing doc examples --- docs/MockFunctionAPI.md | 52 ++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 2a0992233ef1..249ef01f5a75 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -253,9 +253,11 @@ jest.fn().mockReturnValue(Promise.resolve(value)); Useful to mock async functions in async tests: ```js -const asyncMock = jest.fn().mockResolvedValue(43); - -await asyncMock(); // 43 +test('async test', async () => { + const asyncMock = jest.fn().mockResolvedValue(43); + + await asyncMock(); // 43 +}); ``` ### `mockFn.mockRejectedValueOnce(value)` @@ -268,16 +270,18 @@ jest.fn().mockReturnValueOnce(Promise.resolve(value)); Useful to resolve different values over multiple async calls: -``` -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 +```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)` @@ -291,9 +295,11 @@ jest.fn().mockReturnValue(Promise.reject(value)); Useful to create async mock functions that will always reject: ```js -const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); - -await asyncMock(); // throws "Async error" +test('async test', async () => { + const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); + + await asyncMock(); // throws "Async error" +}); ``` ### `mockFn.mockRejectedValueOnce(value)` @@ -307,10 +313,12 @@ jest.fn().mockReturnValueOnce(Promise.reject(value)); Example usage: ```js -const asyncMock = jest.fn() - .mockResolvedValueOnce('first call') - .mockRejectedValueOnce(new Error('Async error')); - -await asyncMock(); // first call -await asyncMock(); // throws "Async error" +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" +}); ```