Releases: timkindberg/jest-when
v3.7.0
Support @golevelup/ts-jest and jest-mock-extended proxies
Default Behavior Methods
New methods:
defaultReturnValue
defaultResolvedValue
defaultRejectedValue
defaultImplementation
Use them to set up a default behavior, which will serve as fallback if no matcher fits.
when(fn)
.calledWith('foo').mockReturnValue('special')
.defaultReturnValue('default') // This line can be placed anywhere, doesn't have to be at the end
expect(fn('foo')).toEqual('special')
expect(fn('bar')).toEqual('default')
Previously you needed to use any of mockReturnValue
, mockResolvedValue
, mockRejectedValue
, or mockImplementation
directly on the object before using calledWith to create a default fallback.
// Same as above example
when(fn)
.mockReturnValue('default')
.calledWith('foo').mockReturnValue('special')
One neat idea, and the reason I even did this work, is to set up a default implementation that throws an error if an improper call is made to the mock.
when(fn)
.calledWith(correctArgs)
.mockReturnValue(expectedValue)
.defaultImplementation(unsupportedCallError)
// A default implementation that fails your test
function unsupportedCallError(...args) {
throw new Error(`Wrong args: ${JSON.stringify(args, null, 2)}`);
}
Commits:
- Merge pull request #92 from timkindberg/default_methods 67b7532
- Add several methods to set up default behaviors. These methods can be placed anywhere in the mocking set up, including after other calledWith setups, unlike previous "default" behavior using
when(fn).mockReturnValue(z)
. You can now do:when(fn).calledWith(x).mockReturnValue(y).defaultReturnValue(z)
. 77fe672 - Add documentation example for unsupported call error as default implementation a6dc74d
v3.4.2
- Reset assertionCalls to zero so that our hack doesn't interfere with existing expect.assertions() usage 3a27d0b
v3.4.1
- Access the
equals
util by grabbing it from theexpect
this arg. Temporary hack until core jest exposes this util officially. 5a8cc93
v3.4.0
New Feature: Use mockReturnValue
after or without calledWith
usage
Works with mockReturnValue, mockResolvedValue, mockRejectedValue, mockImplementation
Previously you were not allowed to set a default implementation with a mock return function without a calledWith:
when(fn).mockReturnValue(true)
fn() // ⚠️ Threw error!
Now you can!
when(fn).mockReturnValue(true)
fn() // ✅ Returns `true`
Also you could set the default after using calledWith:
when(fn).mockReturnValue('b') // ✅ Before was supported
when(fn).calledWith(1).mockReturnValue('a')
when(fn).mockReturnValue('b') // ⚠️ After was not
Now you can!
when(fn).mockReturnValue('b') // ✅ Before is still supported
when(fn).calledWith(1).mockReturnValue('a')
when(fn).mockReturnValue('b') // ✅ After is now supported
New Feature: Keep original function implementation when not matched
class TheClass {
fn () {
return 'real'
}
}
const instance = new TheClass()
const spy = jest.spyOn(instance, 'fn')
when(spy)
.calledWith(1)
.mockReturnValue('mock')
expect(instance.fn(2)).toBe('real') // ✅ Successfully falls back to the real underlying spied function
Installation Improvement: Jest is now a peer dependency
This helps avoid installing lots of duplicated dependencies.
We presume that you were not implicitly relying on jest-when to install the jest dependency in your codebase. And assuming this we are able to bump by a minor instead of major version.
Tested with jest 24, 25, 26 and 27, it should work with other versions as well. We will likely not work to support more than the last couple versions without community PRs.
Commits:
- Allow defaults to be set up after calledWiths or even without any calledWiths cc6ca58
- Merge pull request #82 from mfressdorf/master fe6dba9
- Merge pull request #79 from timkindberg/dependabot/npm_and_yarn/path-parse-1.0.7 bbb5396
- Merge pull request #74 from timkindberg/dependabot/npm_and_yarn/hosted-git-info-2.8.9 6a523f9
- Merge pull request #84 from chyzwar/mmake-jest-peer-deps 26a907a
- Merge branch 'master' into mmake-jest-peer-deps 1379b18
- Merge pull request #85 from AndrewSouthpaw/replace-bunyan-with-smaller-logger d591350
- Fully remove logger. fb72636
- Replace bunyan with more stable, zero-dependency logger. 5624306
- fix(): lower peer deps to jest 24, make it non breaking ad2084a
- feat(): make jest an peer dependancy 4092a59
- Keep original function implementation when not matched 9b81e2b
- Bump path-parse from 1.0.5 to 1.0.7 1eca1bc
- Bump hosted-git-info from 2.6.0 to 2.8.9 40e8ef1
- Merge pull request #73 from timkindberg/dependabot/npm_and_yarn/glob-parent-5.1.2 8af0e4c
- Merge pull request #72 from timkindberg/dependabot/npm_and_yarn/normalize-url-4.5.1 f8c822e
- Merge pull request #71 from timkindberg/dependabot/npm_and_yarn/trim-newlines-3.0.1 5c3ef60
- Merge pull request #70 from timkindberg/dependabot/npm_and_yarn/lodash-4.17.21 0a35f5b
- Merge pull request #69 from timkindberg/dependabot/npm_and_yarn/handlebars-4.7.7 83e8d88
- Bump glob-parent from 5.1.1 to 5.1.2 73da46c
- Bump normalize-url from 4.5.0 to 4.5.1 2577978
- Bump trim-newlines from 3.0.0 to 3.0.1 a3b5623
- Bump lodash from 4.17.19 to 4.17.21 d397bc1
- Bump handlebars from 4.5.3 to 4.7.7 a32694c
Fix calling `calledWith` with `null`
This should work again now:
const fn = jest.fn();
when(fn).calledWith(null).mockReturnValue('yay!');
expect(fn(null)).toBe('yay!');
v3.3.0
Match All Args with when.allArgs
This release adds support for matching or asserting against all of the arguments together using when.allArgs
:
Just pass a single special matcher, when.allArgs
. Pass when.allArgs
an array predicate function.
calledWith(when.allArgs(anArrayPredicateFn))
The array predicate function will receive all of the arguments as an array as well as the powerful equals
utility from Jasmine. It should return true
if the args are a match, or false
if not.
calledWith(
when.allArgs(
(allArgs: Array<any>, equals: Function) => boolean
)
)
This allows some convenient patterns:
- Less verbose for variable args where all need to be of a certain type or match (e.g. all numbers)
- Can be useful for partial matching, because you can assert just the first arg for example and ignore the rest
Examples
All args should be numbers:
const areNumbers = (args, equals) => args.every(arg => equals(arg, expect.any(Number)))
when(fn).calledWith(when.allArgs(areNumbers)).mockReturnValue('yay!')
expect(fn(3, 6, 9)).toEqual('yay!')
expect(fn(3, 666)).toEqual('yay!')
expect(fn(-100, 2, 3.234234, 234, 90e3)).toEqual('yay!')
expect(fn(123, 'not a number')).toBeUndefined()
Single arg match:
const argAtIndex = (index, matcher) => when.allArgs((args, equals) => equals(args[index], matcher))
when(fn).calledWith(argAtIndex(0, expect.any(Number))).mockReturnValue('yay!')
expect(fn(3, 6, 9)).toEqual('yay!')
expect(fn(3, 666)).toEqual('yay!')
expect(fn(-100, 2, 3.234234, 234, 90e3)).toEqual('yay!')
expect(fn(123, 'not a number')).toBeUndefined()
Partial match, only first defined matching args matter:
const fn = jest.fn()
const partialArgs = (...argsToMatch) => when.allArgs((args, equals) => equals(args, expect.arrayContaining(argsToMatch)))
when(fn)
.calledWith(partialArgs(1, 2, 3))
.mockReturnValue('x')
expect(fn(1, 2, 3)).toEqual('x')
expect(fn(1, 2, 3, 4, 5, 6)).toEqual('x')
expect(fn(1, 2)).toBeUndefined()
expect(fn(1, 2, 4)).toBeUndefined()
v3.2.0
New Feature: Function Matchers
You can now pass a predicate function as a matcher. The function will receive the arg and will be considered a match if the function returns true
.
It works with both calledWith
and expectCalledWith
.
Just wrap any regular function (cannot be a jest mock or spy!) with when
.
const mock = jest.fn()
const myFunctionMatcher = when(arg => arg === 'hello' ? true : false)
when(mock).calledWith(myFunctionMatcher).mockReturnValue('x')
expect(mock('hello')).toEqual('x')
expect(mock('bye')).toEqual(undefined)
More Examples
const fn = jest.fn()
// Some predicate functions to be used as arg matchers
const allValuesTrue = when((arg) => Object.values(arg).every(Boolean))
const numberDivisibleBy3 = when((arg) => arg % 3 === 0)
when(fn)
// Pass the predicate functions here as matchers
.calledWith(allValuesTrue, numberDivisibleBy3)
.mockReturnValue('x')
expect(fn({ foo: true, bar: true }, 9)).toEqual('x')
expect(fn({ foo: true, bar: false }, 9)).toEqual(undefined)
expect(fn({ foo: true, bar: true }, 13)).toEqual(undefined)
This should add some really great flexibility and allow for some great custom matcher utils.
For example, you can more easily use jest-when now for mocking your fetch calls for both back or front end testing.
// fetch will come in as a jest mock fn
import fetch from '~/our/companies/fetch.js'
// userService uses the fetch.js file underneath the hood to perform an outward api call
import userService from '~/someService.js'
import _ from 'lodash'
// Here's where we mocked it
jest.mock('~/our/companies/fetch.js', () => jest.fn())
// Build up a collection of custom matcher functions!!
const bodyHas = match => when(obj => _.isMatch(JSON.parse(obj).body, match))
test('calls an api', () => {
when(fetch)
.calledWith(
expect.stringContaining("api/user/44"),
bodyHas({ firstName: "Tim" )
)
.mockReturnValue(Response(...))
const user = await userService.updateUser(44, { firstName: "Tim" })
expect()...
})
Commits
- v3.2.0 fb0dbe6
- Add np for releases 414c2d2
- Docs for function matchers 36cd03f
- Merge remote-tracking branch 'origin/master' ba0bcd3
- Support function matchers 6190463
- Merge pull request #60 from jlissner/patch-1 ea8f335
- Update README.md f5df9a3
- Add ThoughtWorks Adopt Badge b415550
- Merge pull request #56 from timkindberg/dependabot/npm_and_yarn/ini-1.3.7 435aa82
- Bump ini from 1.3.5 to 1.3.7 b739bb8