Mock概念
Mock叫做模拟对象,即用来模拟未被实现的对象可以预先定义这个对象在特定调用时的行为(例如返回值或抛出异常),从而模拟不同的系统状态。
导入Mock依赖
pom文件中引入springboot测试依赖,spring-boot-starter-test中包含了Mockito
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Mock测试环境和Spring上下文环境
仅使用Mock环境
使用Mock进行测试时候,可以仅仅使用Mock环境,不添加@SpringBootTest,这个时候不会加载Spring上下文(@Autowired等不会起作用),需要手动处理使用@Mock和@InjectMock来处理类之间的依赖关系。
常用注解:
@Mock:
@Mock
是 Mockito 提供的注解,用于生成模拟对象,是创建了一个新的对象。这里的 userDao
和 webServiceClient
是通过 Mockito 模拟的对象,而不是 Spring 容器中的实际 bean。它们的行为可以通过 when/thenReturn
或其他模拟方法来定义。
注意:当你使用 Mockito 的 @Mock
注解来 mock 一个类时,即使该类已经实现了部分方法,Mockito 也会拦截这些方法的调用。这意味着,默认情况下,Mockito 会模拟这个类的所有方法(包括已经实现的方法),除非你显式定义模拟行为。
因此,当你通过 @Mock
来 mock 一个已经实现部分方法的类时:
- 如果你调用了已经实现的方法,并且没有为这个方法定义具体的
when/thenReturn
行为,Mockito 会返回 默认值(例如null
、0、false
等),而不会执行类中的实际实现。 - 如果你想让某些方法在调用时执行它们的实际实现,你需要使用
Mockito
提供的spy()
功能。
@Spy
@Spy
创建的对象是真实对象的部分模拟(Partial Mock),它会调用对象的真实方法,而只有那些明确模拟的方法才会被替换成模拟的行为。spy()
提供部分模拟功能。未被显式模拟的方法将调用实际实现,已经被模拟的方法则返回预设的模拟值。
在使用 @InjectMocks
时,Mockito 会将 @Mock
和 @Spy
注解的对象注入到被测试的对象中。如果某个依赖项使用了 @Spy
,Mockito 会确保被注入的是该对象的部分模拟实现。
spy()
与 mock()
的对比
特性
mock()
spy()
默认行为
模拟所有方法,返回默认值(如 null
)
调用真实的实现,除非被显式模拟
是否执行实际代码
不执行
执行实际的代码实现
定义模拟行为时是否拦截
会拦截并返回模拟值
如果定义了模拟行为,使用模拟值,没定义则执行实际实现
@InjectMocks:
@InjectMocks
是 Mockito 的一个注解,用于将模拟对象(即用@Mock
创建的对象)注入到被测对象中(这里是UserService
)。- Mockito 会创建一个新的
UserService
对象,并将userDao
和webServiceClient
作为依赖注入到这个新的对象中。 - 这与 Spring 容器的行为无关。即使
UserService
已经通过@Service
注解注册到了 Spring 容器中,在使用@InjectMocks
时,Mockito 会创建并管理一个全新的UserService
对象。
具体演示:
//UserDao定义
public class UserDao {
//getUserById有真实的实现
User getUserById(int userId)
{
return new User(1,"张三");
}
int saveUser(User user);
}
//WebServiceClient定义
public class WebServiceClient {
boolean isServiceAvailable();
String getUserDataFromWebService(int userId);
}
@Service
//UserService依赖于UserDao以及WebServiceClient
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private WebServiceClient webServiceClient;
//省略操作
}
//可以没有@SpringBootTest
public class UserServiceTest {
// 使用@Spy部分模拟UserDao对象
@Spy
private UserDao userDao;
// 模拟WebServiceClient对象
@Mock
private WebServiceClient webServiceClient;
// 根据依赖将mock对象注入到UserService中
@InjectMocks
private UserService userService;
//必须首先初始化
@BeforeEach
public void setUp() {
//非常重要!!!!!
MockitoAnnotations.openMocks(this); // 初始化Mockito
}
@Test
void Spytest()
{
//模拟saveUser方法,而调用getUserById为其真实实现
doReturn(1).when(userDao).saveUser(any(User.class));
//真实行为
User FoundUser = userDao.getUserById(1);// User张三
//模拟行为
userDao.saveUser(FoundUser);// 返回1
}
//省略其他测试方法
}
MockitoAnnotations.openMocks(this)作用:
MockitoAnnotations.openMocks(this)
是用于初始化 @Mock
、@Spy
、@InjectMocks
注解的关键步骤。如果没有这行代码,Mockito 将不会创建和初始化这些模拟对象,导致测试失败。
MockitoAnnotations.openMocks(this)
适用于非 Spring 环境下的单元测试。在 Spring Boot 测试中,你通常使用 @MockBean
或 @Autowired
,Spring Boot 会自动处理模拟对象的初始化,因此不需要调用这个方法。
搭配Spring上下文
使用Spring上下文需要使用@MockBean来在测试中将 Spring 容器中的某些 bean 替换为 Mockito 模拟的对象,然后可以使用@Autowired处理类之间的依赖关系。
结合 Spring Boot 和 Mockito 的测试方法
- 使用
@MockBean
:用来替换 Spring 容器中的 bean,模拟它的行为。 - 使用
@Autowired
:注入 Spring 容器中实际的服务(如UserService
)。 - 使用
@SpringBootTest
:启动 Spring Boot 的测试上下文。
常用注解:
@MockBean:
@MockBean
是 Spring Boot 提供的注解,用于创建一个 Mockito 模拟对象,并将它替换到 Spring 上下文中。userDao
和 webServiceClient
是通过 @MockBean
模拟的对象,而不是真实的对象。这些模拟对象将替换 Spring 容器中的相应 bean,然后可以通过@Autowird自动注入被依赖类中。
@SpyBean:
@SpyBean
是 Spring Boot 提供的一个注解,专门用于 部分模拟(Partial Mocking) Spring 容器中的 Bean。它的作用是创建一个部分模拟的对象,部分调用真实方法,部分进行模拟(Mock)行为。相比于 Mockito 提供的 @Spy
,@SpyBean
更加集成到 Spring 环境中,并且允许你将某个 Spring 容器中的 Bean 替换为部分模拟对象。
@SpyBean
的工作原理
- 部分模拟:
@SpyBean
允许你对 Spring 容器中的现有 Bean 进行部分模拟。这意味着模拟的 Bean 会保留其大部分原始行为,只有你明确模拟的部分会改变。 - 注入到 Spring 容器中:使用
@SpyBean
时,Spring Boot 会将该部分模拟的 Bean 注入到 Spring 容器中,替换原有的 Bean。
具体演示:
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private WebServiceClient webServiceClient;
//省略操作
}
@SpringBootTest //使用Spring上下文
public class UserServiceTest {