1. 核心依赖 (Maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

该 Starter 自动引入:

  • JUnit 5 (Jupiter)

  • Mockito

  • AssertJ

  • Spring Test 等


2. 基础注解

注解 作用
@SpringBootTest 启动完整 Spring 上下文(集成测试)
@Test 标记测试方法
@BeforeEach / @AfterEach 每个测试前/后执行
@BeforeAll / @AfterAll 所有测试前/后执行(需静态方法)
@DisplayName 给测试起中文名
@Disabled 禁用测试

3. 单元测试(Service 层)

被测 Service:

@Service
public class UserService {
    public String getUserName(Long id) {
        return "user-" + id;
    }
}

测试类:

@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    @DisplayName("根据ID获取用户名")
    void testGetUserName() {
        String name = userService.getUserName(1L);
        assertEquals("user-1", name);
        assertNotNull(name);
    }
}

4. Web 层测试(Controller)

使用 @WebMvcTest 只加载 Web 层,比 @SpringBootTest 轻量。

被测 Controller:

@RestController
@RequestMapping("/api/users")
public class UserController {
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id) {
        return "user-" + id;
    }
}

测试类:

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testGetUser() throws Exception {
        mockMvc.perform(get("/api/users/1"))
               .andExpect(status().isOk())
               .andExpect(content().string("user-1"));
    }
}

5. Mock 依赖(隔离测试)

使用 @MockBean 模拟 Service,避免依赖真实数据库。

@SpringBootTest
class UserControllerMockTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void testGetUser() throws Exception {
        when(userService.getUserName(1L)).thenReturn("mock-user");

        mockMvc.perform(get("/api/users/1"))
               .andExpect(status().isOk())
               .andExpect(content().string("mock-user"));
    }
}

6. 数据层测试(Repository)

使用 @DataJpaTest 测试 JPA,默认回滚事务。

@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveAndFind() {
        User user = new User("test");
        userRepository.save(user);

        Optional<User> found = userRepository.findByName("test");
        assertTrue(found.isPresent());
    }
}

7. 断言进阶(AssertJ)

import static org.assertj.core.api.Assertions.*;

assertThat(user.getName()).startsWith("user");
assertThat(list).hasSize(3).contains("a", "b");

8. 测试生命周期

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class LifecycleTest {
    @BeforeAll
    void initAll() { /* 无需 static */ }
}

9. 常用 Mockito 操作

// 验证调用次数
verify(userService, times(1)).getUserName(anyLong());

// 抛出异常
when(userService.getUserName(1L)).thenThrow(new RuntimeException("error"));

// 参数匹配
when(userService.getUserName(eq(1L))).thenReturn("user-1");

10. 测试覆盖率 (Jacoco)

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
</plugin>

执行 mvn clean test 后生成 target/site/jacoco/index.html


11. 最佳实践总结

类型 推荐注解 说明
单元测试(Service) @SpringBootTest 可配合 @MockBean
Web 层测试 @WebMvcTest 只加载 Controller
Repository 测试 @DataJpaTest 轻量,自动回滚
集成测试 @SpringBootTest 加载完整上下文
测试命名 should_xxx_when_xxx 清晰表达意图
断言 AssertJ 更丰富流畅

12. 面试常见问题

  1. @SpringBootTest 和 @WebMvcTest 区别?

    • @SpringBootTest:加载全部 Bean,适合集成测试。

    • @WebMvcTest:只加载 Web 层,更快。

  2. @MockBean 和 @Mock 区别?

    • @MockBean 是 Spring 提供的,会将 Mock 对象注入 Spring 容器。

    • @Mock 是 Mockito 的,不管理容器。

  3. 如何测试异步方法?
    使用 @Async + CountDownLatch 或 CompletableFuture 配合 await()

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