Giter Site home page Giter Site logo

jest-when's Introduction

jest-when

build status codecov GitHub license npm ThoughtWorks Tech Radar 2020 | Adopt

Specify dynamic return values for specifically matched mocked function arguments. Flexible matchers. Feels like canonical jest syntax.

ThoughtWorks says:

jest-when is a lightweight JavaScript library that complements Jest by matching mock function call arguments. Jest is a great tool for testing the stack; jest-when allows you to expect specific arguments for mock functions which enables you to write more robust unit tests of modules with many dependencies. It's easy to use and provides great support for multiple matchers, which is why our teams have made jest-when their default choice for mocking in this space.

Introduction

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

So in jest if you want to mock a return value you would do:

const fn = jest.fn()
fn.mockReturnValue('yay!')

But that will return "yay!" regardless of what arguments are send to the fn. If you want to change the return value based on the arguments, you have to use mockImplementation and it can be a bit cumbersome.

jest-when makes this easy and fun!

when(fn).calledWith(1).mockReturnValue('yay!')

Now, the mock function fn will behave as follows—assuming no other trainings took place:

  • return yay! if called with 1 as the only parameter
  • return undefined if called with any parameters other than 1

So the steps are:

const fn = jest.fn()                    // 1) Start with any normal jest mock function
when(fn)                                // 2) Wrap it with when()
  .calledWith(/* any matchers here */)  // 3) Add your matchers with calledWith()
  .mockReturnValue(/* some value */)    // 4) Then use any of the normal set of jest mock functions

The supported set of mock functions is:

  • mockReturnValue
  • mockReturnValueOnce
  • mockResolvedValue
  • mockResolvedValueOnce
  • mockRejectedValue
  • mockRejectedValueOnce
  • mockImplementation
  • mockImplementationOnce

For extended usage see the examples below.

Features

  • Match literals: 1, true, "string", /regex/, null, etc
  • Match objects or arrays: { foo: true }, [1, 2, 3]
  • Match asymmetric matchers: expect.any(), expect.objectContaining(), expect.stringMatching(), etc
  • Setup multiple matched calls with differing returns
  • Chaining of mock trainings
  • Replacement of mock trainings
  • One-time trainings, removed after they are matched
  • Promises, resolved or rejected
  • Can also wrap jest.spyOn functions with when()
  • Supports function matchers
  • Setup a default behavior
  • Supports resetting mocks between tests
  • Supports verifying all whenMocks were called

Usage Examples

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 only happens for mock functions not ending in *Once. Trainings like mockReturnValueOnce are removed after a matching function call anyway.

Thanks to @fkloes.

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 asymmetric matchers:

Use all the same asymmetric matchers available to the toEqual() assertion

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 function matchers:

Just wrap any regular function (cannot be a jest mock or spy!) with when.

The function will receive the arg and will be considered a match if the function returns true.

It works with both calledWith and expectCalledWith.

const allValuesTrue = when((arg) => Object.values(arg).every(Boolean))
const numberDivisibleBy3 = when((arg) => arg % 3 === 0)

when(fn)
.calledWith(allValuesTrue, numberDivisibleBy3)
.mockReturnValue('yay!')

expect(fn({ foo: true, bar: true }, 9)).toEqual('yay!')
expect(fn({ foo: true, bar: false }, 9)).toEqual(undefined)
expect(fn({ foo: true, bar: false }, 13)).toEqual(undefined)

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, 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() // passes
const { when, verifyAllWhenMocksCalled } = require('jest-when')
const fn = jest.fn()

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

verifyAllWhenMocksCalled() // fails

Thanks to @roaclark.

Contributors (in order of contribution)

Many thanks to @jonasholtkamp. He forked this repo when I was inactive and stewarded several key features and bug fixes!

jest-when's People

Contributors

timkindberg avatar dependabot[bot] avatar tjenkinson avatar ebaumqb avatar whoaa512 avatar haines avatar idan-at avatar quassnoi avatar aleonov-virtru avatar devzeebo avatar jeyj0 avatar jlissner avatar kibiz0r avatar jozzi05 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.