测试代码Skill TestingCode

测试代码技能专注于编写和执行自动化测试,用于验证软件功能、确保代码质量、提高覆盖率,并涵盖单元测试、集成测试、组件测试和端到端测试等多种类型。它帮助开发者提前发现缺陷,优化软件可靠性。关键词:自动化测试、代码覆盖率、软件测试、测试框架、测试驱动开发、边缘案例。

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

name: 测试代码 description: 为功能编写自动化测试,根据验收标准验证功能,并确保代码覆盖率。在编写测试代码、验证功能或向现有代码添加测试覆盖时使用。

测试代码

核心工作流程

测试编写遵循系统化方法:确定范围、理解模式、映射需求、编写测试、验证覆盖。

1. 确定测试范围

阅读项目文档:

  • docs/user-stories/US-###-*.md 用于测试的验收标准
  • docs/feature-spec/F-##-*.md 用于技术需求
  • docs/api-contracts.yaml 用于API规范
  • 现有测试文件以理解模式

选择所需的测试类型:

  • 单元测试: 单个函数、纯逻辑、实用工具
  • 集成测试: 多个组件协同工作、API端点
  • 组件测试: UI组件、用户交互
  • 端到端测试: 完整用户流程、关键路径
  • 合约测试: API请求/响应验证
  • 性能测试: 负载、压力、基准测试

2. 理解现有模式

调查当前测试方法:

  • 测试框架(Jest、Vitest、Pytest等)
  • 模拟模式和实用工具
  • 测试数据夹具和设置/清理
  • 断言风格

如果不熟悉测试结构,请使用 code-finder 代理。

3. 将测试映射到需求

将3-5个验收标准转换为跨测试类型的具体测试用例:

示例映射:

## 用户故事:US-101 用户登录

### 测试用例
1. **单元:认证服务**
   - validateCredentials() 对有效邮箱/密码返回true
   - validateCredentials() 对无效密码返回false
   - checkAccountStatus() 检测锁定账户

2. **集成:登录端点**
   - POST /api/login 使用有效凭据返回200 + 令牌
   - POST /api/login 使用无效凭据返回401 + 错误
   - POST /api/login 使用锁定账户返回403

3. **组件:登录表单**
   - 提交表单调用登录API
   - 错误消息在401响应时显示
   - 成功重定向到 /dashboard

4. **端到端:完整登录流程**
   - 用户输入凭据 → 提交 → 看到仪表板
   - 用户输入错误密码 → 看到错误 → 重试成功

4. 编写测试

单元测试结构:

describe('AuthService', () => {
  describe('validateCredentials', () => {
    it('对有效邮箱和密码返回true', async () => {
      const result = await authService.validateCredentials(
        'user@example.com',
        'ValidPass123'
      );
      expect(result).toBe(true);
    });

    it('对无效密码返回false', async () => {
      const result = await authService.validateCredentials(
        'user@example.com',
        'WrongPassword'
      );
      expect(result).toBe(false);
    });
  });
});

集成测试结构:

describe('POST /api/auth/login', () => {
  beforeEach(async () => {
    await resetTestDatabase();
    await createTestUser({
      email: 'test@example.com',
      password: 'Test123!'
    });
  });

  it('对有效凭据返回200和令牌', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({ email: 'test@example.com', password: 'Test123!' });

    expect(response.status).toBe(200);
    expect(response.body).toHaveProperty('token');
    expect(response.body.token).toMatch(/^eyJ/); // JWT格式
  });

  it('对无效密码返回401', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({ email: 'test@example.com', password: 'WrongPassword' });

    expect(response.status).toBe(401);
    expect(response.body.error).toBe('Invalid credentials');
  });
});

组件测试结构:

describe('LoginForm', () => {
  it('用有效数据提交表单', async () => {
    const mockLogin = jest.fn().mockResolvedValue({ success: true });
    render(<LoginForm onLogin={mockLogin} />);

    await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com');
    await userEvent.type(screen.getByLabelText(/password/i), 'Password123');
    await userEvent.click(screen.getByRole('button', { name: /log in/i }));

    expect(mockLogin).toHaveBeenCalledWith({
      email: 'user@example.com',
      password: 'Password123'
    });
  });

  it('在API失败时显示错误消息', async () => {
    const mockLogin = jest.fn().mockRejectedValue(new Error('Invalid credentials'));
    render(<LoginForm onLogin={mockLogin} />);

    await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com');
    await userEvent.type(screen.getByLabelText(/password/i), 'wrong');
    await userEvent.click(screen.getByRole('button', { name: /log in/i }));

    expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument();
  });
});

