SpringBoot JUnit 教程

1. 核心依赖 (Maven)

XML 复制代码
<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:

java 复制代码
@Service
public class UserService {
    public String getUserName(Long id) {
        return "user-" + id;
    }
}

测试类:

java 复制代码
@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:

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

测试类:

java 复制代码
@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,避免依赖真实数据库。

java 复制代码
@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,默认回滚事务。

java 复制代码
@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)

java 复制代码
import static org.assertj.core.api.Assertions.*;

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

8. 测试生命周期

java 复制代码
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class LifecycleTest {
    @BeforeAll
    void initAll() { /* 无需 static */ }
}

9. 常用 Mockito 操作

java 复制代码
// 验证调用次数
verify(userService, times(1)).getUserName(anyLong());

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

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

10. 测试覆盖率 (Jacoco)

java 复制代码
<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 + CountDownLatchCompletableFuture 配合 await()

相关推荐
IvorySQL5 小时前
PostgreSQL 日报 | JOIN 与外键优化(8 月 9 日)
数据库·postgresql
ltl5 小时前
自治数据库十年回顾:Peloton、NoisePage、OtterTune 到云原生 auto-tuning
数据库
萧瑟余晖6 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
灯澜忆梦7 小时前
【MySQL10】进阶篇 | 索引_#2性能优化
数据库·sql·mysql·性能优化
C++ 老炮儿的技术栈9 小时前
从 Qt Designer 属性编辑器的层级可以看到继承链
c语言·数据库·c++·qt·sqlite·visual studio
MC皮蛋侠客9 小时前
Redis 系列(一):全景与最小闭环——从 `SET` 命令到内存数据结构
数据结构·数据库·redis
zt1985q10 小时前
本地部署开源网络书签与内容管理工具 Karakeep 并实现外部访问
运维·服务器·网络·数据库·网络协议·开源
IvorySQL11 小时前
PostgreSQL 日报| PostgreSQL 19 默认 WAL 压缩算法(8 月 8 日)
数据库·postgresql·区块链
油丶酸萝卜别吃11 小时前
MySQL B+ 树查询全过程详解
数据库·mysql
leisoo809711 小时前
股票数据本地化存储实战:JSON、数据库与列式存储的方案对比
jvm·数据库·json