5

I have a very complex object i am using to mock out a DataTable() object for the purposes of testing.

const obj = {
  DataTable: () => {
    return {
      columns: () => {
        return {
          data: () => {
            return {
              eq: () => {
                return {
                  indexOf: jest.fn(),
                };
              },
            };
          },
          visible: () => {
            return jest.fn();
          },
        };
      },
    };
  },
};

Inside my testing code I am trying to spy on some of these functions but it always returns undefined. Is there a way to mock out the return value of deeply nested functions?

    jest.spyOn(obj.DataTable().columns().data().eq(), 'indexOf').mockReturnValue('test');
    console.log(obj.DataTable().columns().data().eq().indexOf()); // returns undefined, should return 'test'

1 Answer 1

11

Here is the unit test solution:

index.ts:

import { obj } from './obj';

export function main() {
  return obj.DataTable().columns().data().eq().indexOf();
}

obj.ts:

export const obj = {
  DataTable: () => {
    return {
      columns: () => {
        return {
          data: () => {
            return {
              eq: () => {
                return {
                  indexOf: () => 'real data',
                };
              },
            };
          },
        };
      },
    };
  },
};

index.test.ts:

import { main } from './';
import { obj } from './obj';

describe('61396089', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should pass', () => {
    const mockDataTable = {
      columns: jest.fn().mockReturnThis(),
      data: jest.fn().mockReturnThis(),
      eq: jest.fn().mockReturnThis(),
      indexOf: jest.fn().mockReturnValueOnce('fake data'),
    };
    jest.spyOn(obj, 'DataTable').mockImplementationOnce(() => mockDataTable);
    const actual = main();
    expect(actual).toBe('fake data');
    expect(mockDataTable.columns).toBeCalledTimes(1);
    expect(mockDataTable.data).toBeCalledTimes(1);
    expect(mockDataTable.eq).toBeCalledTimes(1);
    expect(mockDataTable.indexOf).toBeCalledTimes(1);
  });
});

unit test results with coverage report:

 PASS  stackoverflow/61396089/index.test.ts (20.978s)
  61396089
    ✓ should pass (8ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   44.44 |      100 |   16.67 |   44.44 |                   
 index.ts |     100 |      100 |     100 |     100 |                   
 obj.ts   |   16.67 |      100 |       0 |   16.67 | 3-10              
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        23.889s

source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61396089

Sign up to request clarification or add additional context in comments.

2 Comments

I was looking for that 2 days... At the beginning I could not understand why I can not mock function, but they are isolated behind parent function... I slightly modified that as const mockDataTable = (some return values or even whole jest.fn().. spy) => ({ ..., indexOf: () => return value or indexOf: indexOfSpy passed by mockDataTable(params ...). Thx
Much more elegant solution than the nest of mockImplementationOnce that seems to be prevalent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.