测试模式技能Skill testing-patterns

此技能提供 Jest 和 Playwright 测试的指南和模式,用于编写单元测试、集成测试、设置测试夹具、验证 RLS 执行,并提供证据模板,帮助实现一致有效的软件测试。关键词:测试模式,Jest,Playwright,RLS,证据模板,软件测试,测试指南。

测试 0 次安装 0 次浏览 更新于 3/15/2026

名称: 测试模式 描述: Jest 和 Playwright 的测试模式。在编写测试、设置测试夹具或验证 RLS 执行时使用。路由到现有测试约定并提供证据模板。

测试模式技能

目的

指导一致和有效的测试。路由到现有测试模式并为 Linear 提供证据模板。

何时应用此技能

在以下情况下调用此技能:

  • 编写新的单元测试
  • 创建集成测试
  • 设置带 RLS 的测试夹具
  • 运行测试套件
  • 为 Linear 打包测试证据

关键规则

❌ 禁止的模式

// 禁止:在测试中直接调用 Prisma(绕过 RLS)
const user = await prisma.user.findUnique({ where: { user_id } });

// 禁止:共享测试状态(导致不稳定测试)
let sharedUser: User;
beforeAll(() => { sharedUser = createUser(); });

// 禁止:硬编码 ID(测试污染)
const userId = "user-123";

// 禁止:缺少清理(泄漏测试)
it("创建用户", async () => {
  await prisma.user.create({ data: userData });
  // 没有清理!
});

✅ 正确的模式

// 正确:使用 RLS 上下文助手
const user = await withSystemContext(prisma, "测试", async (client) => {
  return client.user.findUnique({ where: { user_id } });
});

// 正确:每个测试的隔离测试状态
beforeEach(() => {
  const testUser = createTestUser();
});

// 正确:唯一标识符
const userId = `user-${crypto.randomUUID()}`;
const email = `test-${Date.now()}@example.com`;

// 正确:适当的清理
afterEach(async () => {
  await withSystemContext(prisma, "测试", async (client) => {
    await client.user.deleteMany({ where: { email: { contains: "test-" } } });
  });
});

测试目录结构

__tests__/
├── unit/              # 快速、隔离的测试
│   ├── components/    # React 组件测试
│   ├── lib/           # 库函数测试
│   ├── services/      # 服务层测试
│   └── user/          # 用户助手测试
├── integration/       # API 和数据库测试
├── database/          # 数据库助手测试
├── e2e/               # 端到端测试 (Playwright)
├── payments/          # 支付流程测试
└── setup.ts           # 全局测试设置

配置文件

  • Jest 配置: jest.config.js
  • 测试设置: __tests__/setup.ts
  • Playwright 配置: playwright.config.ts

RLS 感知测试

设置测试上下文

在测试中始终使用 RLS 上下文助手:

import { withUserContext, withSystemContext } from "@/lib/rls-context";
import { prisma } from "@/lib/prisma";

describe("用户支付", () => {
  const testUserId = "测试用户-123";

  beforeEach(async () => {
    // 使用 RLS 上下文创建测试用户
    await withSystemContext(prisma, "测试", async (client) => {
      await client.user.create({
        data: {
          user_id: testUserId,
          email: `test-${Date.now()}@example.com`,
          first_name: "测试",
          last_name: "用户",
        },
      });
    });
  });

  it("应仅看到自己的支付", async () => {
    const payments = await withUserContext(
      prisma,
      testUserId,
      async (client) => {
        return client.payments.findMany();
      },
    );
    // RLS 确保仅返回此用户的支付
    expect(payments.every((p) => p.user_id === testUserId)).toBe(true);
  });
});

测试隔离

使用唯一标识符防止测试污染:

const uniqueEmail = `test-${Date.now()}@example.com`;
const uniqueUserId = `user-${crypto.randomUUID()}`;

测试命令

# 运行所有单元测试
yarn test:unit

# 运行集成测试
yarn test:integration

# 运行特定测试文件
yarn jest __tests__/unit/components/my-component.test.tsx

# 运行匹配模式的测试
yarn jest --testNamePattern="应处理"

# 运行带覆盖率的测试
yarn test:unit --coverage

# 运行 E2E 测试
yarn test:e2e

常见模式

组件测试

import { render, screen, fireEvent } from "@testing-library/react";
import { MyComponent } from "@/components/my-component";

describe("MyComponent", () => {
  it("正确渲染", () => {
    render(<MyComponent />);
    expect(screen.getByRole("button")).toBeInTheDocument();
  });

  it("处理点击事件", async () => {
    const onClickMock = jest.fn();
    render(<MyComponent onClick={onClickMock} />);

    fireEvent.click(screen.getByRole("button"));
    expect(onClickMock).toHaveBeenCalledTimes(1);
  });
});

API 路由测试

import { GET } from "@/app/api/my-route/route";
import { NextRequest } from "next/server";

describe("GET /api/my-route", () => {
  it("返回 200 带数据", async () => {
    const request = new NextRequest("http://localhost:3000/api/my-route");
    const response = await GET(request);

    expect(response.status).toBe(200);
    const data = await response.json();
    expect(data).toHaveProperty("success", true);
  });
});

模拟 Prisma

jest.mock("@/lib/prisma", () => ({
  prisma: {
    user: {
      findUnique: jest.fn(),
      create: jest.fn(),
    },
  },
}));

Linear 的证据模板

完成测试工作时,附加此证据块:

**测试执行证据**

**测试套件**: [单元/集成/E2E]
**更改的文件**: [列出文件]

**测试结果:**

- 总测试: [X]
- 通过: [X]
- 失败: [0]
- 跳过: [X]

**覆盖率** (如适用):

- 语句: X%
- 分支: X%
- 函数: X%
- 行: X%

**运行的命令:**

\`\`\`bash
yarn test:unit --coverage
\`\`\`

**输出:**
[粘贴相关测试输出]

推送前验证

推送前始终运行:

yarn ci:validate

这运行:

  • 类型检查
  • ESLint
  • 单元测试
  • 格式检查

权威参考

  • Jest 配置: jest.config.js
  • 测试设置: __tests__/setup.ts
  • RLS 上下文: lib/rls-context.ts
  • CI 验证: package.json 脚本