单元测试 vs. 集成测试 vs. E2E测试
- 什么的单元测试、集成测试和E2E测试?
-
- [单元测试 vs 集成测试](#单元测试 vs 集成测试)
- [📦 单元测试(Unit Test)](#📦 单元测试(Unit Test))
- [🔗 集成测试(Integration Test)](#🔗 集成测试(Integration Test))
-
-
- 通俗理解:测试几个零件组装后能不能用。
- 后端例子:PartCrudApiTest.java
- [🎭 E2E 测试(End-to-End Test)](#🎭 E2E 测试(End-to-End Test))
-
- 通俗理解:测试真实用户使用的完整流程。
- 理解:
- [前端例子:part-crud.spec.ts(E2E 测试,属于集成测试)](#前端例子:part-crud.spec.ts(E2E 测试,属于集成测试))
- 对比总结
- 我的项目中的测试
-
- 最佳实践
什么的单元测试、集成测试和E2E测试?
单元测试 vs 集成测试
📦 单元测试(Unit Test)
定义:测试单个组件/函数,隔离其他依赖(用 Mock 替代)。
通俗理解:测试单个零件能不能用。
说人话就是:在不运行完整前后端系统的情况下,通过构造假设数据(Mock/Stub)来验证业务逻辑的正确性。
类比:造汽车
你造了一个发动机,单元测试就是:
- 把发动机单独拿出来
- 不装到车上
- 用模拟器测试:能不能启动?转速正常吗?
- 不管它和轮胎、方向盘配不配
优点:快!几毫秒就测完
缺点:不知道装到车上能不能跑
java
// 单元测试:只测 PartService.createPart() 这一个方法
@Test
void testCreatePart() {
// 假装数据库返回成功(不真的连数据库)
when(partMapper.insert(any())).thenReturn(1);
// 测试这个方法的逻辑
Part result = partService.createPart(dto);
// 验证:零件编号生成了吗?
assertNotNull(result.getPartNumber());
}
后端例子:AuthServiceTest.java
java
@ExtendWith(MockitoExtension.class) // 使用 Mockito 框架
class AuthServiceTest {
@Mock // 模拟依赖(不真实调用数据库)
private SysUserMapper sysUserMapper;
@Mock
private BCryptPasswordEncoder passwordEncoder;
@InjectMocks // 被测试的对象(注入上面的 Mock)
private AuthService authService;
@Test
void testLogin_success() {
// 1. 准备测试数据
SysUser user = new SysUser();
user.setUsername("admin");
user.setPassword("$2a$10$encoded");
// 2. 模拟依赖行为(不真实查数据库)
when(sysUserMapper.selectOne(any())).thenReturn(user);
when(passwordEncoder.matches("admin123", user.getPassword())).thenReturn(true);
// 3. 执行被测方法
LoginDTO dto = new LoginDTO("admin", "admin123");
SaTokenInfo result = authService.login(dto);
// 4. 验证结果
assertNotNull(result.getTokenValue());
}
}
特点:
- ✅ 快:不启动 Spring 容器,不连数据库,毫秒级
- ✅ 隔离:只测 AuthService.login() 逻辑,不测 Mapper/数据库
- ✅ 可控:Mock 可以模拟任何场景(用户不存在、密码错误等)
- ❌ 不真实:无法发现 SQL 错误、网络问题
🔗 集成测试(Integration Test)
定义:测试多个组件协作,使用真实依赖(真实数据库/网络)。
通俗理解:测试几个零件组装后能不能用。
类比:造汽车(续)
说人话:与单元测试的区别是,不再"造假"。
你把发动机 + 变速箱 + 轮子组装好,集成测试就是:
- 真的装到车上
- 真的加油
- 真的踩油门
- 看能不能跑起来
java
// 集成测试:测试完整的 API 调用链路
@Test
void testCreatePartApi() {
// 发送真实的 HTTP 请求(会真的连数据库)
Response resp = RestAssured
.post("http://localhost:8082/api/parts");
// 验证:数据库里真的插入了吗?
Part dbPart = partMapper.selectOneById(partId);
assertNotNull(dbPart);
}
后端例子:PartCrudApiTest.java
java
@Feature("零部件管理")
class PartCrudApiTest extends BaseApiTest { // 继承基类(启动真实服务)
@Test
@Description("正常创建零部件,期望返回 200 及自动生成的零件编号")
void testCreatePart_success() {
Map<String, Object> body = Map.of(
"name", "测试零件-API-001",
"type", "MECHANICAL"
);
// 发送真实 HTTP 请求到 http://localhost:8082/api/parts
Response resp = RestAssured.given(REQUEST_SPEC)
.body(body)
.post("/api/parts");
// 验证响应
resp.then().statusCode(200)
.body("code", equalTo(200))
.body("data.partNumber", startsWith("P")); // 验证编号生成逻辑
// 验证数据库(真实插入了)
Long partId = resp.jsonPath().getLong("data.id");
Part dbPart = partMapper.selectOneById(partId);
assertEquals("测试零件-API-001", dbPart.getName());
}
}
特点:
- ✅ 真实:测试完整链路 Controller → Service → Mapper → MySQL
- ✅ 发现问题:能发现 SQL 错误、事务问题、网络超时
- ❌ 慢:需要启动 Spring Boot + 连数据库,秒级
- ❌ 依赖环境:需要 MySQL/Redis 运行
🎭 E2E 测试(End-to-End Test)
定义:最广泛的集成测试(全系统)
通俗理解:测试真实用户使用的完整流程。
类比:造汽车(续)
E2E 测试就是:
- 找一个真人司机
- 让他从家里开车去公司
- 看会不会:
- 车门打不开?
- 导航找不到路?
- 半路抛锚?
优点:最接近真实用户体验
缺点:最慢,最容易出问题(UI 改了就挂)
理解:
如果把软件开发比作造汽车:
单元测试是工程师在实验室里测试每一个活塞、齿轮。
集成测试是把发动机装进底盘,看能不能点火、漏不漏油。
E2E 测试 (Playwright) 是试车员开着整车在跑道上跑一圈,看这辆车能不能从起点顺利开到终点,乘客坐得舒不舒服。
java
// E2E 测试:模拟用户操作
test('用户创建零件', async ({ page }) => {
// 1. 打开网页
await page.goto('http://localhost:5173/pdm/parts');
// 2. 点击"新建"按钮
await page.click('button:has-text("新建")');
// 3. 填写表单
await page.fill('input[name="name"]', '测试零件');
// 4. 点击"保存"
await page.click('button:has-text("保存")');
// 5. 验证:成功提示出现了吗?
await expect(page.locator('.success-message')).toBeVisible();
});
前端例子:part-crud.spec.ts(E2E 测试,属于集成测试)
java
test('创建零件并验证编号自动生成', async ({ page }) => {
// 1. 登录(真实调用后端 API)
await loginAsAdmin(page);
// 2. 打开零件列表页(真实渲染 Vue 组件)
await page.goto('http://localhost:5173/pdm/parts');
// 3. 点击新建按钮
await page.locator('button:has-text("新建")').click();
// 4. 填写表单
await page.locator('input[placeholder*="名称"]').fill('E2E测试零件');
// 5. 提交(真实发送 POST /api/parts 请求)
await page.locator('button:has-text("确定")').click();
// 6. 验证成功提示
await expect(page.locator('.el-message--success')).toBeVisible();
// 7. 验证列表中出现新零件
await expect(page.locator('td:has-text("E2E测试零件")')).toBeVisible();
});
特点:
- ✅ 用户视角:模拟真实用户操作(点击、输入、提交)
- ✅ 全链路:测试前端 Vue 组件 + 后端 API + 数据库
- ❌ 最慢:需要启动浏览器 + 前端服务 + 后端服务,10秒级
- ❌ 脆弱:UI 改动(按钮文字变化)会导致测试失败
对比总结

我的项目中的测试


最佳实践

