Skip to content

Commit

Permalink
Add toReturnValues and toHaveReturnedValues
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii committed Mar 27, 2018
1 parent abb6321 commit 7a3cf01
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 0 deletions.
16 changes: 16 additions & 0 deletions docs/ExpectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,22 @@ test('drinkEach drinks each drink', () => {
});
```

### `.toHaveReturned(value)`

Also under the alias: `.toReturn(value)`

If you have a mock function, you can use `.toHaveReturned` to test that the spy
returned a value. For example, let's say you have mock `drink` that returns
`true`. You can write:

```js
test('drinks returns true', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned(true);
});
```

### `.toBeCloseTo(number, numDigits)`

Using exact equality with floating point numbers is a bad idea. Rounding means
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1059,3 +1059,75 @@ exports[`toHaveBeenNthCalledWith works with trailing undefined arguments 1`] = `
Expected mock function first call to have been called with:
Did not expect argument 2 but it was called with <red>undefined</>."
`;

exports[`toHaveReturned .not passes when called 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).toHaveReturned(</><green>expected</><dim>)</>

Expected mock function to have returned:
<green>\\"Some Other Value\\"</>"
`;

exports[`toHaveReturned .not passes when called with no arguments 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).toHaveReturned(</><green>expected</><dim>)</>

Expected mock function to have returned:
<green>undefined</>"
`;

exports[`toHaveReturned passes when called 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).not.toHaveReturned(</><green>expected</><dim>)</>

Expected mock function not to have returned:
<green>\\"Return Value\\"</>"
`;

exports[`toHaveReturned passes with no arguments 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).not.toHaveReturned(</><green>expected</><dim>)</>

Expected mock function not to have returned:
<green>undefined</>"
`;

exports[`toHaveReturned works only on spies or jest.fn 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>)[.not].toHaveReturned(</><dim>)</>

<red>jest.fn()</> value must be a mock function or spy.
Received:
function: <red>[Function fn]</>"
`;

exports[`toReturn .not passes when called 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).toReturn(</><green>expected</><dim>)</>

Expected mock function to have returned:
<green>\\"Some Other Value\\"</>"
`;

exports[`toReturn .not passes when called with no arguments 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).toReturn(</><green>expected</><dim>)</>

Expected mock function to have returned:
<green>undefined</>"
`;

exports[`toReturn passes when called 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).not.toReturn(</><green>expected</><dim>)</>

Expected mock function not to have returned:
<green>\\"Return Value\\"</>"
`;

exports[`toReturn passes with no arguments 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>).not.toReturn(</><green>expected</><dim>)</>

Expected mock function not to have returned:
<green>undefined</>"
`;

exports[`toReturn works only on spies or jest.fn 1`] = `
"<dim>expect(</><red>jest.fn()</><dim>)[.not].toReturn(</><dim>)</>

<red>jest.fn()</> value must be a mock function or spy.
Received:
function: <red>[Function fn]</>"
`;
67 changes: 67 additions & 0 deletions packages/expect/src/__tests__/spy_matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,70 @@ const jestExpect = require('../');
}
});
});

['toReturn', 'toHaveReturned'].forEach(called => {
describe(`${called}`, () => {
test(`works only on spies or jest.fn`, () => {
const fn = function fn() {};

expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot();
});

test(`passes with no arguments`, () => {
const fn = jest.fn();
fn();
jestExpect(fn)[called]();
expect(() => jestExpect(fn).not[called]()).toThrowErrorMatchingSnapshot();
});

test(`.not passes when called with no arguments`, () => {
const fn = jest.fn(() => 'Return Value');

fn();
jestExpect(fn).not[called]();
expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot();
});

test(`passes when called`, () => {
const fn = jest.fn(() => 'Return Value');
fn();
jestExpect(fn)[called]('Return Value');
expect(() =>
jestExpect(fn).not[called]('Return Value'),
).toThrowErrorMatchingSnapshot();
});

test(`.not passes when called`, () => {
const fn = jest.fn(() => 'Return Value');
fn();
jestExpect(fn).not[called]('Some Other Value');
expect(() =>
jestExpect(fn)[called]('Some Other Value'),
).toThrowErrorMatchingSnapshot();
});

test(`passes with mutliple calls`, () => {
const fn = jest.fn(a => a * 2);

fn(1);
fn(2);
fn(3);

jestExpect(fn)[called](2);
jestExpect(fn)[called](4);
jestExpect(fn)[called](6);
});

test(`.not passes with multiple calls`, () => {
const fn = jest.fn(a => a * 2);

fn(1);
fn(2);
fn(3);

jestExpect(fn).not[called](1);
jestExpect(fn).not[called](3);
jestExpect(fn).not[called](5);
});
});
});
34 changes: 34 additions & 0 deletions packages/expect/src/spy_matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@ const createToBeCalledWithMatcher = matcherName => (
return {message, pass};
};

const createToReturnValuesMatcher = matcherName => (
received: any,
expected: any,
) => {
ensureMock(received, matcherName);

const receivedIsSpy = isSpy(received);
const type = receivedIsSpy ? 'spy' : 'mock function';
const receivedName = receivedIsSpy ? 'spy' : received.getMockName();

const calls = receivedIsSpy
? received.returnValues.all().map(x => x.args)
: received.mock.returnValues;

const [match] = partition(calls, call => equals(expected, call));
const pass = match.length > 0;

const message = pass
? () =>
matcherHint('.not' + matcherName, receivedName) +
'\n\n' +
`Expected ${type} not to have returned:\n` +
` ${printExpected(expected)}`
: () =>
matcherHint(matcherName, receivedName) +
'\n\n' +
`Expected ${type} to have returned:\n` +
` ${printExpected(expected)}`;

return {message, pass};
};

const createLastCalledWithMatcher = matcherName => (
received: any,
...expected: any
Expand Down Expand Up @@ -206,6 +238,8 @@ const spyMatchers: MatchersObject = {
toHaveBeenNthCalledWith: createNthCalledWithMatcher(
'.toHaveBeenNthCalledWith',
),
toHaveReturned: createToReturnValuesMatcher('.toHaveReturned'),
toReturn: createToReturnValuesMatcher('.toReturn'),
};

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

0 comments on commit 7a3cf01

Please sign in to comment.