unknown_boundaries
unknown_boundaries

Reputation: 1590

Jest method-invocation count is wrong?

Consider code -

// utils.js
export const foo = async (a, b) => {
   // do something
   bar(a)
}

export const bar = async (a) => {
   // do something
}

// utils.spec.js
const utils = require('./utils');

const barSpy = jest.spyOn(utils, 'bar');
const result = await utils.foo('a', 'b');

expect(barSpy).toHaveBeenCalledTimes(1);

The test is failing -

Error: expect(jest.fn()).toHaveBeenCalledTimes(expected)

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

I read https://medium.com/@DavideRama/mock-spy-exported-functions-within-a-single-module-in-jest-cdf2b61af642 and https://github.com/facebook/jest/issues/936 but could not solve this with multiple permutations.

Do you see any issue with this?

Upvotes: 4

Views: 6660

Answers (2)

Marcos Luis Delgado
Marcos Luis Delgado

Reputation: 1429

As @bhargav-shah said, when you spy on a module function with jest you are actually spying on its exported function values, not in the internal function itself.

This happens because of how commonJS modules work. With an ES modules environment you could actually be able to achieve what you are trying to do here without modifying your code, as the exports would be bindings. A more in depth explanation can be found here.

At the moment Jest doesn't support ES modules, so the only way you could make your code work would be calling the actual exported function from your foo function:

// utils.js
export const foo = async (a, b) => {
   // do something
   // Call the actual exported function
   exports.bar(a)
}

export const bar = async (a) => {
   // do something
}

// utils.spec.js
const utils = require('./utils');

const barSpy = jest.spyOn(utils, 'bar');
const result = await utils.foo('a', 'b');

expect(barSpy).toHaveBeenCalledTimes(1);

Upvotes: 3

Bhargav Shah
Bhargav Shah

Reputation: 865

As explained in the article that you shared, in your utils.js file you are exporting an object that has foo and bar. Your utils.spec.js actually imports exports.foo and exports.bar. Consequently, you mock exports.bar

By changing the utils.js as below, when you mock bar you will be mocking the actual bar that foo uses.

utils.js

const bar = async () => {};
const foo = async () => {
  exportFunctions.bar();
};
const exportFunctions = {
  foo,
  bar
};
module.exports = exportFunctions;

See this in action in this codesandbox. You can open a new terminal (bottom right) and run npm test right in the browser

Upvotes: 3

Related Questions