Jest测试既不返回Promise也不接受回调的异步函数问题
测试无Promise/回调的异步函数的解决方案
你的问题核心在于getUser里的异步操作(fetch+setTimeout)还没完成,测试就执行了断言。下面提供两种可行的解决思路:
方法一:改造原函数(推荐)
最规范的做法是让异步函数返回Promise,这样测试就能明确追踪异步状态。修改index.js如下:
// index.js export let usersList = []; export function getUser() { return fetch("https://jsonplaceholder.typicode.com/users") .then((x) => x.json()) .then((users) => { return new Promise(resolve => { setTimeout(() => { usersList = users; resolve(); // 通知Promise完成 }, 2000); }); }); } getUser();
对应的测试用例可以用async/await来等待异步完成:
// index.test.js const { getUser, usersList } = require("./index"); // 模拟fetch,避免真实网络请求 global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(new Array(10).fill({})) // 模拟10个用户数据 }) ); it("should fetch users and assign to usersList", async () => { await getUser(); // 等待异步操作完成 expect(usersList.length).toBe(10); });
方法二:不修改原函数,用Jest的定时器模拟
如果无法改动原函数,可以通过Jest的假定时器和fetch模拟来实现测试:
// index.test.js const { getUser, usersList } = require("./index"); // 模拟fetch global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(new Array(10).fill({})) }) ); beforeEach(() => { jest.useFakeTimers(); // 启用假定时器 jest.clearAllMocks(); // 清空mock usersList.length = 0; // 重置用户列表 }); afterEach(() => { jest.useRealTimers(); // 恢复真实定时器 }); it("should fetch users and assign to usersList", async () => { getUser(); // 先让fetch的Promise链执行到setTimeout这一步 await Promise.resolve(); // 快进2000ms,触发setTimeout里的逻辑 jest.advanceTimersByTime(2000); // 再等待一次微任务,确保赋值操作完成 await Promise.resolve(); expect(usersList.length).toBe(10); expect(fetch).toHaveBeenCalledTimes(1); });
注意事项
- 无论哪种方法,都要模拟
fetch,避免测试依赖真实网络请求,保证测试的稳定性和速度。 - 方法一的优势在于让异步逻辑更清晰,后续维护和测试都更简单,是优先推荐的方案。
内容的提问来源于stack exchange,提问作者Sameer
相关产品推荐
相关产品推荐

