Jest运行npm run jest时异步回调内测试用例未被识别报错
问题根因
Jest 运行测试分为两个独立阶段:
- 测试收集阶段:同步执行所有
describe回调,收集所有同步声明的it/test用例,该阶段不会等待任何异步回调执行。 - 测试执行阶段:按顺序运行收集到的用例、执行对应生命周期钩子,这个阶段才支持等待异步操作完成。
你将it测试用例写在hgetall的异步回调内,等Redis异步IO返回、回调触发准备注册用例时,Jest早已完成测试收集流程,没有识别到任何测试用例,因此抛出Your test suite must contain at least one test错误。从你加的debug日志也能验证时序问题:debug2会在测试收集阶段打印,debug3到debug6要等Redis操作返回才会执行,完全赶不上测试用例注册的时间窗口。
修复方案
核心原则:所有it测试用例必须在describe回调内同步声明,异步逻辑全部放到测试用例内部,或者beforeAll/beforeEach这类官方提供的生命周期钩子中处理。
修复后的可运行代码示例:
const { promisify } = require('util'); describe("some testing", () => { describe("redisProcessor", () => { let queryResult; let isContentMatch = true; // 将回调形式的hgetall包装为Promise形式,方便用async/await控制时序 const hgetallAsync = promisify(aisListenerSpec.redisSetter.client.hgetall) .bind(aisListenerSpec.redisSetter.client); // 所有前置异步操作放到beforeAll钩子中,等待全部执行完成后再运行测试用例 beforeAll(async () => { aisListenerSpec.run("123456"); // 如果redisProcessor是异步写入方法,必须加await等待写入完成再查询 await aisListenerSpec.redisProcessor(JSON.stringify(validMessage)); // 等待Redis查询结果返回 queryResult = await hgetallAsync("________someString______"); // 提前做字段比对 for (const key in queryResult) { if (validMessage[key] !== undefined && JSON.stringify(validMessage[key]) !== queryResult[key]) { isContentMatch = false; } } }); // 所有it用例同步注册,Jest在收集阶段可以正常识别 it("________someString______${missionId} should contain 2 required fields: messageTime and missionId", () => { expect(queryResult["messageTime"]).toBeTruthy(); expect(queryResult["missionId"]).toBe("123456"); }); it("________someString______${missionId} fields should match validMessage", () => { expect(isContentMatch).toBeTruthy(); }); }); });
注意事项
- 如果你使用的是ioredis、或4.0以上版本的node-redis客户端,原生支持Promise返回,不需要手动做promisify包装,直接
await对应方法即可。 - 不要在
describe回调的最外层直接执行数据库写入、查询这类带IO的异步操作,这类有副作用的逻辑统一放到生命周期钩子或测试用例内部,避免和Jest的执行时序冲突。 - 如果不想用async/await语法,也可以用Jest提供的
done回调处理异步逻辑,只要保证it本身是同步注册的即可,示例如下:
it("verify redis data with done callback", (done) => { aisListenerSpec.redisSetter.client.hgetall("________someString______", (err, result) => { expect(result.missionId).toBe("123456"); // 异步操作完成后必须调用done,否则Jest会等待超时 done(); }); });
内容的提问来源于stack exchange,提问作者Thekatsman
相关产品推荐
相关产品推荐

