【前后端开发知识 - 边开发边学习】什么的单元测试、集成测试和E2E测试?

单元测试 vs. 集成测试 vs. 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 改动(按钮文字变化)会导致测试失败

对比总结

我的项目中的测试


最佳实践


相关推荐
red_redemption2 小时前
自由学习记录(136)
学习
EnglishJun2 小时前
ARM嵌入式学习(二) --- 入门51(中断)
arm开发·学习
for_ever_love__2 小时前
Objective-C学习 NSArray 和 NSMutableArray 功能详解
学习·ios·objective-c
Pyeako2 小时前
自然语言学习--bert框架
人工智能·深度学习·学习·bert·lstm·自然语言学习
载数而行52010 小时前
QT的五类布局
c++·qt·学习
载数而行52011 小时前
QT的QString类
c++·qt·学习
zl_dfq11 小时前
Python学习2 之 【数据类型、运算及相关函数、math库】
学习
2301_7811435613 小时前
C语言学习笔记
笔记·学习
Alphapeople15 小时前
Isaac Sim学习
学习