I ran into problems when I was unit testing map functions with Jest. I gave the array map only the reference to a function and as a result I was in trouble. Here's how it happened.
import { functionInsideMap } from './functionInsideMap';
export function referenceToArrowFuncInMap(names: string[]): string[] {
const namesWithHello = names.map(functionInsideMap);
return namesWithHello;
}
The referenceToArrowFuncInMap above applies the functionInsideMap to each array value. Let's write a unit test for the referenceToArrowFuncInMap.
import { referenceToArrowFuncInMap } from './index';
import { functionInsideMap } from './functionInsideMap';
jest.mock('./functionInsideMap');
const mockedFunctionInsideMap = jest.mocked(functionInsideMap, false);
describe("Test the 'referenceToArrowFuncInMap' function", () => {
beforeEach(() => {
mockedFunctionInsideMap.mockReset();
});
it('Using reference to function inside map will fail', () => {
//Arrange
//Setting inputs
const names = ['John', 'Matt'];
//Setting output
const expectedOutput = ['Hello John!', 'Hello Matt!'];
//Mocking functions and objects
mockedFunctionInsideMap
.mockReturnValueOnce('Hello John!')
.mockReturnValueOnce('Hello Matt!');
//Act
const callOutput = referenceToArrowFuncInMap(names);
//Assert output
expect(callOutput).toEqual(expectedOutput);
//Assert function under test internals
expect(functionInsideMap).toHaveBeenCalledTimes(2);
expect(functionInsideMap).toHaveBeenNthCalledWith(1, 'John');
expect(functionInsideMap).toHaveBeenNthCalledWith(2, 'Matt');
});
});
I get the following error from Jest when I run the above unit test. The toHaveBeenNthCalledWith
assert for functionInsideMap seems to break.
> unit-testing-map-functions-with-jest@1.0.0 test
> jest
FAIL src/index.referenceToArrowFuncInMap.spec.ts
Test the 'referenceToArrowFuncInMap' function
✕ Using reference to function inside map will fail (11 ms)
● Test the 'referenceToArrowFuncInMap' function › Using reference to function inside map will fail
expect(jest.fn()).toHaveBeenNthCalledWith(n, ...expected)
n: 1
Expected: "John"
Received
-> 1: "John", 0, ["John", "Matt"]
2: "Matt", 1, ["John", "Matt"]
Number of calls: 2
31 | //Assert function under test internals
32 | expect(functionInsideMap).toHaveBeenCalledTimes(2);
> 33 | expect(functionInsideMap).toHaveBeenNthCalledWith(1, 'John');
| ^
34 | expect(functionInsideMap).toHaveBeenNthCalledWith(2, 'Matt');
35 | });
36 | });
at Object.<anonymous> (src/index.referenceToArrowFuncInMap.spec.ts:33:29)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.79 s
Ran all test suites.
Jest expected the functionInsideMap to have been called with value "John". But instead map called it with 3 values.
- "John"
- 0
- ["John", "Matt"]
Reason
This is because Javascript array map calls your callback-function with three arguments.
- element - The current element being processed in the array.
- index - The index of the current element being processed in the array.
- array - the array map was called upon. MDN Web Docs
If you give a function reference to the array map function Jest records all arguments given. As a result the error shows that map called functionInsideMap with 3 arguments. Not with 1.
The easiest way to fix this is to change the source code. Firstly use an anonymous function as the map's callback. Secondly call the functionInsideMap inside the anonymous function.
The fix
import { functionInsideMap } from './functionInsideMap';
export function referenceToArrowFuncInMap(names: string[]): string[] {
const namesWithHello = names.map((name) => functionInsideMap(name));
return namesWithHello;
}
This way the anonymous function only uses the first given argument and Jest works the right way.
Conclusion
In conclusion it's better to use concise functions as the callback functions. Firstly this makes your unit testing easier. Additionally the readability of the code also improves. The reader clearly sees what the array map passes to the callback. Unit testing map functions with concise functions makes your life easier.
Top comments (0)