jest-when
An extended, sugary way to mock return values for specific arguments only
Many thanks to @jonasholtkamp. He forked this repo when I was inactive and stewarded several key features and bug fixes!
Features
jest-when
allows you to use a set of the original
Jest mock functions in order to train
your mocks only based on parameters your mocked function is called with.
An example statement would be as follows:
when(fn).calledWith(1).mockReturnValue('yay!')
The trained mock function fn
will now behave as follows -- assumed no other trainings took place:
- return
yay!
if called with 1
as first parameter - return
undefined
if called with any other first parameter than 1
For extended usage see the examples below.
The supported set of mock functions is:
mockReturnValue
mockReturnValueOnce
mockResolvedValue
mockResolvedValueOnce
mockRejectedValue
mockRejectedValueOnce
mockImplementation
mockImplementationOnce
Usage
Installation
npm i --save-dev jest-when
Basic usage:
import { when } from 'jest-when'
const fn = jest.fn()
when(fn).calledWith(1).mockReturnValue('yay!')
expect(fn(1)).toEqual('yay!')
Supports chaining of mock trainings:
when(fn)
.calledWith(1).mockReturnValue('yay!')
.calledWith(2).mockReturnValue('nay!')
expect(fn(1)).toEqual('yay!')
expect(fn(2)).toEqual('nay!')
Thanks to @fkloes.
when(fn)
.calledWith(1)
.mockReturnValueOnce('yay!')
.mockReturnValue('nay!')
expect(fn(1)).toEqual('yay!')
expect(fn(1)).toEqual('nay!')
Thanks to @danielhusar.
Supports replacement of mock trainings:
when(fn).calledWith(1).mockReturnValue('yay!')
expect(fn(1)).toEqual('yay!')
when(fn).calledWith(1).mockReturnValue('nay!')
expect(fn(1)).toEqual('nay!')
This replacement of the training does only happen for mock functions not ending in *Once
.
Trainings like mockReturnValueOnce
are removed after a matching function call anyway.
Thanks to @fkloes.
Supports multiple args with partial argument matching:
when(fn).calledWith(1, true).mockReturnValue('yay!')
expect(fn(1, true)).toEqual('yay!')
expect(fn(1, true, 'foo')).toEqual('yay!')
Supports training for single calls
when(fn).calledWith(1, true, 'foo').mockReturnValueOnce('yay!')
when(fn).calledWith(1, true, 'foo').mockReturnValueOnce('nay!')
expect(fn(1, true, 'foo')).toEqual('yay!')
expect(fn(1, true, 'foo')).toEqual('nay!')
expect(fn(1, true, 'foo')).toBeUndefined()
Supports Promises, both resolved and rejected
when(fn).calledWith(1).mockResolvedValue('yay!')
when(fn).calledWith(2).mockResolvedValueOnce('nay!')
await expect(fn(1)).resolves.toEqual('yay!')
await expect(fn(1)).resolves.toEqual('yay!')
await expect(fn(2)).resolves.toEqual('nay!')
expect(await fn(2)).toBeUndefined()
when(fn).calledWith(3).mockRejectedValue(new Error('oh no!'))
when(fn).calledWith(4).mockRejectedValueOnce(new Error('oh no, an error again!'))
await expect(fn(3)).rejects.toThrow('oh no!')
await expect(fn(3)).rejects.toThrow('oh no!')
await expect(fn(4)).rejects.toThrow('oh no, an error again!')
expect(await fn(4)).toBeUndefined()
Supports jest.spyOn:
const theSpiedMethod = jest.spyOn(theInstance, 'theMethod');
when(theSpiedMethod)
.calledWith(1)
.mockReturnValue('mock');
const returnValue = theInstance.theMethod(1);
expect(returnValue).toBe('mock');
Supports jest matchers:
when(fn).calledWith(
expect.anything(),
expect.any(Number),
expect.arrayContaining(false)
).mockReturnValue('yay!')
const result = fn('whatever', 100, [true, false])
expect(result).toEqual('yay!')
Supports compound declarations:
when(fn).calledWith(1).mockReturnValue('no')
when(fn).calledWith(2).mockReturnValue('way?')
when(fn).calledWith(3).mockReturnValue('yes')
when(fn).calledWith(4).mockReturnValue('way!')
expect(fn(1)).toEqual('no')
expect(fn(2)).toEqual('way?')
expect(fn(3)).toEqual('yes')
expect(fn(4)).toEqual('way!')
expect(fn(5)).toEqual(undefined)
Assert the args:
Use expectCalledWith
instead to run an assertion that the fn
was called with the provided
args. Your test will fail if the jest mock function is ever called without those exact
expectCalledWith
params.
Disclaimer: This won't really work very well with compound declarations, because one of them will
always fail, and throw an assertion error.
when(fn).expectCalledWith(1).mockReturnValue('x')
fn(2);
Supports default behavior
Use any of mockReturnValue
, mockResolvedValue
, mockRejectedValue
, mockImplementation
directly on the object
to set up a default behavior, which will serve as fallback if no matcher fits.
when(fn)
.mockReturnValue('default')
.calledWith('foo').mockReturnValue('special')
expect(fn('foo')).toEqual('special')
expect(fn('bar')).toEqual('default')
Supports custom mockImplementation
You could use this to call callbacks passed to your mock fn or other custom functionality.
const cb = jest.fn()
when(fn).calledWith(cb).mockImplementation(callbackArg => callbackArg())
fn(cb)
expect(cb).toBeCalled()
Thanks to @idan-at.
Supports reseting mocks between tests
You could use this to prevent mocks from carrying state between tests or assertions.
const { when, resetAllWhenMocks } = require('jest-when')
const fn = jest.fn()
when(fn).expectCalledWith(1).mockReturnValueOnce('x')
expect(fn(1)).toEqual('x')
resetAllWhenMocks()
when(fn).expectCalledWith(1).mockReturnValueOnce('z')
expect(fn(1)).toEqual('z')
Thanks to @whoaa512.
Supports verifying that all mocked functions were called
Call verifyAllWhenMocksCalled
after your test to assert that all mocks were used.
const { when, verifyAllWhenMocksCalled } = require('jest-when')
const fn = jest.fn()
when(fn).expectCalledWith(1).mockReturnValueOnce('x')
expect(fn(1)).toEqual('x')
verifyAllWhenMocksCalled()
const { when, verifyAllWhenMocksCalled } = require('jest-when')
const fn = jest.fn()
when(fn).expectCalledWith(1).mockReturnValueOnce('x')
verifyAllWhenMocksCalled()
Thanks to @roaclark.
Contributors (in order of contribution)