using-tests

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Working with Tests

测试工作指南

Testing strategy and workflow. Tests run in parallel with isolated data per suite. Prioritize Playwright for UI, integration tests for APIs, unit tests for logic.
测试策略与工作流。每个测试套件使用隔离数据并行运行测试。优先使用Playwright进行UI测试,集成测试用于API,单元测试用于逻辑验证。

Testing Strategy

测试策略

Follow this hierarchy when deciding what kind of test to write:
  1. Playwright tests (browser) - Preferred for most features
  2. Integration tests (API) - When Playwright is not practical
  3. Unit tests (pure functions) - Only for complex isolated logic

在决定编写哪种类型的测试时,请遵循以下优先级:
  1. Playwright测试(浏览器端)- 大多数功能的首选测试方式
  2. 集成测试(API端)- 当Playwright不适用时
  3. 单元测试(纯函数)- 仅用于复杂的独立逻辑

When to Use Each Test Type

各类测试的适用场景

Playwright Tests (Default Choice)

Playwright测试(默认选择)

Write Playwright tests when the feature involves:
  • User interactions (clicking, typing, navigation)
  • Visual feedback (toasts, loading states, error messages)
  • Form submissions and validation
  • Multi-step UI flows
  • Protected routes and redirects
  • Accessibility behavior
Example features best tested with Playwright:
  • Sign-in flow with error handling
  • Chat creation and deletion with confirmation dialogs
  • Theme toggle
  • Form validation messages
  • Navigation between pages
当功能涉及以下场景时,编写Playwright测试:
  • 用户交互(点击、输入、导航)
  • 视觉反馈(提示框、加载状态、错误信息)
  • 表单提交与验证
  • 多步骤UI流程
  • 受保护路由与重定向
  • 可访问性行为
最适合用Playwright测试的功能示例:
  • 包含错误处理的登录流程
  • 带确认对话框的聊天创建与删除
  • 主题切换
  • 表单验证提示
  • 页面间导航

Integration Tests

集成测试

Write integration tests when:
  • Testing API responses directly (status codes, JSON structure)
  • Verifying database state after operations
  • Testing server-side logic without UI
  • Playwright would be too slow or complex for the scenario
Example features best tested with integration tests:
  • API route returns correct status codes
  • User creation populates database correctly
  • Session cookies are set on sign-in
  • Protected API routes return 401/403
在以下场景编写集成测试:
  • 直接测试API响应(状态码、JSON结构)
  • 验证操作后的数据库状态
  • 无需UI即可测试服务端逻辑
  • Playwright在该场景下过慢或过于复杂
最适合用集成测试的功能示例:
  • API路由返回正确的状态码
  • 用户创建操作正确填充数据库
  • 登录时设置会话Cookie
  • 受保护的API路由返回401/403

Unit Tests

单元测试

Write unit tests only when:
  • Testing pure functions with complex logic
  • Testing code with many edge cases
  • Testing type narrowing and error messages
  • The function has no external dependencies
Example features best tested with unit tests:
  • Assertion helpers
  • Config schema validation
  • Data transformation functions
  • Utility functions

仅在以下场景编写单元测试:
  • 测试包含复杂逻辑的纯函数
  • 测试存在多种边缘情况的代码
  • 测试类型收窄与错误信息
  • 函数无外部依赖
最适合用单元测试的功能示例:
  • 断言辅助工具
  • 配置 schema 验证
  • 数据转换函数
  • 工具函数

Running Tests

运行测试

All tests run against an isolated Neon database branch that auto-deletes after 1 hour.
bash
bun run test              # All tests with isolated Neon branch
bun run test:playwright   # Browser tests only
bun run test:integration  # Integration tests only
bun run test:unit         # Unit tests only

所有测试都在独立的Neon数据库分支上运行,分支会在1小时后自动删除。
bash
bun run test              # 使用独立Neon分支运行所有测试
bun run test:playwright   # 仅运行浏览器测试
bun run test:integration  # 仅运行集成测试
bun run test:unit         # 仅运行单元测试

Folder Structure

文件夹结构

