IamaC
IamaC

Reputation: 377

How to mock a function that returns a function using jest

I am trying to mock the function that returns another function. However, I am not sure how to get this done using jest. Thanks in advance.

  1. Should return successfully.
  2. Should throw an error.

Module needed to be mocked:


const initGreeter = () =>{
    return {
        sayHello: (name:string) =>{
            console.log(`Hello: ${name}`)
        }
    }
}
export default initGreeter;

The module under test:

import initGreeter from './greeter';

export const greetSomeone = (name:string) =>{
    const greeter = initGreeter();
    greeter.sayHello(name)
}

Tests:

import initGreeter from '../../utils/greeter';
import { greetSomeone } from '../../utils/greeterConsumer';

describe('greeterConsumer', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  it('greeter consumer should call SayHello on greeter', () => {
    //TODO:mock greeter here
    greetSomeone('sam');
    expect(greeter.sayHello.mock.call[0][0]).toBe('sam');
  });

  it('greeter consumer throws exception', () => {
    //TODO:mock greeter here so it throws exception
    expect(greetSomeone('sam')).toThrow(Error);
  });
});

Please note code in Update 1 and 2 are a continuation of the code in the solution from @slideshowp2.

Update 2: Looks like if the change the code below

jest.mock('./greeter', () => {
  return jest.fn(() => mGreeter);
});

to

jest.mock('../../utils/greeter.ts', () => ({  
  __esModule: true,
  default: jest.fn(() => mGreeter)

}));

it works.

There are other ways to make the code from @slideshowp2 to work as is, for example setting "esModuleInterop": true in tsconfig. However, I don't fully understand why it works for either case. I may check in the future and update here.

Update 1: after implementing the solution from @slideshowp2

I am getting the error below in my test

FAIL src/tests/utils/greeterConsumer.test.ts ● greeterConsumer › greeter consumer should call SayHello on greeter

TypeError: greeter_1.default is not a function

  2 | 
  3 | export const greetSomeone = (name:string) =>{
> 4 |     const greeter = initGreeter();
    |                     ^
  5 |     greeter.sayHello(name)
  6 | }

  at Object.<anonymous>.exports.greetSomeone (src/utils/greeterConsumer.ts:4:21)
  at Object.<anonymous> (src/__tests__/utils/greeterConsumer.test.ts:18:5)

Upvotes: 2

Views: 9749

Answers (2)

ravthiru
ravthiru

Reputation: 9623

I use below to mock logger , which return logger instance with logging methods

jest.mock('logging', () => ({
  getLogger: jest.fn().mockReturnValue({
  error: jest.fn(),
  debug: jest.fn(),
  info: jest.fn(),
  warn: jest.fn(),
})
}
));

Upvotes: 0

Lin Du
Lin Du

Reputation: 102237

You can use jest.mock(moduleName, factory, options) to mock ../../utils/greeter module manually.

E.g.

greeter.ts:

const initGreeter = () => {
  return {
    sayHello: (name: string) => {
      console.log(`Hello: ${name}`);
    },
  };
};
export default initGreeter;

greeterConsumer.ts:

import initGreeter from './greeter';

export const greetSomeone = (name: string) => {
  const greeter = initGreeter();
  greeter.sayHello(name);
};

greeterConsumer.test.ts:

import initGreeter from './greeter';
import { greetSomeone } from './greeterConsumer';

const mGreeter = {
  sayHello: jest.fn(),
};

jest.mock('./greeter', () => {
  return jest.fn(() => mGreeter);
});

describe('greeterConsumer', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  it('greeter consumer should call SayHello on greeter', () => {
    greetSomeone('sam');
    expect(initGreeter).toBeCalledTimes(1);
    expect(mGreeter.sayHello).toBeCalledWith('sam');
  });

  it('greeter consumer throws exception', () => {
    mGreeter.sayHello.mockImplementationOnce(() => {
      throw new Error('oops');
    });
    expect(() => greetSomeone('sam')).toThrowError('oops');
    expect(initGreeter).toBeCalledTimes(1);
    expect(mGreeter.sayHello).toBeCalledWith('sam');
  });
});

unit test result:

 PASS  examples/65281989/greeterConsumer.test.ts
  greeterConsumer
    ✓ greeter consumer should call SayHello on greeter (3 ms)
    ✓ greeter consumer throws exception (10 ms)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------------|---------|----------|---------|---------|-------------------
All files           |     100 |      100 |     100 |     100 |                   
 greeterConsumer.ts |     100 |      100 |     100 |     100 |                   
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.473 s

source code: https://github.com/mrdulin/jest-v26-codelab/tree/main/examples/65281989

Upvotes: 4

Related Questions