DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Unit testing map functions with Jest
Juha SΓ€lli
Juha SΓ€lli

Posted on • Originally published at testent.io

Unit testing map functions with Jest

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;
}
Enter fullscreen mode Exit fullscreen mode

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');
    });
});
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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)

We want your help! Become a Tag Moderator.
Fill out this survey and help us moderate our community by becoming a tag moderator here at DEV.