Skip to content

Commit

Permalink
Adding async versions of mockReturnValue & mockreturnValueOnce (#5318)
Browse files Browse the repository at this point in the history
* Adding async utility versions of mockReturnValue & mockreturnValueOnce

* Fixing rejection tests

* simplify assertions

* Adding changes to CHANGELOG and Docs

* Fixing doc examples
  • Loading branch information
m4n3z40 authored and cpojer committed Jan 22, 2018
1 parent 36c705b commit 139f976
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
81 changes: 81 additions & 0 deletions docs/MockFunctionAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,84 @@ 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
test('async test', async () => {
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:

```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"
});
```
47 changes: 47 additions & 0 deletions packages/jest-mock/src/__tests__/jest_mock.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,53 @@ 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 expect(promise).resolves.toBe('abcd');
});

it('supports mocking resolvable async functions only once', () => {
const fn = moduleMocker.fn();
fn.mockResolvedValue('abcd');
fn.mockResolvedValueOnce('abcde');

return Promise.all([
expect(fn()).resolves.toBe('abcde'),
expect(fn()).resolves.toBe('abcd'),
]);
});

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 expect(promise).rejects.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);

return Promise.all([
expect(fn()).rejects.toBe(err),
expect(fn()).rejects.toBe(defaultErr),
]);
});

describe('timestamps', () => {
const RealDate = Date;

Expand Down
22 changes: 22 additions & 0 deletions packages/jest-mock/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ function getSlots(object?: Object): Array<string> {
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<Function, MockFunctionState>;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down

0 comments on commit 139f976

Please sign in to comment.