跳至主要內容

告别 @MockBean!在 Spring Boot 3.2+ 中使用 @MockitoBean 进行单元测试

DD编辑部原创Spring BootSpring Boot大约 3 分钟

告别 @MockBean!在 Spring Boot 3.2+ 中使用 @MockitoBean 进行单元测试

多年来,@MockBean 一直被广泛用于 Spring Boot 单元测试中来模拟依赖项。

然而,在 Spring Boot 3.2 中,@MockBean 由于性能和可维护性方面的问题已被标记为废弃

🔴 @MockBean 存在的问题

创建不必要的 Spring 上下文代理 → 拖慢测试执行速度 ✔ 全局模拟 Bean → 可能在多个测试中产生副作用 ✔ 未针对 Spring Boot 3.2 的测试改进进行优化

✅ 解决方案:使用 @MockitoBean

Spring Boot 3.2 引入了 @MockitoBean,它提供了: ✔ 更快的测试执行速度 - 直接集成 Mockito ✔ 更好的测试组件隔离更可靠的单元测试体验

1️⃣ 什么是 @MockitoBean

@MockitoBeanSpring Boot 3.2 中的一个新注解,它是 @MockBean直接替代品

✔ 它为依赖项创建模拟实例 ✔ 它与 JUnit 5 和 Mockito 有更好的集成 ✔ 它不需要完整的 Spring 上下文重新加载

2️⃣ 将 @MockBean 替换为 @MockitoBean

让我们通过一个在服务测试中模拟存储库的示例来演示。

📝 示例 1:使用 @MockBean 的旧方法(已废弃)

@SpringBootTest
class UserServiceTest {

    @MockBean  // ❌ 在 Spring Boot 3.2+ 中已废弃
    private UserRepository userRepository;

    @Autowired
    private UserService userService;

    @Test
    void testGetUserById() {
        User mockUser = new User(1L, "张三");

        Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));

        User result = userService.getUserById(1L);
        assertEquals("张三", result.getName());
    }
}

这种方法中 @MockBean 的问题:

  • 需要初始化 Spring 上下文,使测试变慢
  • 由于全局模拟会影响其他测试

✅ 示例 2:使用 @MockitoBean 的新方法(Spring Boot 3.2+)

@SpringBootTest
class UserServiceTest {

    @MockitoBean  // ✅ 新的推荐注解
    private UserRepository userRepository;

    @Autowired
    private UserService userService;

    @Test
    void testGetUserById() {
        User mockUser = new User(1L, "张三");

        Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));

        User result = userService.getUserById(1L);
        assertEquals("张三", result.getName());
    }
}

使用 @MockitoBean 的优势:
✔ 执行更快 – 不会重新加载 Spring 上下文
作用域模拟 – 仅在测试类中进行模拟
更稳定的单元测试体验

3️⃣ 使用 @MockitoBeanMockMvc 进行 REST 控制器单元测试

Spring Boot 还允许测试 REST 控制器而无需启动完整的应用程序。

✅ 示例:测试 UserController

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean  // 仅在此测试类中模拟
    private UserService userService;

    @Test
    void testGetUserById() throws Exception {
        User mockUser = new User(1L, "张三");

        Mockito.when(userService.getUserById(1L)).thenReturn(mockUser);

        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("张三"));
    }
}

使用 MockMvc 高效测试 API 接口模拟 UserService 而无需加载完整应用程序

4️⃣ 核心差异:@MockBean vs. @MockitoBean

@MockBean vs. @MockitoBean 对比

🚀 在 Spring Boot 3.2+ 的单元测试中请始终使用 @MockitoBean

5️⃣ Spring Boot 3.2+ 单元测试最佳实践

使用 @MockitoBean 而不是 @MockBean 以获得更快的测试速度使用 @WebMvcTest 进行控制器测试仅在集成测试中使用 @SpringBootTest模拟依赖项而不是使用真实的数据库连接使用 Mockito.when()verify() 来验证方法调用

🎯 总结:为什么要切换到 @MockitoBean

通过切换到 @MockitoBean,你将获得:
更快的测试执行(无需重新加载 Spring 上下文)更好的测试隔离(按测试的模拟作用域)更高效的 Mockito 集成

🚀 你在 Spring Boot 3.2+ 项目中使用 @MockitoBean 了吗?欢迎在评论区分享你的经验!

🔗 将这个指南分享给开发者朋友们,帮助他们从 @MockBean 迁移到 @MockitoBean 🚀