如何使用node-tap在fastify inject场景下mock函数实现测试全覆盖
问题原因与解决方案
你存在三个核心问题导致mock不生效、catch分支无法覆盖:
- 业务代码缺少
await关键字:你定义的create是async异步函数,但接口中调用时没有加await,异步函数的报错不会被同步catch块捕获,就算create真的抛错也进不了错误分支。 - mock执行顺序错误:node-tap的mock机制仅对mock调用之后导入的模块生效,你提前在测试文件顶部导入了server,并且在初始化app之后才执行mock,此时server已经加载了真实的
query.js模块,mock完全不会生效。 - mock导出项不完整:
query.js同时导出了migration和create两个方法,你只mock了create,server初始化调用migration时会出现方法缺失报错。
修复步骤
第一步:修复server.js的接口逻辑
app.post("/", async (request, reply) => { try { const { body } = request; // 新增await关键字捕获异步错误 const book = await create(body.title); reply.send(book); } catch (error) { reply.code(500).send({ message: "internal server error" }); } });
第二步:调整test.js的mock逻辑与执行顺序
const tap = require("tap"); tap.test("coba", async (t) => { t.test("should success create books", async (t) => { // 正常场景直接加载server const fastify = require("../server"); const app = fastify(); const response = await app.inject({ method: "POST", url: "/", payload: { title: "Hello,World!", }, }); t.equal(response.statusCode, 200); app.close(); // 清除模块缓存,避免影响后续测试的mock delete require.cache[require.resolve("../server")]; delete require.cache[require.resolve("../query.js")]; }); t.test("should throw error", async (t) => { // 先mock query.js,再加载server t.mock("../query.js", { // 补全所有导出项 migration: async () => {}, create: async () => { throw new Error("mock internal error"); } }); // mock完成后再引入server const fastify = require("../server"); const app = fastify(); const response = await app.inject({ method: "POST", url: "/", payload: { title: "Hello,World!", }, }); t.equal(response.statusCode, 500); app.close(); }); });
额外说明
- 你之前尝试mock db.js的写法完全错误,
t.mock的第一个参数是你要mock的目标模块路径,第二个参数是该模块的导出内容,不需要嵌套其他模块的mock逻辑。 - 测试用例之间清除模块缓存是为了避免前一次加载的真实模块覆盖后续的mock规则,保证每个测试用例的隔离性。
内容的提问来源于stack exchange,提问作者mandaputtra
相关产品推荐
相关产品推荐

