Skip to content
第 63 章 后端 ⏱ 10 分钟阅读

第 63 章:测试与排错 ​

学习目标 ​

  • 写 JUnit 5 单元测试
  • 用 Mockito 打桩
  • 用 Testcontainers 做集成测试
  • 用 Arthas 线上排错

一、JUnit 5 单元测试 ​

java
@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    void testCreate() {
        User user = new User();
        user.setUsername("Tom");
        user.setAge(18);

        userService.create(user);

        assertNotNull(user.getId());
        assertEquals("Tom", user.getUsername());
    }

    @Test
    void testGetById() {
        User user = userService.create(new User("Tom", 18));
        User found = userService.getById(user.getId());
        assertEquals(18, found.getAge());
    }
}

⚠️ 坑 1:@SpringBootTest 启动慢(几秒到几十秒),单测不要用。要测 Service 直接 mock Mapper。

二、Mockito 单独测 ​

java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserMapper userMapper;        // mock 依赖

    @InjectMocks
    private UserService userService;      // 被测对象

    @Test
    void testGetById() {
        User mockUser = new User(1L, "Tom", 18);
        when(userMapper.selectById(1L)).thenReturn(Optional.of(mockUser));

        User result = userService.getById(1L);

        assertEquals("Tom", result.getUsername());
        verify(userMapper, times(1)).selectById(1L);    // 验证调用
    }

    @Test
    void testGetByIdNotFound() {
        when(userMapper.selectById(99L)).thenReturn(Optional.empty());

        assertThrows(BusinessException.class,
            () -> userService.getById(99L));
    }
}

⚠️ 坑 2:@Mock / @InjectMocks 用 @ExtendWith(MockitoExtension.class),不要混 Spring。

三、断言 ​

java
assertEquals(1, 1);                              // 等于
assertNotNull(user);                              // 非 null
assertTrue(list.isEmpty());                       // 真
assertThrows(Exception.class, () -> foo());        // 抛异常
assertTimeout(Duration.ofSeconds(1), () -> foo()); // 不超时

// 批量
assertAll("user",
    () -> assertEquals("Tom", user.getName()),
    () -> assertEquals(18, user.getAge())
);

四、Controller 测试(MockMvc) ​

java
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserService userService;

    @Test
    void testGetById() throws Exception {
        when(userService.getById(1L)).thenReturn(new User(1L, "Tom", 18));

        mvc.perform(get("/api/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data.username").value("Tom"));
    }
}

五、Testcontainers(集成测试) ​

真实数据库:

xml
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mysql</artifactId>
    <version>1.19.7</version>
    <scope>test</scope>
</dependency>
java
@SpringBootTest
@Testcontainers
class UserIntegrationTest {

    @Container
    static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
        .withDatabaseName("test")
        .withUsername("root")
        .withPassword("test");

    @DynamicPropertySource
    static void datasource(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", mysql::getJdbcUrl);
        registry.add("spring.datasource.username", mysql::getUsername);
        registry.add("spring.datasource.password", mysql::getPassword);
    }

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setUsername("Tom");
        userMapper.insert(user);
        assertNotNull(user.getId());
    }
}

优点:用真实 DB,测出 SQL 语法错、约束错。 缺点:每次启动慢(几秒到十几秒)。

六、覆盖率 ​

xml
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
</plugin>
bash
./mvnw test
# 报告:target/site/jacoco/index.html

目标:

  • 单元测试覆盖率 > 80%
  • 核心业务 > 95%

七、Arthas(线上排错) ​

bash
# 下载
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 选进程
# 然后:
dashboard        # 概览
thread           # 线程
watch demo.MathGame primeFactors "{params, returnObj, throwExp}" -x 2    # 监控方法
trace demo.MathGame primeFactors '#cost > 5'                               # 慢方法
jad com.example.UserService                                                # 反编译

⚠️ 生产慎用:trace / watch 影响性能,采样用。

八、本章小结 ​

要点关键
单元测试@ExtendWith(MockitoExtension.class)
Controller@WebMvcTest + MockMvc
集成Testcontainers
覆盖率Jacoco > 80%
排错Arthas

动手练习 ​

  1. 给 UserService 写 5 个单元测试
  2. 用 Testcontainers 跑一次集成测试

下一章:第 64 章:Spring Boot 进阶与资源 →

本站基于 VitePress 构建 · 由 StackHub 团队维护