# 测试、Mock 与覆盖率

## 最小测试

```ts title="user_test.ts"
import { assertEquals } from "jsr:@std/assert";

Deno.test("normalizes a user name", () => {
  assertEquals(" Ada ".trim(), "Ada");
});
```

```bash
deno test
deno test --filter "user"
deno test --watch
```

Deno 2.9 的 test context 内置 snapshot，无需额外导入：

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

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

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

## 异步与资源清理

```ts
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。需要泄漏检测时在单个测试或测试步骤上显式打开，而不是依赖默认值：

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

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

## 覆盖率

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

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

## 大型套件

```bash
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 长期掩盖确定性缺陷。

官方参考：[Testing](https://docs.deno.com/runtime/test/)、[Coverage](https://docs.deno.com/runtime/test/coverage/)、[Mocking](https://docs.deno.com/runtime/test/mocking/)。
