npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jest-when

v4.0.1

Published

An extension lib for jest

Readme

jest-when

npm license

Train Jest mocks by argument list.

jest-when lets you keep Jest's familiar mock API while returning different values for different calls, without stuffing branching logic into mockImplementation.

Table of contents

Why jest-when?

Plain Jest makes one thing easy:

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

But that returns 'yay!' no matter how fn is called.

If you want different behavior for different arguments, the usual alternative is a custom implementation with if statements inside your test. That works, but it gets noisy fast.

With jest-when:

import { when } from 'jest-when'

const fn = jest.fn()
when(fn).calledWith(1).mockReturnValue('yay!')

Now fn(1) returns 'yay!', and non-matching calls fall through to undefined unless you configure a default.

A good default mental model is:

  • use calledWith(...) for the normal case
  • use Jest asymmetric matchers when literals are too specific
  • use mockReturnValue*, mockResolvedValue*, and mockRejectedValue* just like you already do in Jest
  • use default* methods when you want a fallback
  • use expectCalledWith(...) when unexpected calls should fail loudly
  • use when.allArgs(...) for advanced matching across the entire argument list

Installation

npm install --save-dev jest-when

Compatibility

  • Jest >= 27
  • Works in both JavaScript and TypeScript projects
  • Named imports are recommended:
import { when, resetAllWhenMocks, verifyAllWhenMocksCalled } from 'jest-when'

If you use CommonJS, require('jest-when') works too.

Quick start

import { when } from 'jest-when'

const fn = jest.fn()

when(fn).calledWith(1).mockReturnValue('one')
when(fn).calledWith(2).mockReturnValue('two')

expect(fn(1)).toBe('one')
expect(fn(2)).toBe('two')
expect(fn(3)).toBeUndefined()

[!IMPORTANT] calledWith(...) uses exact arity matching.

when(fn).calledWith(1) matches fn(1). It does not match fn(), fn(1, 2), or fn(1, undefined).

Async values work the same way:

const fetchUser = jest.fn()

when(fetchUser).calledWith(1).mockResolvedValue({ id: 1, role: 'admin' })
when(fetchUser).calledWith(2).mockRejectedValue(new Error('not found'))

await expect(fetchUser(1)).resolves.toEqual({ id: 1, role: 'admin' })
await expect(fetchUser(2)).rejects.toThrow('not found')

Core concepts

when(fn) wraps a Jest mock or spy

Start with any normal Jest mock function or spy, then train it:

const fn = jest.fn()

when(fn)
  .calledWith('hello')
  .mockReturnValue('world')

when() also supports jest.spyOn(...):

const spy = jest.spyOn(api, 'fetchUser')

when(spy).calledWith(123).mockResolvedValue({ id: 123 })

calledWith(...) trains a specific call

when(fn).calledWith(1, true, 'foo').mockReturnValue('yay!')

That training matches only that exact argument list.

Trainings can be chained

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

expect(fn(1)).toBe('yay!')
expect(fn(2)).toBe('nay!')

Later non-Once trainings replace earlier ones for the same matchers

when(fn).calledWith(1).mockReturnValue('old')
when(fn).calledWith(1).mockReturnValue('new')

expect(fn(1)).toBe('new')

*Once trainings are queued and removed after use

when(fn)
  .calledWith(1)
  .mockReturnValueOnce('first')
  .mockReturnValue('later')

expect(fn(1)).toBe('first')
expect(fn(1)).toBe('later')
expect(fn(1)).toBe('later')

Defaults are explicit in v3+ and still familiar in v4

The clearest way to add a fallback is with a default* method:

when(fn)
  .calledWith('foo').mockReturnValue('special')
  .defaultReturnValue('default')

expect(fn('foo')).toBe('special')
expect(fn('bar')).toBe('default')

You can place the default anywhere in the chain.

You can also set the fallback by calling a Jest-style method on the when(fn) chain before any calledWith(...):

when(fn)
  .mockReturnValue('default')
  .calledWith('foo').mockReturnValue('special')

That behaves the same as defaultReturnValue('default').

Matchers

Literals, objects, arrays, regexes, null, and friends

when(fn).calledWith(1).mockReturnValue('number')
when(fn).calledWith({ role: 'admin' }).mockReturnValue('object')
when(fn).calledWith([1, 2, 3]).mockReturnValue('array')
when(fn).calledWith(/abc/).mockReturnValue('regex')
when(fn).calledWith(null).mockReturnValue('null')

Jest asymmetric matchers

Anything that works well with Jest's equality matching also works well here:

when(fn)
  .calledWith(
    expect.anything(),
    expect.any(Number),
    expect.objectContaining({ enabled: true })
  )
  .mockReturnValue('matched')

Function matchers

Wrap a regular predicate function with when(...) to use it as an argument matcher.

const allValuesTrue = when((arg: Record<string, boolean>) => Object.values(arg).every(Boolean))
const divisibleBy3 = when((arg: number) => arg % 3 === 0)

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

expect(fn({ a: true, b: true }, 9)).toBe('yay!')
expect(fn({ a: true, b: false }, 9)).toBeUndefined()

when.allArgs(...)

Use when.allArgs(...) when matching one argument at a time is awkward and you want to evaluate the entire argument list at once.

const areNumberArgs = (args, equals) => args.every((arg) => equals(arg, expect.any(Number)));

when(fn).calledWith(when.allArgs(areNumberArgs)).mockReturnValue('all numbers')