端到端测试结构:

test('用户可以成功登录', async ({ page }) => {
  await page.goto('/login');

  await page.fill('[name="email"]', 'test@example.com');
  await page.fill('[name="password"]', 'Test123!');
  await page.click('button:has-text("Log In")');

  await page.waitForURL('/dashboard');
  expect(page.url()).toContain('/dashboard');
});

5. 边缘情况与错误场景

包括边界条件和错误路径:

describe('边缘情况', () => {
  it('优雅处理空邮箱', async () => {
    await expect(
      authService.validateCredentials('', 'password')
    ).rejects.toThrow('Email is required');
  });

  it('处理极长密码', async () => {
    const longPassword = 'a'.repeat(10000);
    await expect(
      authService.validateCredentials('user@example.com', longPassword)
    ).rejects.toThrow('Password too long');
  });

  it('处理网络超时', async () => {
    jest.spyOn(global, 'fetch').mockImplementation(
      () => new Promise((resolve) => setTimeout(resolve, 10000))
    );

    await expect(
      authService.login('user@example.com', 'pass')
    ).rejects.toThrow('Request timeout');
  });
});

始终包括的边缘情况:

  • 空/空输入
  • 最小/最大值
  • 无效格式
  • 网络故障
  • API错误(4xx、5xx)
  • 超时条件
  • 并发操作

6. 测试数据与夹具

创建可重用的测试夹具:

// tests/fixtures/users.ts
export const validUser = {
  email: 'test@example.com',
  password: 'Test123!',
  name: 'Test User'
};

export const invalidUsers = {
  noEmail: { password: 'Test123!' },
  noPassword: { email: 'test@example.com' },
  invalidEmail: { email: 'not-an-email', password: 'Test123!' },
  weakPassword: { email: 'test@example.com', password: '123' }
};

// 在测试中使用
import { validUser, invalidUsers } from './fixtures/users';

it('验证用户数据', () => {
  expect(validate(validUser)).toBe(true);
  expect(validate(invalidUsers.noEmail)).toBe(false);
});

7. 并行测试实施

当测试独立时(不同模块、不同测试类型),启动并行代理:

模式1:基于层

  • 代理1:服务/实用工具的单元测试
  • 代理2:API端点的集成测试
  • 代理3:UI的组件测试
  • 代理4:关键流程的端到端测试

模式2:基于功能

  • 代理1:功能A的所有测试
  • 代理2:功能B的所有测试
  • 代理3:功能C的所有测试

模式3:基于类型

  • 代理1:所有单元测试
  • 代理2:所有集成测试
  • 代理3:所有端到端测试

8. 运行与验证测试

执行测试套件:

# 单元测试
npm test -- --coverage

# 集成测试
npm run test:integration

# 端到端测试
npm run test:e2e

# 所有测试
npm run test:all

验证覆盖:

  • 目标 >80% 代码覆盖率
  • 关键路径100%覆盖
  • 所有验收标准有测试
  • 所有错误场景已测试

质量检查清单

覆盖:

  • [ ] 用户故事的所有验收标准已测试
  • [ ] 快乐路径已覆盖
  • [ ] 包括边缘情况
  • [ ] 错误场景已测试
  • [ ] 边界条件已验证

结构:

  • [ ] 测试遵循现有模式
  • [ ] 清晰的测试描述
  • [ ] 适当的设置/清理
  • [ ] 无随机测试(结果一致)
  • [ ] 测试是隔离的(无相互依赖)

数据:

  • [ ] 测试夹具可重用
  • [ ] 数据库正确播种/重置
  • [ ] 模拟使用适当
  • [ ] 无硬编码测试数据在生产中

集成:

  • [ ] 测试在CI/CD中运行
  • [ ] 覆盖阈值强制执行
  • [ ] 快速反馈(快速测试)
  • [ ] 清晰的失败消息