Deno中模拟内部导入logger模块的替代方案有哪些?
在Deno中模拟Logger模块的替代方案
问题背景
我有一个导入了logger模块的utils模块,需要为utils编写单元测试并模拟logger。现有代码如下:
utils.ts
import logger from "logger"; export function add(j: number, k: number) { logger.log(`Adding ${j} and ${k}`); return j + k; }
logger.ts
export default { log: (msg: string) => console.info(msg), }
utils.test.ts(初始版本)
import { assertEquals } from "@std/assert"; import { add } from "../src/utils.ts"; Deno.test(function testAdd() { assertEquals(add(1, 2), 3); });
当前使用import map模拟时,必须手动复制deno.json中的所有依赖到导入映射文件(因为导入映射会完全覆盖配置中的列表),导致每次添加依赖都要手动更新,非常繁琐。同时不想用依赖注入(Logger是横切关注点,全量注入成本太高),需要更优的模拟方案。
解决方案
方案1:使用Deno的--replace参数(推荐)
Deno的测试命令支持--replace参数,可以直接替换特定模块的导入路径,无需修改import map或配置文件。
- 创建模拟的logger文件
logger-stub.ts:
// logger-stub.ts export default { log: () => {} // 空实现,避免输出日志 };
- 运行测试时指定替换规则:
deno test --replace logger=./logger-stub.ts
这样Deno会自动将所有对logger模块的导入替换为./logger-stub.ts,同时保留deno.json中其他依赖的配置,无需手动维护导入映射。
如果需要验证logger的调用情况,可以给模拟添加跟踪逻辑:
// logger-stub.ts let logCalls: string[] = []; export default { log: (msg: string) => { logCalls.push(msg); }, // 用于测试中获取调用记录 getLogCalls: () => [...logCalls], // 重置调用记录,避免测试间干扰 resetLogCalls: () => { logCalls = []; } };
对应的测试文件更新:
// utils.test.ts import { assertEquals } from "@std/assert"; import { add } from "../src/utils.ts"; import loggerStub from "./logger-stub.ts"; Deno.test(function testAdd() { loggerStub.resetLogCalls(); const result = add(1, 2); assertEquals(result, 3); assertEquals(loggerStub.getLogCalls(), ["Adding 1 and 2"]); });
方案2:使用@std/testing/mock的mockModule API
Deno标准库的@std/testing/mock提供了mockModule函数,可以动态模拟模块的导出内容,无需额外创建 stub 文件。
- 安装标准库测试模块:
deno add @std/testing/mock
- 修改测试文件,利用动态导入和模块模拟:
// utils.test.ts import { assertEquals } from "@std/assert"; import { mockModule, spy } from "@std/testing/mock"; // 模拟logger模块,创建一个spy函数跟踪log调用 const loggerMock = mockModule("logger", () => { const logSpy = spy(() => {}); return { default: { log: logSpy } }; }); // 必须在mock之后动态导入utils模块 const { add } = await import("../src/utils.ts"); Deno.test(function testAdd() { const result = add(1, 2); assertEquals(result, 3); // 验证log方法被调用了一次,且参数正确 assertEquals(loggerMock.default.log.calls.length, 1); assertEquals(loggerMock.default.log.calls[0][0], "Adding 1 and 2"); }); // 测试结束后恢复原模块,避免影响其他测试 Deno.test.afterAll(() => { loggerMock.restore(); });
方案3:环境变量驱动的条件导入
在业务代码中根据环境变量决定导入的 logger 实现,无需修改测试命令,但会对业务代码有轻微侵入:
// utils.ts let logger; // 根据TEST_ENV环境变量选择导入目标 if (Deno.env.get("TEST_ENV") === "true") { logger = await import("../test/logger-stub.ts").then(m => m.default); } else { logger = await import("logger").then(m => m.default); } export function add(j: number, k: number) { logger.log(`Adding ${j} and ${k}`); return j + k; }
运行测试时设置环境变量:
TEST_ENV=true deno test
内容的提问来源于stack exchange,提问作者Lucio Paiva
相关产品推荐
相关产品推荐

