-1

I am using Jest for my testing environment and I am trying to see if a method in the tested module calls another method at least once.

Here is the nested.js module:

export function getUserData() {
  console.log("CALLING API");
  const user = {
    name: "user",
  };
  setUserData(user);
}

export function setUserData(user) {
  console.log("Setting user data");
}

And here is the nested.test.js:

import * as nested from "../nested";
let storeUserDataSpy = jest.spyOn(nested, "setUserData");

describe("Get user data calls child method", () => {
  it("should call the child method", async () => {
    nested.getUserData();
    expect(storeUserDataSpy).toHaveBeenCalledTimes(1);
  });
});

The error that I am getting is that the method has not been called at all:

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

I tried various ways of resolving the issue including jest.mock and including the actual methods from the nested.js module.

What I don't want to do is to spyOn console logs as these won't be included in the production code.

I uploaded the code with all the Jest and Babel config to a GitHub repo.

Here are the steps to try it locally:

git clone git@github.com:verebes1/tests-jest.git
cd tests-jest
npm install
npm run test
AD Progress
  • 4,190
  • 1
  • 14
  • 33
  • does it make any difference if you export `setUserData` directly instead of wrapping it into an object? – messerbill May 11 '23 at 14:36
  • @messerbill Unfortunately I already tried that but it also does not work that way. I updated my answer to include that info. – AD Progress May 11 '23 at 14:40
  • Does this answer your question? [How to mock functions in the same module using Jest?](https://stackoverflow.com/questions/45111198/how-to-mock-functions-in-the-same-module-using-jest) – jonrsharpe May 11 '23 at 14:57
  • `getUserData` doesn't call `setUserData` via `exportedForTesting` (and having "for testing" in your implementation is a definite smell). This is explored in great detail on the proposed dupe, but just [don't do it](https://stackoverflow.com/a/70066090/3001761). – jonrsharpe May 11 '23 at 14:57
  • @jonrsharpe Unfortunately the answer posted above does not solve the issue of method not showing as called. The `exportedForTesting` was only a tried approach I tried exporting directly in fact I'll revert to that code so it is more readable. – AD Progress May 11 '23 at 15:10
  • @jonrsharpe I removed the `exportedForTesting` part of the code to avoid confusion and updated the question accordingly. – AD Progress May 11 '23 at 15:17
  • `getUserData` doesn't call `setUserData` via the namespace object, either. You can't spy or mock its reference, it's closed over that name. Again, the proposed dupe covers this with various options (including mine, which corresponds with the answer below - extract the dependency so you're **not** mocking inside the module boundary) to solve it. – jonrsharpe May 11 '23 at 15:20

2 Answers2

1

Easy way to achieve that is to extract function you want to spy on to separate file and spy on that.

Test file nested.test.js

import {getUserData} from "../nested";
import * as utils from './utils'


let storeUserDataSpy = jest.spyOn(utils, "setUserData");

describe("Get user data calls child method", () => {
  it("should call the child method", async () => {
    getUserData();
    expect(storeUserDataSpy).toHaveBeenCalledTimes(1);
  });
});

Code file nested.js

import {setUserData} from "./test/utils";

export function getUserData() {
  console.log("CALLING API");
  const user = {
    name: "user",
  };
  setUserData(user);
}

Utils utils.js

export function setUserData(user) {
    console.log("Setting user data");
}
  • Thanks @Dmitrii Zolotuhin I tried your approach and it works well, however I would like to avoid code separation. I added your approach to the repo. https://github.com/verebes1/tests-jest/tree/module-separation – AD Progress May 11 '23 at 15:58
0

I found a solution based on another answer by using the Rewire package, and by including it as a babel plugin. Here is the code:

nested.js

export function getUserData() {
  console.log("CALLING API");
  const user = {
    name: "user",
  };
  setUserData(user);
}

export function setUserData(user) {
  console.log("Setting user data");
}

And the nested.test.js

import __RewireAPI__, * as nested from "../nested";
let storeUserDataSpy = jest.spyOn(nested, "setUserData");

describe("Get user data calls child method", () => {
  it("should call the child method", async () => {
    __RewireAPI__.__Rewire__('setUserData', storeUserDataSpy);
    nested.getUserData();
    expect(storeUserDataSpy).toHaveBeenCalledTimes(1);
  });
});

finally .babelrc

{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    "rewire"
  ]
}

The answer-from-stack-overflow-using-rewire branch on the repo is updated with the above

AD Progress
  • 4,190
  • 1
  • 14
  • 33