实用测试分层指南 | AI生成和翻译

Home 2025.09

很好的问题。合理的测试策略主要关乎风险与反馈速度。以下是一个实用的方法,用于决定在哪个层级测试什么——不用表格,只有清晰的规则和示例。


核心原则


测试内容与层级(快速启发法)

1) 单元测试(快速、隔离)

使用时机: 纯/领域逻辑无需 I/O(数据库、HTTP、文件系统)即可测试。

Java/Spring 示例

@ExtendWith(MockitoExtension.class)
class FeeServiceTest {
  @Mock AccountRepo repo;
  @InjectMocks FeeService svc;

  @Test void vipGetsDiscount() {
    when(repo.tier("u1")).thenReturn("VIP");
    assertEquals(Money.of(90), svc.charge("u1", Money.of(100)));
    verify(repo).tier("u1");
  }
}

2) 集成 / 组件测试(真实装配,最少模拟)

使用时机: 需要验证 Spring 装配、序列化、过滤器、数据库查询、事务。

HTTP 切片示例

@WebMvcTest(controllers = OrderController.class)
class OrderControllerTest {
  @Autowired MockMvc mvc;
  @MockBean OrderService svc;

  @Test void createsOrder() throws Exception {
    when(svc.create(any())).thenReturn(new Order("id1", 100));
    mvc.perform(post("/orders").contentType("application/json")
        .content("{\"amount\":100}"))
      .andExpect(status().isCreated())
      .andExpect(jsonPath("$.id").value("id1"));
  }
}

使用 Testcontainers 的数据库测试

@Testcontainers
@SpringBootTest
class RepoIT {
  @Container static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16");
  @Autowired OrderRepo repo;

  @Test void persistsAndQueries() {
    var saved = repo.save(new OrderEntity(null, 100));
    assertTrue(repo.findById(saved.getId()).isPresent());
  }
}

3) API 契约 & 端到端 API 测试

使用时机: 必须保证向后兼容的契约或完整的系统工作流。

API E2E 示例

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class ApiFlowIT {
  @LocalServerPort int port;
  @Test void happyPath() {
    given().port(port).contentType("application/json")
      .body("{\"amount\":100}")
      .when().post("/orders")
      .then().statusCode(201)
      .body("amount", equalTo(100));
  }
}

4) UI 端到端测试(浏览器)

使用时机: 仅有少量关键用户旅程必须在真实浏览器中得到验证:

Selenium 还是 Playwright/Cypress?

Playwright (TypeScript) 示例

import { test, expect } from '@playwright/test';

test('checkout happy path', async ({ page }) => {
  await page.goto('http://localhost:4200');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.getByLabel('Email').fill('u@example.com');
  await page.getByLabel('Password').fill('secret');
  await page.getByRole('button', { name: 'Login' }).click();

  await page.getByText('Add to cart', { exact: true }).first().click();
  await page.getByRole('button', { name: 'Checkout' }).click();
  await expect(page.getByText('Order confirmed')).toBeVisible();
});

如果必须使用 Selenium (Java)

WebDriver d = new ChromeDriver();
d.get("http://localhost:4200");
new WebDriverWait(d, Duration.ofSeconds(10))
  .until(ExpectedConditions.elementToBeClickable(By.id("loginBtn"))).click();

逐层决策(快速流程)

  1. 能否在没有 I/O 的情况下测试? → 能:进行单元测试

  2. 是否依赖框架装配/序列化或数据库查询? → 是:进行集成/组件测试(Spring 切片,Testcontainers)。

  3. 是否是跨服务/公共 API 契约? → 是:进行契约测试(模式/Pact)加上几个 API E2E 流程。

  4. 其价值是否仅在 UI 中可见或是关键 UX? → 是:进行 UI E2E 测试,但仅限于核心旅程。


合理的比例与预算


优先事项(基于风险的检查清单)


CI 流水线形态(实用主义)


测试数据与不稳定性提示


简明经验法则

如果你愿意,告诉我你的前 3–5 个业务关键流程,我可以为你勾勒出每个层级的具体测试清单(包括工具选择和 CI 门控)。


Back Donate