src/
├── lib/
│   ├── common/
│   │   ├── assert.ts
│   │   └── assert.test.ts      # Unit test (co-located)
│   └── config/
│       ├── schema.ts
│       └── schema.test.ts      # Unit test (co-located)
tests/
├── integration/
│   ├── llms.test.ts            # Integration test
│   ├── r.test.ts
│   ├── mcp/
│   │   └── route.test.ts
│   └── recipes/
│       └── [slug]/
│           └── route.test.ts
└── playwright/
    ├── auth.spec.ts            # Playwright test
    ├── chat.spec.ts
    ├── home.spec.ts
    └── lib/
        └── test-user.ts        # Playwright-specific helpers

src/
├── lib/
│   ├── common/
│   │   ├── assert.ts
│   │   └── assert.test.ts      # 单元测试(与代码同目录)
│   └── config/
│       ├── schema.ts
│       └── schema.test.ts      # 单元测试(与代码同目录)
tests/
├── integration/
│   ├── llms.test.ts            # 集成测试
│   ├── r.test.ts
│   ├── mcp/
│   │   └── route.test.ts
│   └── recipes/
│       └── [slug]/
│           └── route.test.ts
└── playwright/
    ├── auth.spec.ts            # Playwright测试
    ├── chat.spec.ts
    ├── home.spec.ts
    └── lib/
        └── test-user.ts        # Playwright专用辅助工具

Writing Tests for New Features

为新功能编写测试

Step 1: Determine Test Type

步骤1:确定测试类型

Ask: "How would a user verify this feature works?"
  • If through the UI → Playwright test
  • If through API calls → Integration test
  • If by calling a function directly → Unit test
思考:“用户会如何验证该功能正常工作?”
  • 如果通过UI操作 → Playwright测试
  • 如果通过API调用 → 集成测试
  • 如果直接调用函数 → 单元测试

Step 2: Create Test File

步骤2:创建测试文件

Playwright tests:
tests/playwright/{feature}.spec.ts
typescript
import { test, expect } from "@playwright/test";

test.describe("Feature Name", () => {
  test("should do expected behavior", async ({ page }) => {
    await page.goto("/feature");
    // Test implementation
  });
});
Integration tests:
tests/integration/{feature}.test.ts
For API routes, import the handler directly for faster, more reliable tests:
typescript
import { describe, it, expect } from "bun:test";
import { GET } from "@/app/api/feature/route";

describe("GET /api/feature", () => {
  it("should return expected response", async () => {
    const response = await GET();

    expect(response.status).toBe(200);
    const data = await response.json();
    expect(data.value).toBeDefined();
  });
});
Unit tests:
src/lib/{domain}/{file}.test.ts
(co-located)
typescript
import { describe, it, expect } from "bun:test";
import { myFunction } from "./my-file";

describe("myFunction", () => {
  it("should do expected behavior", () => {
    expect(myFunction()).toBe("expected");
  });
});

Playwright测试:
tests/playwright/{feature}.spec.ts
typescript
import { test, expect } from "@playwright/test";

test.describe("Feature Name", () => {
  test("should do expected behavior", async ({ page }) => {
    await page.goto("/feature");
    // 测试实现代码
  });
});
集成测试:
tests/integration/{feature}.test.ts
对于API路由,直接导入处理函数以实现更快速、更可靠的测试:
typescript
import { describe, it, expect } from "bun:test";
import { GET } from "@/app/api/feature/route";

describe("GET /api/feature", () => {
  it("should return expected response", async () => {
    const response = await GET();

    expect(response.status).toBe(200);
    const data = await response.json();
    expect(data.value).toBeDefined();
  });
});
单元测试:
src/lib/{domain}/{file}.test.ts
(与代码同目录)
typescript
import { describe, it, expect } from "bun:test";
import { myFunction } from "./my-file";

describe("myFunction", () => {
  it("should do expected behavior", () => {
    expect(myFunction()).toBe("expected");
  });
});

Test Data Management

测试数据管理

Database Isolation

数据库隔离

Tests run against isolated Neon branches. Each test run:
  1. Creates a fresh schema-only branch
  2. Runs tests against the branch
  3. Branch auto-deletes after 1 hour
