jest-mock-chain-fn
v0.0.1
Published
[![npm version](https://badge.fury.io/js/jest-mock-chain-fn.svg)](https://badge.fury.io/js/jest-mock-chain-fn)
Downloads
4
Maintainers
Readme
jest-mock-chain-fn
Easier testing with chained functions + jest 🧪
⭐️ Also allows snapshot testing with chained functions! ⭐️
Who's it for?
- You use libraries that use method chaining (eg.
new Item().setPrice(2).setQuantity(5)
)- see more: Method Chaining
- You need unit tests to assert that these chained methods are called correctly
- You would like to mock away the actual implementation for ease of testing
- You don't want to write so much setup code for testing libraries that use method chaining
Related concepts: Builder Design Pattern, Fluent Interface
Scenario
// yargsExample.ts
// as per example usage on: https://yargs.js.org/
export const yargsExample = (yargs: any) => {
const result = yargs
.scriptName('pirate-parser')
.usage('$0 <cmd> [args]')
.command(
'hello [name]',
'welcome ter yargs!',
(yargs: any) => {
yargs.positional('name', {
type: 'string',
default: 'Cambi',
describe: 'the name to say hello to',
})
},
function (argv: any) {
console.log('hello', argv.name, 'welcome to yargs!')
}
)
.help().argv
return result
}
Scenario:
- System Under Test is
yargsExample()
- When
yargsExample()
is called, - Then we expect that
yargs
has been called with:scriptName()
with args['pirate-parser']
usage()
with args['$0 <cmd> [args]']
- ... (rest of chained functions)
- Returned value from
.argv
Usage
// 1. import makeMockChainFn()
import { makeMockChainFn } from 'jest-mock-chain-fn'
// 2. import function under test
import { yargsExample } from './yargsExample'
test('asserts chained fn is called correctly', () => {
// 3. set .argv to return mock object
const mockPropertyReturns = { argv: { value: { some: 'object' } } }
// 4. create mock chain fn
const { mockChainFn: yargs, calls } = makeMockChainFn({
mockPropertyReturns,
})
// 5. trigger usage of top-level function
const result = yargsExample(yargs)
// 7. assert chain fn calls against snapshot
expect(calls).toMatchSnapshot()
// 8. assert return value
expect(result).toEqual({ some: 'object' })
})
Key point: With minimal setup, we are able to:
- Capture all calls made to
yargs
- Assert calls against a snapshot!
No more tedious handwiring of mocks to test method chaining. 🙃
[
{ key: 'scriptName', type: 'function', args: [ 'pirate-parser' ] },
{ key: 'usage', type: 'function', args: [ '$0 <cmd> [args]' ] },
{
key: 'command',
type: 'function',
args: [ 'hello [name]', 'welcome ter yargs!', [Function], [Function] ]
},
{ key: 'help', type: 'function', args: [] },
{ key: 'argv', type: 'value', value: { some: 'object' } }
]
See more: yargsExample.test.ts, chalkExample.test.ts
API
const options = {
// optional: sets overrides for specific property keys
mockPropertyReturns: { somePropName: {} },
}
const result = makeMockChainFn(options)
const {
// mock that accepts method/property calls against it
mockChainFn,
// map of mock fns (automatically created upon call against mockChainFn)
mocks,
// list of calls made against mockChainFn
calls,
// clears mocks mapping
clearExistingMocks,
// clears list of calls
clearExistingCalls,
} = result
See more: makeMockChainFn.ts
mockPropertyReturns
Used to define non-standard mock function behaviors upon property call
For: Chained function (Default scenario)
- By default,
mockChainFn
supports method chaining. (ie. nomockPropertyReturns
setup required) - Upon calling any arbitrary property (eg.
mockChainFn.myCall()
), library creates a mapping formyCall
inmocks
, - The corresponding value for the
myCall
key is ajest.fn()
that captures all calls and returnsmockChainFn()
(to support method chaining).
For: Chained value (non-function)
- In order to support chained value (non-function) calls (eg.
mockChainFn.compute.save
), - Need to set
mockPropertyReturns = { compute: {}, save: {} }
For: Property with fixed value (function)
- Supports returning of fixed value (function) against a given property (eg.
mockChainFn.myCall()
) - Useful to provide own mock functions for customizable behvaior for a given function
- Eg. We want a function call to call our own mock function and return the result of our mock function
- Need to set
mockPropertyReturns = { myCall: { value: jest.fn() } }
(wherejest.fn()
is your own mock) - Note: In this mode, a call against
myCall
will still be captured intocalls
For: Property with fixed value (non-function)
- Supports returning of fixed value (non-function) against a given property (eg.
mockChainFn.someProperty
) - Eg. We want a property call to return a mock value
- Need to set
mockPropertyReturns = { someProperty: { value: 'own value' } }
Tips
- Calling
jest.resetAllMocks()
will clearjest.fn()
mock functions that are used internally withinmockChainFn
(ie. inmocks
mapping) - If you want your own mock function to continue chaining, just set the return value to
mockChainFn
- When defining own mock function to pass to given property, if own mock function throws error, it will be propagated upwards (ie. library does not swallow errors)