8

I am writing unit tests for a function which calls another function inside the same module.

Eg.

export function add(a, b) {
  return a + b
}

export function showMessage(a, b) {
  let sum = add(a, b)
  return `The sum is ${sum}`
}

Test:

import * as Logics from './logics;

describe('showMessage', () => {
  it('should return message with sum', () => {
      let addSpy = jest.spyOn(Logics, 'add')
      let  showMessageResponse = Logics.showMessage(2, 2)
      expect(addSpy).toHaveBeenCalledTimes(1)
  });
});

I want to test if the add function is being called when showMessage is executed. The above one is giving the following error:

Expected number of calls: 1 Received number of calls: 0

I have found a solution but that requires changes in the way functions are exported:

function add(a, b) {
  return a + b
}

function showMessage(a, b) {
  const sum = Logics.add(a, b)
  return `The sum is ${sum}`
}

const Logics = {
  showMessage,
  add
}
export default Logics

I do not want to change the way I am exporting the functions.

  • Check if this helps: https://stackoverflow.com/questions/39755439/how-to-mock-imported-named-function-in-jest-when-module-is-unmocked – manishg Apr 04 '22 at 17:53

2 Answers2

1

Ideally you should test function independent. One test is not responsible for another function test functionality.

Not a best approach but you could do something like this:

util.js

export function add(a, b) {
  return a + b;
}

export function showMessage(a, b, addNew = add) {
  let sum = addNew(a, b);
  return `The sum is ${sum}`;
}

And in your test you could do like this: util.test.js

import * as Logics from "./util";

describe("showMessage", () => {
  it("should return message with sum", () => {
    let addSpy = jest.spyOn(Logics, "add");
    addSpy.mockReturnValue(123);
    let showMessageResponse = Logics.showMessage(2, 2, addSpy);
    expect(addSpy).toHaveBeenCalledTimes(1);
    expect(showMessageResponse).toBe(`The sum is 123`);
  });
});


Here is the sandbox you can play with: https://codesandbox.io/s/jest-test-forked-9zk1s4?file=/util.test.js:0-366

Shubham Verma
  • 4,918
  • 1
  • 9
  • 22
1

On short you can't really achieve it without changing your exports at all. You can read the reasons (and maybe other options on this answer as well as this answer).

A better option imo would be something like (in your logics.js file):

import * as Logics from './logics;

and in showMessage function use it like you did in your second example:

  const sum = Logics.add(a, b)

Basically just importing everything in your logics.js and using that value in order to get the reference to the same add function.

Additional explanation: While you are mocking correctly the add, that is not the same function as the one called in showMessage and you basically can't mock that function (You can also check this code for proof

describe("showMessage", () => {
  it("should return the mocked sum (PASSES)", () => {
    jest.spyOn(Logics, "add").mockReturnValue(123);
    const showMessageResponse = Logics.add(2, 2);
    expect(showMessageResponse).toBe(123);
  });

  it("should return message with sum (FAILS)", () => {
    let addSpy = jest.spyOn(Logics, "add").mockReturnValue(123);
    const showMessageResponse = Logics.showMessage(2, 2);
    expect(addSpy).toHaveBeenCalledTimes(0);
    expect(showMessageResponse).toBe(`The sum is 123`);
  });
});

) also posted in this sandbox

Berci
  • 2,876
  • 1
  • 18
  • 28