Jest模拟时模块引入顺序导致api为undefined的原因
问题:Jest模拟因模块引入顺序导致失效的原因
当const { findFoo } = require("./foo");留在test.js当前位置时,foo.js内的api会打印为undefined;但将其移到const spy =代码行下方后,功能正常。请问为何这种引入顺序下Jest的模拟无法生效?
foo.js
/* eslint-disable camelcase */ const { createApiInstance } = require("./../utils/api"); const api = createApiInstance() async function findFoo(name) { console.log({ api }); const res = await api({ method: "GET", url: `/bar`, }) } module.exports = { findFoo }
utils.js
const axios = require("axios"); const createApiInstance = (params) => axios.create(); module.exports = { createApiInstance, };
test.js
const apiWrapper = require("./../utils/api"); jest.mock("./../utils/api"); const { findFoo } = require("./foo"); describe("Foo", () => { describe("findFoo", () => { it("foos", async () => { const spy = jest .spyOn(apiWrapper, "createApiInstance") .mockImplementation(() => Promise.resolve(jest.fn())); const res = await findFoo("foo"); console.log(res); spy.mockRestore(); }); }); });
原因解释
核心问题出在模块加载时机和Jest模拟的生效顺序上:
jest.mock("./../utils/api")会自动创建一个空的模拟对象,此时createApiInstance是一个无返回值的Jest mock函数,默认调用会返回undefined。- 当你在创建
spy之前就require("./foo"),foo.js会立即执行模块内的所有代码:- 先引入
createApiInstance(此时是Jest的空mock) - 同步执行
const api = createApiInstance(),因为mock还没被设置返回值,api直接被赋值为undefined
- 先引入
- 后续再给
createApiInstance设置mockImplementation已经没用了——foo.js里的api是模块级变量,在模块加载时就已经确定值,不会重新执行赋值逻辑。
而把require("./foo")移到spy创建之后,顺序就变成:
- 先给
createApiInstance设置好带有预期返回值的mock - 再加载foo.js,此时执行
createApiInstance()会拿到你设置的mock函数,api就能被正确赋值。
另外补充一个小问题:你当前的mock返回了Promise,但foo.js里是同步调用createApiInstance(),所以api会变成Promise对象而非可调用的函数。正确的mock应该直接返回jest函数:
const spy = jest .spyOn(apiWrapper, "createApiInstance") .mockImplementation(() => jest.fn(() => Promise.resolve({})));
内容的提问来源于stack exchange,提问作者benhowdle89
相关产品推荐
相关产品推荐

