jeyj0 / jest-when

A when(fn).calledWith(args).thenReturn(value) lib for jest

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

jest-when

build status codecov GitHub license npm

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); // Will throw a helpful jest assertion error with args diff

Supports default behavior

Use any of mockReturnValue, mockResolvedValue or mockRejectedValue 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() // passes
const { when, verifyAllWhenMocksCalled } = require('jest-when')
const fn = jest.fn()

when(fn).expectCalledWith(1).mockReturnValueOnce('x')

verifyAllWhenMocksCalled() // fails

Thanks to @roaclark.

Alternative syntax using given

As an alternative to the syntax using when.mock*, there's aliases for a given.* syntax:

given(..).calledWith(..).returns === when(..).calledWith(..).mockReturnValue
given(..).calledWith(..).onceReturns === when(..).calledWith(..).mockReturnValueOnce
given(..).calledWith(..).resolvesTo === when(..).calledWith(..).mockResolvedValue
given(..).calledWith(..).onceResolvesTo === when(..).calledWith(..).mockResolvedValueOnce
given(..).calledWith(..).rejectsWith === when(..).calledWith(..).mockRejectedValue
given(..).calledWith(..).onceRejectsWith === when(..).calledWith(..).mockRejectedValueOnce
given(..).calledWith(..).isImplementedAs === when(..).calledWith(..).mockImplementation
given(..).calledWith(..).onceIsImplementedAs === when(..).calledWith(..).mockImplementationOnce

This works with expectCalledWith too, of course.

The exports GivenMock, verifyAllGivenMocksCalled and resetAllGivenMocks are aliases to WhenMock, verifyAllWhenMocksCalled and resetAllWhenMocks respectively.

The two syntaxes can be used interchangeably, although it's not recommended:

when(..).calledWith(..).returns(..)
given(..).calledWith(..).mockReturnValue(..)

Contributors (in order of contribution)

About

A when(fn).calledWith(args).thenReturn(value) lib for jest

License:MIT License


Languages

Language:JavaScript 100.0%