Unit testing map functions with Jest

Problem in unit testing map functions

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 assert for functionInsideMap on line 32 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.

On the line 13 we see that 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.

1. element – The current element being processed in the array.

2. index – The index of the current element being processed in the array.

3. 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.

Testent

Testent makes testing easier for you. It automatically creates unit tests for your functions. To illustrate this see the video below. You can use our VSCode extension for free. Install the extension from the Microsoft Marketplace. Start making testing easier!

Unit testing map functions with Testent