1. 概述
本文将深入探讨在 Spring Boot 测试中使用 @Autowired
和 Mockito 的 @InjectMocks
注解进行依赖注入的最佳实践。我们将分析需要使用这些注解的场景,并通过实际示例演示具体用法。
2. 理解测试注解
在开始代码示例前,先快速梳理几个核心测试注解的基础知识:
✅ Mockito 的 @Mock
创建依赖项的模拟实例,常与 @InjectMocks
配合使用,后者会将标记为 @Mock
的模拟对象注入到被测试的目标对象中。
✅ Spring Boot 的 @MockBean
创建模拟的 Spring Bean,该模拟 Bean 可被上下文中的其他 Bean 使用。当 Spring 上下文自动创建的 Bean 无需模拟时,可直接使用 @Autowired
注入。
3. 示例搭建
我们将创建一个包含两个依赖项的服务,并演示如何使用上述注解进行测试。
3.1. 依赖配置
添加必要的 Maven 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
</dependency>
3.2. DTO 定义
创建服务层使用的 DTO:
public class Book {
private String id;
private String name;
private String author;
// 构造方法、getter/setter
}
3.3. 服务实现
首先定义数据库交互服务:
@Service
public class DatabaseService {
public Book findById(String id) {
// 数据库查询逻辑(示例简化)
return new Book("id","Name", "Author");
}
}
⚠️ 注意:数据库交互细节与示例无关,使用 @Service
声明为 Spring Bean。
接着创建依赖上述服务的业务服务:
@Service
public class BookService {
private final DatabaseService databaseService;
private final ObjectMapper objectMapper;
BookService(DatabaseService databaseService, ObjectMapper objectMapper) {
this.databaseService = databaseService;
this.objectMapper = objectMapper;
}
String getBook(String id) throws JsonProcessingException {
Book book = databaseService.findById(id);
return objectMapper.writeValueAsString(book);
}
}
该服务通过 DatabaseService
获取书籍数据,并使用 Jackson 的 ObjectMapper
将对象转为 JSON 字符串。核心依赖:DatabaseService
和 ObjectMapper
。
4. 测试实现
现在演示测试 BookService
的多种注解组合方案。
4.1. 使用 @Mock 和 @InjectMocks
通过 @Mock
模拟所有依赖,并用 @InjectMocks
注入:
@ExtendWith(MockitoExtension.class)
class BookServiceMockAndInjectMocksUnitTest {
@Mock
private DatabaseService databaseService;
@Mock
private ObjectMapper objectMapper;
@InjectMocks
private BookService bookService;
@Test
void givenBookService_whenGettingBook_thenBookIsCorrect() throws JsonProcessingException {
Book book1 = new Book("1234", "Inferno", "Dan Brown");
when(databaseService.findById(eq("1234"))).thenReturn(book1);
when(objectMapper.writeValueAsString(any())).thenReturn(new ObjectMapper().writeValueAsString(book1));
String bookString1 = bookService.getBook("1234");
Assertions.assertTrue(bookString1.contains("Dan Brown"));
}
}
关键点:
@ExtendWith(MockitoExtension.class)
启用 Mockito 扩展@Mock
创建模拟对象@InjectMocks
将模拟对象注入目标服务
❌ 踩坑提醒:必须模拟所有依赖!未模拟 ObjectMapper
会导致测试时抛出 NullPointerException
。
4.2. 使用 @Autowired 和 @MockBean
当只需模拟部分依赖时(如仅模拟 DatabaseService
):
@SpringBootTest
class BookServiceAutowiredAndInjectMocksUnitTest {
@MockBean
private DatabaseService databaseService;
@Autowired
private BookService bookService;
@Test
void givenBookService_whenGettingBook_thenBookIsCorrect() throws JsonProcessingException {
Book book1 = new Book("1234", "Inferno", "Dan Brown");
when(databaseService.findById(eq("1234"))).thenReturn(book1);
String bookString1 = bookService.getBook("1234");
Assertions.assertTrue(bookString1.contains("Dan Brown"));
}
}
核心机制:
@SpringBootTest
加载 Spring 上下文@MockBean
替换上下文中的真实 Bean@Autowired
注入真实BookService
(其ObjectMapper
依赖保持真实)
✅ 优势:无需模拟所有依赖,适合测试嵌套 Bean 的真实行为。
4.3. 混合使用 @Autowired 和 @InjectMocks
动态控制依赖模拟的进阶方案:
@SpringBootTest
class BookServiceAutowiredAndInjectMocksUnitTest {
@Mock
private DatabaseService databaseService;
@Autowired
@InjectMocks
private BookService bookService;
@Test
void givenBookService_whenGettingBook_thenBookIsCorrect() throws JsonProcessingException {
Book book1 = new Book("1234", "Inferno", "Dan Brown");
MockitoAnnotations.openMocks(this); // 关键步骤!
when(databaseService.findById(eq("1234"))).thenReturn(book1);
String bookString1 = bookService.getBook("1234");
Assertions.assertTrue(bookString1.contains("Dan Brown"));
}
}
执行流程:
@Autowired
注入真实BookService
@Mock
创建模拟对象MockitoAnnotations.openMocks(this)
手动触发模拟对象注入
⚠️ 注意:必须显式调用 openMocks()
,否则模拟对象不会生效!
5. 方案对比
方案组合 | 描述 | 适用场景 |
---|---|---|
@Mock + @InjectMocks |
纯 Mockito 模拟所有依赖 | 单元测试(隔离被测类) |
@MockBean + @Autowired |
Spring Boot 模拟部分 Bean | 集成测试(混合真实/模拟依赖) |
@Autowired + @InjectMocks |
手动控制模拟注入时机 | 复杂场景测试(动态切换依赖) |
6. 总结
本文系统分析了 Spring Boot 测试中 @Autowired
、@Mock
、@InjectMocks
和 @MockBean
的协作机制。根据测试需求选择合适组合:
- ✅ 单元测试:优先用
@Mock
+@InjectMocks
- ✅ 集成测试:优先用
@MockBean
+@Autowired
- ✅ 复杂场景:考虑混合方案动态控制模拟
完整示例代码请参考 GitHub 仓库