Jest: Spy on other function in module being tested

Viewed 997

I'm trying to test a module using jest. In the module, I have a function which works as a kind of front to two other functions, and delegates to them based on its parameter. I'm trying to figure out if these functions have indeed been called.

I have tried using a spy function, but it is never called. Instead, the actual function is called, bypassing my spy.

All examples I have found with mocking and spies involve mocking and spying on another module than the one being tested, but in this case I am trying to spy on a function from the module being tested. Is this even possible? Is there a better way to do this?

Example code:

// funcs.js

const decide = obj => {
  if (obj.a !== undefined) {
    return funcA(obj);
  } else {
    return funcB(obj);
  }
};

const funcA = obj => obj.a;
const funcB = obj => obj.b;

export { decide, funcA, funcB };
// funcs.test.js

import * as fns from "./funcs";

describe("decide", () => {
  it("should delegate to 'funcA' when the parameter has an 'a' property", () => {
    const spy = jest.spyOn(fns, "funcA");
    fns.decide({ a: "Cake" });
    expect(spy).toHaveBeenCalled(); // Error! Number of calls: 0
  });
});
1 Answers

There's no way to spy or mock a function that is called in the same ES module it is defined.

This is possible for CommonJS modules but only if functions are consistently referred as methods:

exports.decide = obj => {
  ...
  exports.funcA(obj);
  ...
};

exports.funcA = obj => obj.a;

It's possible to adopt this recipe to ES modules but this discards their benefits:

const exports = {};
export default exports;

// same as CommonJS

funcA and funcB should be either moved to separate module for testability purposes and be spied or mocked with jest.mock, or all of them should be tested as a single unit.

Related