> ## Documentation Index
> Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 编写测试

> 学习如何使用 Bun 内置的 Jest 兼容 API 编写测试，支持异步测试、超时和各种测试修饰符

使用从内置模块 `bun:test` 导入的类似 Jest 的 API 来定义测试。长期来看，Bun 目标是实现完全的 Jest 兼容性；目前支持的 expect 匹配器集合有限。

## 基本用法

To define a test:

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});
```

### 测试分组

使用 `describe` 将测试分组到测试套件中。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test, describe } from "bun:test";

describe("算术", () => {
  test("2 + 2", () => {
    expect(2 + 2).toBe(4);
  });

  test("2 * 2", () => {
    expect(2 * 2).toBe(4);
  });
});
```

### 异步测试

测试可以是异步的。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test } from "bun:test";

test("2 * 2", async () => {
  const result = await Promise.resolve(2 * 2);
  expect(result).toEqual(4);
});
```

或者使用 `done` 回调来标识测试完成。如果在测试定义中包含了 `done` 回调参数，必须调用它，否则测试将挂起。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test } from "bun:test";

test("2 * 2", done => {
  Promise.resolve(2 * 2).then(result => {
    expect(result).toEqual(4);
    done();
  });
});
```

## 超时

可通过给 `test` 传入第三个参数数字来指定单个测试的超时时间（毫秒）。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { test } from "bun:test";

test("wat", async () => {
  const data = await slowOperation();
  expect(data).toBe(42);
}, 500); // 测试必须在 <500ms 内完成
```

在 `bun:test` 中，测试超时会抛出不可捕获的异常，强制测试停止并判定失败。与此同时，测试中生成的任何子进程也会被杀死，以避免后台留下僵尸进程。

如果未被此超时选项或 `jest.setDefaultTimeout()` 覆盖，每个测试的默认超时是 5000ms（5 秒）。

## 重试和重复执行

### test.retry

使用 `retry` 选项可在测试失败时自动重试。只要在指定的尝试次数内成功，测试即通过。适合不稳定、偶尔失败的测试。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { test } from "bun:test";

test(
  "不稳定的网络请求",
  async () => {
    const response = await fetch("https://example.com/api");
    expect(response.ok).toBe(true);
  },
  { retry: 3 }, // 失败时最多重试3次
);
```

### test.repeats

使用 `repeats` 选项可无视测试结果，重复运行测试多次。只要有任一轮失败，即判定测试失败。适合检测不稳定测试或压力测试。注意，`repeats: N` 实际运行次数为 N+1（1次初始 + N次重复）。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { test } from "bun:test";

test(
  "确保测试稳定",
  () => {
    expect(Math.random()).toBeLessThan(1);
  },
  { repeats: 20 }, // 总计运行21次（1次初始 + 20次重复）
);
```

<Note>同一个测试不能同时使用 `retry` 和 `repeats`。</Note>

### 🧟 僵尸进程清理器

当测试超时且通过 `Bun.spawn`、`Bun.spawnSync` 或 `node:child_process` 生成的进程未被杀死时，Bun 会自动杀死它们并在控制台打印消息。防止超时测试后僵尸进程残留后台。

## 测试修饰符

### test.skip

使用 `test.skip` 跳过单个测试。跳过的测试不会被执行。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test } from "bun:test";

test.skip("wat", () => {
  // TODO: 修复此处
  expect(0.1 + 0.2).toEqual(0.3);
});
```

### test.todo

使用 `test.todo` 标记测试为待办状态，这些测试不会被执行。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expect, test } from "bun:test";

test.todo("修复此处", () => {
  myTestFunction();
});
```

要执行 todo 测试并找出哪些测试已通过，可使用 `bun test --todo`。

```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun test --todo
```

```
my.test.ts:
✗ 未实现功能
  ^ 该测试被标记为 todo，且通过。请移除 `.todo` 或确认测试是否正确。

 0 通过
 1 失败
 1 次 expect() 调用
```

启用此参数时，失败的 todo 测试不会导致错误，但通过的 todo 测试将被标记为失败，方便你移除 todo 标记或修复测试。

### test.only

运行特定测试或测试套件，使用 `test.only()` 或 `describe.only()`。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { test, describe } from "bun:test";

test("测试 #1", () => {
  // 不运行
});

test.only("测试 #2", () => {
  // 运行
});

describe.only("仅此", () => {
  test("测试 #3", () => {
    // 运行
  });
});
```

以下命令只会执行测试 #2 和 #3。

