测试、Mock 与覆盖率

使用 Deno.test、权限、过滤、覆盖率与资源清理建立可靠测试

最小测试

import { assertEquals } from "jsr:@std/assert";

Deno.test("normalizes a user name", () => {
  assertEquals(" Ada ".trim(), "Ada");
});
deno test
deno test --filter "user"
deno test --watch

Deno 2.9 的 test context 内置 snapshot,无需额外导入:

Deno.test("renders a card", async (t) => {
  await t.assertSnapshot(renderCard({ title: "Deno" }));
});

deno test --update-snapshots 创建或更新,并评审 __snapshots__/*.snap diff;CI 只运行普通 deno test,绝不自动更新预期值。

测试默认也受权限沙箱约束。只给需要网络或临时目录的测试相应权限;不要因为一条集成测试使用 -A 运行整个套件。

异步与资源清理

Deno.test("fetches health", async () => {
  const controller = new AbortController();
  try {
    // start resource, assert behavior
  } finally {
    controller.abort();
  }
});

Deno 的 sanitizer 可以帮助发现泄漏的异步操作和资源。注意默认状态:自 Deno 2.8 起,只有 exit sanitizer 默认开启;op 和 resource sanitizer 是 opt-in。需要泄漏检测时在单个测试或测试步骤上显式打开,而不是依赖默认值:

Deno.test({
  name: "closes every handle",
  sanitizeOps: true,
  sanitizeResources: true,
  fn: async () => {
    // ...
  },
});

反之,如果某个测试确有意保留后台操作,在该测试上显式关闭对应 sanitizer 并说明原因,不要全局关闭。

覆盖率

deno test --coverage=coverage
deno coverage --lcov --output=coverage.lcov coverage/

覆盖率不能替代关键失败路径、权限拒绝、超时和取消测试。CI 至少运行 fmt --checklintchecktest

大型套件

deno test --changed=origin/main
deno test --shard=1/3
deno test --retry=2
deno test --repeats=3
deno test --trace-leaks

retry 用于已知偶发失败,成功一次即可通过;repeats 要求每次都通过,用来主动发现不稳定性。不要用 retry 长期掩盖确定性缺陷。

官方参考:TestingCoverageMocking

输入关键词搜索全部文档。