测试、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 --check、lint、check 和 test。
大型套件
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 长期掩盖确定性缺陷。