⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

9.1.1 测试也分三六九等——测试分层:单元/集成/端到端测试

不同类型的测试解决不同层面的问题,混淆它们的边界是测试效率低下的根源。

测试金字塔的三个层级

三种测试类型详解

单元测试:最小粒度的验证

单元测试针对单个函数或类进行测试,不涉及外部依赖。

typescript
// utils/price.ts
export function calculateDiscount(price: number, rate: number): number {
  if (rate < 0 || rate > 1) {
    throw new Error('折扣率必须在 0-1 之间');
  }
  return Math.round(price * (1 - rate) * 100) / 100;
}

// __tests__/utils/price.test.ts
import { calculateDiscount } from '@/utils/price';

describe('calculateDiscount', () => {
  it('应正确计算折扣价格', () => {
    expect(calculateDiscount(100, 0.2)).toBe(80);
    expect(calculateDiscount(99.99, 0.1)).toBe(89.99);
  });

  it('应拒绝无效的折扣率', () => {
    expect(() => calculateDiscount(100, -0.1)).toThrow();
    expect(() => calculateDiscount(100, 1.5)).toThrow();
  });
});

适用场景

  • 纯函数、工具函数
  • 数据转换、格式化逻辑
  • 业务规则计算

集成测试:验证模块协作

集成测试验证多个模块之间的协作,通常涉及数据库、API 调用等。

typescript
// services/order.service.ts
export class OrderService {
  constructor(private prisma: PrismaClient) {}

  async createOrder(userId: string, items: CartItem[]) {
    return this.prisma.$transaction(async (tx) => {
      const order = await tx.order.create({
        data: {
          userId,
          status: 'PENDING',
          items: { create: items.map(item => ({ ...item })) },
        },
      });
      
      // 扣减库存
      for (const item of items) {
        await tx.product.update({
          where: { id: item.productId },
          data: { stock: { decrement: item.quantity } },
        });
      }
      
      return order;
    });
  }
}

// __tests__/services/order.service.test.ts
describe('OrderService', () => {
  let prisma: PrismaClient;
  let orderService: OrderService;

  beforeAll(async () => {
    prisma = new PrismaClient();
    orderService = new OrderService(prisma);
  });

  beforeEach(async () => {
    // 清理并准备测试数据
    await prisma.order.deleteMany();
    await prisma.product.deleteMany();
    await prisma.product.create({
      data: { id: 'prod-1', name: '测试商品', price: 100, stock: 10 },
    });
  });

  it('应创建订单并扣减库存', async () => {
    const order = await orderService.createOrder('user-1', [
      { productId: 'prod-1', quantity: 2, price: 100 },
    ]);

    expect(order.status).toBe('PENDING');
    
    const product = await prisma.product.findUnique({ where: { id: 'prod-1' } });
    expect(product?.stock).toBe(8); // 10 - 2 = 8
  });
});

适用场景

  • 服务层业务逻辑
  • 数据库操作
  • 外部 API 调用(使用 Mock)

端到端测试:模拟真实用户

E2E 测试通过浏览器自动化,模拟真实用户操作

typescript
// e2e/checkout.spec.ts (Playwright)
import { test, expect } from '@playwright/test';

test('用户应能完成结账流程', async ({ page }) => {
  // 登录
  await page.goto('/login');
  await page.fill('[name="email"]', 'test@example.com');
  await page.fill('[name="password"]', 'password123');
  await page.click('button[type="submit"]');
  
  // 添加商品到购物车
  await page.goto('/products/1');
  await page.click('button:has-text("加入购物车")');
  
  // 结账
  await page.goto('/cart');
  await page.click('button:has-text("结账")');
  
  // 验证订单创建成功
  await expect(page).toHaveURL(/\/orders\/\d+/);
  await expect(page.locator('h1')).toContainText('订单已创建');
});

适用场景

  • 核心用户流程(注册、登录、支付)
  • 关键业务路径
  • 跨页面交互

三种测试的对比

特性单元测试集成测试E2E 测试
执行速度毫秒级秒级分钟级
维护成本
发现问题逻辑错误集成错误用户体验问题
定位问题精确较精确模糊
覆盖范围
依赖环境数据库/Mock完整环境

AI 协作指南

当你需要为现有代码添加测试时,可以这样与 AI 沟通:

核心意图:告诉 AI 你要测试的是哪个层级的代码

需求定义公式

  • 单元测试:为这个纯函数写单元测试,覆盖正常输入和边界条件
  • 集成测试:为这个服务层方法写集成测试,需要准备测试数据和清理逻辑
  • E2E 测试:为用户登录流程写 Playwright 测试,包含成功和失败场景

关键术语describeitexpectbeforeEachafterEachmock

本节小结

理解测试分层的核心在于:每种测试类型都有其最佳应用场景。单元测试快速精确但覆盖面窄,E2E 测试覆盖面广但维护成本高,集成测试则在两者之间取得平衡。对于 Vibe Coding 开发者来说,把精力集中在服务层的集成测试上,是性价比最高的选择。