```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun test --only
```

以下命令会执行测试 #1、#2 和 #3。

```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun test
```

### test.if

使用 `test.if()` 有条件地运行测试。当条件为真时运行。适用于仅在特定架构或操作系统上运行的测试。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
test.if(Math.random() > 0.5)("有一半概率运行", () => {
  // ...
});

const macOS = process.platform === "darwin";
test.if(macOS)("仅在 macOS 运行", () => {
  // 仅在 macOS 运行
});
```

### test.skipIf

条件跳过测试，使用 `test.skipIf()` 或 `describe.skipIf()`。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const macOS = process.platform === "darwin";

test.skipIf(macOS)("仅在非 macOS 运行", () => {
  // 在非 macOS 运行
});
```

### test.todoIf

条件标记测试为 TODO，使用 `test.todoIf()` 或 `describe.todoIf()`。切换 `skipIf` 或 `todoIf` 可区分“该目标无效”和“计划但未实现”的意图。

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const macOS = process.platform === "darwin";

// TODO: 目前只在 Linux 上实现。
test.todoIf(macOS)("运行在 posix 系统上", () => {
  // 在非 macOS 运行
});
```

### test.failing

当你知道测试当前会失败，但想跟踪它并在它开始通过时获取通知，使用 `test.failing()`。该修饰符对测试结果取反：

* 标有 `.failing()` 的失败测试会被视为通过
* 标有 `.failing()` 的通过测试会失败（并提示该测试现在通过，应修复）

```ts math.test.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 因测试故意失败，这里会通过
test.failing("数学有问题", () => {
  expect(0.1 + 0.2).toBe(0.3); // 由于浮点精度失败
});

// 测试通过，但期望失败，因此测试失败并提示修复
test.failing("修复了 bug", () => {
  expect(1 + 1).toBe(2); // 通过，但我们预期失败
});
```

适合跟踪你计划以后修复的已知错误，或用于测试驱动开发。

## 条件测试用于 describe 块

条件修饰符 `.if()`、`.skipIf()` 和 `.todoIf()` 也可以用于 describe 块，影响套件内的所有测试：

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const isMacOS = process.platform === "darwin";

// 仅在 macOS 上运行整个套件
describe.if(isMacOS)("macOS 特有功能", () => {
  test("特性 A", () => {
    // 仅在 macOS 运行
  });

  test("特性 B", () => {
    // 仅在 macOS 运行
  });
});

// 在 Windows 上跳过整个套件
describe.skipIf(process.platform === "win32")("Unix 功能", () => {
  test("特性 C", () => {
    // Windows 上跳过
  });
});

// 在 Linux 上将整个套件标记为 TODO
describe.todoIf(process.platform === "linux")("即将支持 Linux", () => {
  test("特性 D", () => {
    // Linux 上标记为 TODO
  });
});
```

## 参数化测试

### `test.each` 和 `describe.each`

使用 `test.each` 可针对多个数据集运行相同测试。每个测试用例都会运行一次。

```ts title="math.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const cases = [
  [1, 2, 3],
  [3, 4, 7],
];

test.each(cases)("%p + %p 应该是 %p", (a, b, expected) => {
  expect(a + b).toBe(expected);
});
```

也可以用 `describe.each` 创建针对每个测试用例运行一次的参数化套件：

```ts title="sum.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
describe.each([
  [1, 2, 3],
  [3, 4, 7],
])("add(%i, %i)", (a, b, expected) => {
  test(`返回 ${expected}`, () => {
    expect(a + b).toBe(expected);
  });

  test(`结果大于每个值`, () => {
    expect(a + b).toBeGreaterThan(a);
    expect(a + b).toBeGreaterThan(b);
  });
});
```

### 参数传递

如何将参数传递给测试函数，依赖于测试用例的结构：

