Skip to content

Commit 7a3cf01

Browse files
committed
Add toReturnValues and toHaveReturnedValues
1 parent abb6321 commit 7a3cf01

File tree

4 files changed

+189
-0
lines changed

4 files changed

+189
-0
lines changed

docs/ExpectAPI.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,22 @@ test('drinkEach drinks each drink', () => {
649649
});
650650
```
651651

652+
### `.toHaveReturned(value)`
653+
654+
Also under the alias: `.toReturn(value)`
655+
656+
If you have a mock function, you can use `.toHaveReturned` to test that the spy
657+
returned a value. For example, let's say you have mock `drink` that returns
658+
`true`. You can write:
659+
660+
```js
661+
test('drinks returns true', () => {
662+
const drink = jest.fn(() => true);
663+
drink();
664+
expect(drink).toHaveReturned(true);
665+
});
666+
```
667+
652668
### `.toBeCloseTo(number, numDigits)`
653669

654670
Using exact equality with floating point numbers is a bad idea. Rounding means

packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,3 +1059,75 @@ exports[`toHaveBeenNthCalledWith works with trailing undefined arguments 1`] = `
10591059
Expected mock function first call to have been called with:
10601060
Did not expect argument 2 but it was called with <red>undefined</>."
10611061
`;
1062+
1063+
exports[`toHaveReturned .not passes when called 1`] = `
1064+
"<dim>expect(</><red>jest.fn()</><dim>).toHaveReturned(</><green>expected</><dim>)</>
1065+
1066+
Expected mock function to have returned:
1067+
<green>\\"Some Other Value\\"</>"
1068+
`;
1069+
1070+
exports[`toHaveReturned .not passes when called with no arguments 1`] = `
1071+
"<dim>expect(</><red>jest.fn()</><dim>).toHaveReturned(</><green>expected</><dim>)</>
1072+
1073+
Expected mock function to have returned:
1074+
<green>undefined</>"
1075+
`;
1076+
1077+
exports[`toHaveReturned passes when called 1`] = `
1078+
"<dim>expect(</><red>jest.fn()</><dim>).not.toHaveReturned(</><green>expected</><dim>)</>
1079+
1080+
Expected mock function not to have returned:
1081+
<green>\\"Return Value\\"</>"
1082+
`;
1083+
1084+
exports[`toHaveReturned passes with no arguments 1`] = `
1085+
"<dim>expect(</><red>jest.fn()</><dim>).not.toHaveReturned(</><green>expected</><dim>)</>
1086+
1087+
Expected mock function not to have returned:
1088+
<green>undefined</>"
1089+
`;
1090+
1091+
exports[`toHaveReturned works only on spies or jest.fn 1`] = `
1092+
"<dim>expect(</><red>jest.fn()</><dim>)[.not].toHaveReturned(</><dim>)</>
1093+
1094+
<red>jest.fn()</> value must be a mock function or spy.
1095+
Received:
1096+
function: <red>[Function fn]</>"
1097+
`;
1098+
1099+
exports[`toReturn .not passes when called 1`] = `
1100+
"<dim>expect(</><red>jest.fn()</><dim>).toReturn(</><green>expected</><dim>)</>
1101+
1102+
Expected mock function to have returned:
1103+
<green>\\"Some Other Value\\"</>"
1104+
`;
1105+
1106+
exports[`toReturn .not passes when called with no arguments 1`] = `
1107+
"<dim>expect(</><red>jest.fn()</><dim>).toReturn(</><green>expected</><dim>)</>
1108+
1109+
Expected mock function to have returned:
1110+
<green>undefined</>"
1111+
`;
1112+
1113+
exports[`toReturn passes when called 1`] = `
1114+
"<dim>expect(</><red>jest.fn()</><dim>).not.toReturn(</><green>expected</><dim>)</>
1115+
1116+
Expected mock function not to have returned:
1117+
<green>\\"Return Value\\"</>"
1118+
`;
1119+
1120+
exports[`toReturn passes with no arguments 1`] = `
1121+
"<dim>expect(</><red>jest.fn()</><dim>).not.toReturn(</><green>expected</><dim>)</>
1122+
1123+
Expected mock function not to have returned:
1124+
<green>undefined</>"
1125+
`;
1126+
1127+
exports[`toReturn works only on spies or jest.fn 1`] = `
1128+
"<dim>expect(</><red>jest.fn()</><dim>)[.not].toReturn(</><dim>)</>
1129+
1130+
<red>jest.fn()</> value must be a mock function or spy.
1131+
Received:
1132+
function: <red>[Function fn]</>"
1133+
`;

packages/expect/src/__tests__/spy_matchers.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,70 @@ const jestExpect = require('../');
342342
}
343343
});
344344
});
345+
346+
['toReturn', 'toHaveReturned'].forEach(called => {
347+
describe(`${called}`, () => {
348+
test(`works only on spies or jest.fn`, () => {
349+
const fn = function fn() {};
350+
351+
expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot();
352+
});
353+
354+
test(`passes with no arguments`, () => {
355+
const fn = jest.fn();
356+
fn();
357+
jestExpect(fn)[called]();
358+
expect(() => jestExpect(fn).not[called]()).toThrowErrorMatchingSnapshot();
359+
});
360+
361+
test(`.not passes when called with no arguments`, () => {
362+
const fn = jest.fn(() => 'Return Value');
363+
364+
fn();
365+
jestExpect(fn).not[called]();
366+
expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot();
367+
});
368+
369+
test(`passes when called`, () => {
370+
const fn = jest.fn(() => 'Return Value');
371+
fn();
372+
jestExpect(fn)[called]('Return Value');
373+
expect(() =>
374+
jestExpect(fn).not[called]('Return Value'),
375+
).toThrowErrorMatchingSnapshot();
376+
});
377+
378+
test(`.not passes when called`, () => {
379+
const fn = jest.fn(() => 'Return Value');
380+
fn();
381+
jestExpect(fn).not[called]('Some Other Value');
382+
expect(() =>
383+
jestExpect(fn)[called]('Some Other Value'),
384+
).toThrowErrorMatchingSnapshot();
385+
});
386+
387+
test(`passes with mutliple calls`, () => {
388+
const fn = jest.fn(a => a * 2);
389+
390+
fn(1);
391+
fn(2);
392+
fn(3);
393+
394+
jestExpect(fn)[called](2);
395+
jestExpect(fn)[called](4);
396+
jestExpect(fn)[called](6);
397+
});
398+
399+
test(`.not passes with multiple calls`, () => {
400+
const fn = jest.fn(a => a * 2);
401+
402+
fn(1);
403+
fn(2);
404+
fn(3);
405+
406+
jestExpect(fn).not[called](1);
407+
jestExpect(fn).not[called](3);
408+
jestExpect(fn).not[called](5);
409+
});
410+
});
411+
});

packages/expect/src/spy_matchers.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,38 @@ const createToBeCalledWithMatcher = matcherName => (
118118
return {message, pass};
119119
};
120120

121+
const createToReturnValuesMatcher = matcherName => (
122+
received: any,
123+
expected: any,
124+
) => {
125+
ensureMock(received, matcherName);
126+
127+
const receivedIsSpy = isSpy(received);
128+
const type = receivedIsSpy ? 'spy' : 'mock function';
129+
const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
130+
131+
const calls = receivedIsSpy
132+
? received.returnValues.all().map(x => x.args)
133+
: received.mock.returnValues;
134+
135+
const [match] = partition(calls, call => equals(expected, call));
136+
const pass = match.length > 0;
137+
138+
const message = pass
139+
? () =>
140+
matcherHint('.not' + matcherName, receivedName) +
141+
'\n\n' +
142+
`Expected ${type} not to have returned:\n` +
143+
` ${printExpected(expected)}`
144+
: () =>
145+
matcherHint(matcherName, receivedName) +
146+
'\n\n' +
147+
`Expected ${type} to have returned:\n` +
148+
` ${printExpected(expected)}`;
149+
150+
return {message, pass};
151+
};
152+
121153
const createLastCalledWithMatcher = matcherName => (
122154
received: any,
123155
...expected: any
@@ -206,6 +238,8 @@ const spyMatchers: MatchersObject = {
206238
toHaveBeenNthCalledWith: createNthCalledWithMatcher(
207239
'.toHaveBeenNthCalledWith',
208240
),
241+
toHaveReturned: createToReturnValuesMatcher('.toHaveReturned'),
242+
toReturn: createToReturnValuesMatcher('.toReturn'),
209243
};
210244

211245
const isSpy = spy => spy.calls && typeof spy.calls.count === 'function';

0 commit comments

Comments
 (0)