You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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或配置文件。

  1. 创建模拟的logger文件logger-stub.ts:
// logger-stub.ts
export default {
    log: () => {} // 空实现,避免输出日志
};
  1. 运行测试时指定替换规则:
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 文件。

  1. 安装标准库测试模块:
deno add @std/testing/mock
  1. 修改测试文件,利用动态导入和模块模拟:
// 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 22:40:08