This ensures tests don't interfere with production data.
测试在独立的Neon分支上运行。每次测试运行:
  1. 创建一个全新的仅包含 schema 的分支
  2. 在该分支上运行测试
  3. 分支会在1小时后自动删除
这确保测试不会影响生产数据。

Parallel Test Isolation

并行测试隔离

Tests run in parallel by default. Each test suite must use its own test data to avoid conflicts:
  • Different test users - Each spec file should create unique users with distinct emails
  • Different resources - Tests creating chats, sessions, etc. should not depend on shared state
  • No cleanup required - The branch TTL handles cleanup automatically
typescript
// auth.spec.ts - uses auth-specific test user
const testUser = await createTestUser({
  email: `auth-test-${uuid}@example.com`,
});

// chat.spec.ts - uses chat-specific test user
const testUser = await createTestUser({
  email: `chat-test-${uuid}@example.com`,
});
Avoid patterns that rely on global state or specific database contents existing from other tests.

默认情况下,测试并行运行。每个测试套件必须使用自己的测试数据以避免冲突:
  • 不同的测试用户 - 每个 spec 文件应创建具有唯一邮箱的独立用户
  • 不同的资源 - 创建聊天、会话等的测试不应依赖共享状态
  • 无需清理 - 分支的自动过期机制会处理清理工作
typescript
// auth.spec.ts - 使用认证专用测试用户
const testUser = await createTestUser({
  email: `auth-test-${uuid}@example.com`,
});

// chat.spec.ts - 使用聊天专用测试用户
const testUser = await createTestUser({
  email: `chat-test-${uuid}@example.com`,
});
避免依赖全局状态或其他测试留下的特定数据库内容的模式。

Common Patterns

常见模式

Testing Protected Routes (Playwright)

测试受保护路由(Playwright)

typescript
test("should redirect unauthenticated user", async ({ page }) => {
  await page.goto("/protected-page");
  await expect(page).toHaveURL(/sign-in/);
});
typescript
test("should redirect unauthenticated user", async ({ page }) => {
  await page.goto("/protected-page");
  await expect(page).toHaveURL(/sign-in/);
});

Testing Error States (Playwright)

测试错误状态(Playwright)

typescript
test("should show error for invalid input", async ({ page }) => {
  await page.goto("/form");
  await page.getByRole("button", { name: /submit/i }).click();

  await expect(page.getByText(/error|required/i)).toBeVisible({
    timeout: 5000,
  });
});
typescript
test("should show error for invalid input", async ({ page }) => {
  await page.goto("/form");
  await page.getByRole("button", { name: /submit/i }).click();

  await expect(page.getByText(/error|required/i)).toBeVisible({
    timeout: 5000,
  });
});

Testing API Responses (Integration)

测试API响应(集成测试)

Import route handlers directly for cleaner tests:
typescript
import { GET } from "@/app/api/endpoint/route";

it("should return 200 for valid request", async () => {
  const response = await GET();
  expect(response.status).toBe(200);
});

直接导入路由处理函数以编写更简洁的测试:
typescript
import { GET } from "@/app/api/endpoint/route";

it("should return 200 for valid request", async () => {
  const response = await GET();
  expect(response.status).toBe(200);
});

Debugging Failed Tests

调试失败的测试

Playwright

Playwright

bash
bunx playwright test --headed              # Watch browser
bunx playwright test --debug               # Step through test
bunx playwright show-report                # View HTML report
bash
bunx playwright test --headed              # 查看浏览器运行过程
bunx playwright test --debug               # 单步调试测试
bunx playwright show-report                # 查看HTML测试报告

Integration/Unit

集成/单元测试

bash
bun test --only "test name"                # Run single test
bun test --watch                           # Re-run on changes
bash
bun test --only "test name"                # 运行单个测试
bun test --watch                           # 代码变更时重新运行测试

View test artifacts

查看测试产物

Failed Playwright tests save screenshots and traces to
test-results/
. Check this folder when CI fails.
失败的Playwright测试会将截图和追踪信息保存到
test-results/
目录。当CI失败时,请检查该文件夹。