expect(fn(3, 6, 9)).toBe('all numbers')
expect(fn(3, 666)).toBe('all numbers')
expect(fn(123, 'not a number')).toBeUndefined()

A handy partial-match pattern:

const firstArgMatches = (matcher: unknown) =>
  when.allArgs((args, equals) => equals(args[0], matcher))

when(fn).calledWith(firstArgMatches(expect.any(Number))).mockReturnValue('yay!')

[!IMPORTANT] when.allArgs(...) must be the only matcher passed to calledWith(...) or expectCalledWith(...).

API reference

Top-level exports

| Export | What it does | | --- | --- | | when(fn) | Wraps a Jest mock or spy so you can train behavior by arguments. | | when(matcherFn) | Turns a regular predicate into a function matcher. | | when.allArgs(fn) | Creates a matcher that receives the entire argument list at once. | | resetAllWhenMocks() | Removes all jest-when trainings and restores original mock implementations. | | verifyAllWhenMocksCalled() | Asserts that every configured training was matched at least once. | | WhenMock | Exported for advanced usage and typing. Most users should not need to import it directly. | | default export | Available for compatibility. Named imports are the recommended API. |

Chain methods

Match a call

| Method | Purpose | | --- | --- | | calledWith(...matchers) | Train behavior for an exact argument list. | | expectCalledWith(...matchers) | Like calledWith, but throws an assertion error if the mock is called with different args. |

Configure behavior for a matched call

| Method | Purpose | | --- | --- | | mockReturnValue(value) | Return a value for matching calls. | | mockReturnValueOnce(value) | Return a value once for matching calls. | | mockResolvedValue(value) | Resolve a promise for matching calls. | | mockResolvedValueOnce(value) | Resolve a promise once for matching calls. | | mockRejectedValue(error) | Reject a promise for matching calls. | | mockRejectedValueOnce(error) | Reject a promise once for matching calls. | | mockImplementation(fn) | Use a custom implementation for matching calls. | | mockImplementationOnce(fn?) | Use a custom implementation once for matching calls. |

Configure fallback behavior

| Method | Purpose | | --- | --- | | defaultReturnValue(value) | Fallback return value when no training matches. | | defaultResolvedValue(value) | Fallback resolved promise when no training matches. | | defaultRejectedValue(error) | Fallback rejected promise when no training matches. | | defaultImplementation(fn) | Fallback implementation when no training matches. |

Reset and verify

| Method | Purpose | | --- | --- | | mockReset() | Removes trainings for the current calledWith(...) / expectCalledWith(...) matcher set. | | resetWhenMocks() | Removes all jest-when trainings for one mock or spy. | | resetAllWhenMocks() | Removes all jest-when trainings across the entire test run. | | verifyAllWhenMocksCalled() | Fails if any configured training was never matched. |

Reset behavior at a glance

| Call | Effect | | --- | --- | | fn.mockReset() | Resets the underlying Jest mock and removes all jest-when trainings for that mock. | | when(fn).calledWith(1, 2, 3).mockReset() | Removes only the training(s) for that exact matcher set. | | when(fn).resetWhenMocks() | Removes all jest-when trainings for that one mock and restores its original implementation. | | resetAllWhenMocks() | Removes all jest-when trainings across all wrapped mocks. |

expectCalledWith(...)

expectCalledWith(...) is intentionally stricter than calledWith(...):

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

fn(2) // throws a helpful Jest assertion error

It is best when you want the mock itself to fail loudly on unexpected calls.

It is less pleasant with lots of compound declarations, because one unmatched branch will still fail the assertion.

TypeScript in v4

v4 is rewritten in TypeScript and has much better inference, while keeping the same core API shape.

Most of the time you should not need to import any jest-when types at all.

const getUser = jest.fn(async (id: number) => ({ id, name: 'original' }))

when(getUser).calledWith(1).mockResolvedValue({ id: 1, name: 'Ada' })

await expect(getUser(1)).resolves.toEqual({ id: 1, name: 'Ada' })

That inference also works well with common patterns such as:

  • jest.fn(...)
  • jest.spyOn(...)
  • jest.mocked(...)
  • mocked module functions
  • cast/mock-library patterns such as jest-mock-extended
  • optional arguments, variadic arguments, void, and async return types

In other words: the docs can stay simple because the types should mostly just follow along.

Recipes

Fail loudly on unexpected calls

A great default is one that throws:

when(fn)
  .calledWith('expected').mockReturnValue('ok')
  .defaultImplementation((...args) => {
    throw new Error(`Unexpected args: ${JSON.stringify(args)}`)
  })

Call callbacks in a custom implementation

const callback = jest.fn()

when(fn)
  .calledWith(callback)
  .mockImplementation((cb) => cb())

fn(callback)

expect(callback).toHaveBeenCalled()

Reset one matcher set without touching the rest

when(fn).calledWith(1, 2, 3).mockReturnValue('yay!')
when(fn).calledWith(2).mockReturnValue('boo!')

when(fn).calledWith(1, 2, 3).mockReset()

expect(fn(1, 2, 3)).toBeUndefined()
expect(fn(2)).toBe('boo!')

Verify that every training was used

import { verifyAllWhenMocksCalled, when } from 'jest-when'

const fn = jest.fn()

when(fn).calledWith(1).mockReturnValue('x')

fn(1)
verifyAllWhenMocksCalled()

This checks that every configured training was matched at least once.

Contributors

Created by @timkindberg.

Many thanks to the people who helped shape and steward the project, especially: