A higher order function takes a function and returns a function. Jest offers a way to make sure the function you gave is being used right.
Testing a Higher Order Function
Let’s say you want to write a memoization function. It might look something like:
// memoize.js export default function memoize(func) { return function (key) { const registry = new Map(); let value = registry.get(key); if (typeof value === 'undefined') { value = func(key); registry.set(key, value); } return value; } }
And to test it, you’d have something like:
import memoize from './memoize'; it('memoizes `func`.', () => { // `func` here just cubes the input. const func = value => Math.pow(value, 3); const memoizedFunc = memoize(func); expect(memoizedFunc(3)).toEqual(27); expect(memoizedFunc(4)).toEqual(64); expect(memoizedFunc(3)).toEqual(27); expect(memoizedFunc(5)).toEqual(125); });
yarn run test
, everything passes, we’re done, right?
The Trouble With Testing Higher Order Functions
Except there’s a problem with memoize
. Did you catch it? The issue is that you’re testing the resultant function, but not how the input function is being used. To do this, Jest provides mock functions.
Creating a Mock Function
It’s pretty simple; just pass an implementation function to jest.fn
and it’ll give you a mock function.
Hey, that's a higher order function, too!
Let’s use this to test memoize
a bit better:
import memoize from './memoize'; it('memoizes `func`.', () => { // A mock function: a function wrapped in `jest.fn`. const func = jest.fn(value => Math.pow(value, 3)); const memoizedFunc = memoize(func); expect(memoizedFunc(3)).toEqual(27); expect(memoizedFunc(4)).toEqual(64); expect(memoizedFunc(3)).toEqual(27); expect(memoizedFunc(5)).toEqual(125); // Mock functions have a few special matchers available to them. Let's take a look. // `func` was called. expect(func).toHaveBeenCalled(); // `func` has been called with 5. expect(func).toHaveBeenCalledWith(4); // `func` was last called with 5. expect(func).toHaveBeenLastCalledWith(5); // `func` was called thrice (since the second 3 was supposed to be memoized). // Supposed to. expect(func).toHaveBeenCalledTimes(3); });
Now that you’re testing how the function is used, you can catch that error in memoize
.