* 如果表格行是数组（例如 `[1, 2, 3]`），则每个元素作为单独参数传入
* 如果行不是数组（如对象），则作为单个参数传入

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 数组项作为单独参数传入
test.each([
  [1, 2, 3],
  [4, 5, 9],
])("add(%i, %i) = %i", (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// 对象项作为单个参数传入
test.each([
  { a: 1, b: 2, expected: 3 },
  { a: 4, b: 5, expected: 9 },
])("add($a, $b) = $expected", data => {
  expect(data.a + data.b).toBe(data.expected);
});
```

### 格式化占位符

The following options are available for formatting the test title:

| 占位符  | 描述                 |
| ---- | ------------------ |
| `%p` | pretty-format 美化输出 |
| `%s` | 字符串                |
| `%d` | 数字                 |
| `%i` | 整数                 |
| `%f` | 浮点数                |
| `%j` | JSON               |
| `%o` | 对象                 |
| `%#` | 测试用例索引             |
| `%%` | 单个百分号 (%)          |

#### 示例

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 基本占位符
test.each([
  ["hello", 123],
  ["world", 456],
])("字符串: %s, 数字: %i", (str, num) => {
  // "字符串: hello, 数字: 123"
  // "字符串: world, 数字: 456"
});

// %p 用于 pretty-format 输出
test.each([
  [{ name: "Alice" }, { a: 1, b: 2 }],
  [{ name: "Bob" }, { x: 5, y: 10 }],
])("用户 %p，数据 %p", (user, data) => {
  // "用户 { name: 'Alice' }，数据 { a: 1, b: 2 }"
  // "用户 { name: 'Bob' }，数据 { x: 5, y: 10 }"
});

// %# 用于索引
test.each(["apple", "banana"])("水果 #%# 是 %s", fruit => {
  // "水果 #0 是 apple"
  // "水果 #1 是 banana"
});
```

## 断言计数

Bun 支持验证测试期间调用的断言数量：

### expect.hasAssertions()

使用 `expect.hasAssertions()` 验证测试中至少调用过一次断言：

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
test("异步工作调用断言", async () => {
  expect.hasAssertions(); // 如果没有断言调用，测试失败

  const data = await fetchData();
  expect(data).toBeDefined();
});
```

这对于异步测试尤其有用，确保你的断言真正执行了。

### expect.assertions(count)

使用 `expect.assertions(count)` 验证测试中调用了指定数量的断言：

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
test("恰好两个断言", () => {
  expect.assertions(2); // 如果断言不是精确2个则失败

  expect(1 + 1).toBe(2);
  expect("hello").toContain("ell");
});
```

尤其适合复杂异步代码中确保所有断言都执行。

## 类型测试

Bun 包含用于测试 TypeScript 类型的 `expectTypeOf`，兼容 Vitest。

### expectTypeOf

<Warning>
  这些函数在运行时不执行任何操作 —— 你需要单独运行 TypeScript 来验证类型检查。
</Warning>

`expectTypeOf` 函数提供类型级别的断言，由 TypeScript 的类型检查器检查。测试类型的方法：

1. 使用 `expectTypeOf` 编写类型断言
2. 运行 `bunx tsc --noEmit` 检查类型是否正确

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { expectTypeOf } from "bun:test";

// 基本类型断言
expectTypeOf<string>().toEqualTypeOf<string>();
expectTypeOf(123).toBeNumber();
expectTypeOf("hello").toBeString();

// 对象类型匹配
expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>();

// 函数类型
function greet(name: string): string {
  return `Hello ${name}`;
}

expectTypeOf(greet).toBeFunction();
expectTypeOf(greet).parameters.toEqualTypeOf<[string]>();
expectTypeOf(greet).returns.toEqualTypeOf<string>();

// 数组类型
expectTypeOf([1, 2, 3]).items.toBeNumber();

// Promise 类型
expectTypeOf(Promise.resolve(42)).resolves.toBeNumber();
```

完整的 `expectTypeOf` 匹配器文档请参考 [API 参考](https://bun.com/reference/bun/test/expectTypeOf)。

## 匹配器

Bun 已实现以下匹配器。完全兼容 Jest 正在规划中；[进度跟踪](https://github.com/oven-sh/bun/issues/1825)。

### 基本匹配器

| 状态 | 匹配器                |
| -- | ------------------ |
| ✅  | `.not`             |
| ✅  | `.toBe()`          |
| ✅  | `.toEqual()`       |
| ✅  | `.toBeNull()`      |
| ✅  | `.toBeUndefined()` |
| ✅  | `.toBeNaN()`       |
| ✅  | `.toBeDefined()`   |
| ✅  | `.toBeFalsy()`     |
| ✅  | `.toBeTruthy()`    |
| ✅  | `.toStrictEqual()` |

### 字符串和数组匹配器

| 状态 | 匹配器                   |
| -- | --------------------- |
| ✅  | `.toContain()`        |
| ✅  | `.toHaveLength()`     |
| ✅  | `.toMatch()`          |
| ✅  | `.toContainEqual()`   |
| ✅  | `.stringContaining()` |
| ✅  | `.stringMatching()`   |
| ✅  | `.arrayContaining()`  |

### 对象匹配器

| 状态 | 匹配器                     |
| -- | ----------------------- |
| ✅  | `.toHaveProperty()`     |
| ✅  | `.toMatchObject()`      |
| ✅  | `.toContainAllKeys()`   |
| ✅  | `.toContainValue()`     |
| ✅  | `.toContainValues()`    |
| ✅  | `.toContainAllValues()` |
| ✅  | `.toContainAnyValues()` |
| ✅  | `.objectContaining()`   |

### 数字匹配器

| 状态 | 匹配器                         |
| -- | --------------------------- |
| ✅  | `.toBeCloseTo()`            |
| ✅  | `.closeTo()`                |
| ✅  | `.toBeGreaterThan()`        |
| ✅  | `.toBeGreaterThanOrEqual()` |
| ✅  | `.toBeLessThan()`           |
| ✅  | `.toBeLessThanOrEqual()`    |

### 函数和类匹配器

| 状态 | 匹配器                 |
| -- | ------------------- |
| ✅  | `.toThrow()`        |
| ✅  | `.toBeInstanceOf()` |

### Promise 匹配器

| 状态 | 匹配器           |
| -- | ------------- |
| ✅  | `.resolves()` |
| ✅  | `.rejects()`  |

### 模拟函数匹配器

| 状态 | 匹配器                           |
| -- | ----------------------------- |
| ✅  | `.toHaveBeenCalled()`         |
| ✅  | `.toHaveBeenCalledTimes()`    |
| ✅  | `.toHaveBeenCalledWith()`     |
| ✅  | `.toHaveBeenLastCalledWith()` |
| ✅  | `.toHaveBeenNthCalledWith()`  |
| ✅  | `.toHaveReturned()`           |
| ✅  | `.toHaveReturnedTimes()`      |
| ✅  | `.toHaveReturnedWith()`       |
| ✅  | `.toHaveLastReturnedWith()`   |
| ✅  | `.toHaveNthReturnedWith()`    |

### 快照匹配器

| 状态 | 匹配器                                     |
| -- | --------------------------------------- |
| ✅  | `.toMatchSnapshot()`                    |
| ✅  | `.toMatchInlineSnapshot()`              |
| ✅  | `.toThrowErrorMatchingSnapshot()`       |
| ✅  | `.toThrowErrorMatchingInlineSnapshot()` |

### 工具匹配器

| 状态 | 匹配器                |
| -- | ------------------ |
| ✅  | `.extend`          |
| ✅  | `.anything()`      |
| ✅  | `.any()`           |
| ✅  | `.assertions()`    |
| ✅  | `.hasAssertions()` |

### 未实现

| 状态 | 匹配器                        |
| -- | -------------------------- |
| ❌  | `.addSnapshotSerializer()` |

## 最佳实践

### 使用描述性的测试名称

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 好的示例
test("应计算多个商品含税总价", () => {
  // 测试实现
});

// 避免
test("价格计算", () => {
  // 测试实现
});
```

### 分组相关测试

```ts title="auth.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
describe("用户认证", () => {
  describe("有效凭证时", () => {
    test("应返回用户数据", () => {
      // 测试实现
    });

    test("应设置认证令牌", () => {
      // 测试实现
    });
  });

  describe("无效凭证时", () => {
    test("应抛出认证错误", () => {
      // 测试实现
    });
  });
});
```

### 使用合适的匹配器

```ts title="auth.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 好的示例：使用具体匹配器
expect(users).toHaveLength(3);
expect(user.email).toContain("@");
expect(response.status).toBeGreaterThanOrEqual(200);

// 避免：将逻辑表达式用 toBe 包裹
expect(users.length === 3).toBe(true);
expect(user.email.includes("@")).toBe(true);
expect(response.status >= 200).toBe(true);
```

### 测试错误情况

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
test("无效输入应抛出错误", () => {
  expect(() => {
    validateEmail("not-an-email");
  }).toThrow("Invalid email format");
});

test("应处理异步错误", async () => {
  await expect(async () => {
    await fetchUser("invalid-id");
  }).rejects.toThrow("User not found");
});
```

### 使用初始化和清理

```ts title="example.test.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { beforeEach, afterEach, test } from "bun:test";

let testUser;

beforeEach(() => {
  testUser = createTestUser();
});

afterEach(() => {
  cleanupTestUser(testUser);
});

test("应更新用户资料", () => {
  // 在测试中使用 testUser
});
```
