个人博客传送门
What’s Mockito ?
Mockito是Java单元测试开发框架。在写测试单元时它可以Mock(模拟)开发中一些未完成的接口或者网络断开、数据库连接错误等方法调用。Mockito很强大文中有限还有很多使用方式未提及,请参考官方文档。官方文档链接在文章尾部给出。
eq:when() 多个参数或者搭配List根据不同index设置返回结果。
QuickStart
为了更好的表达使用一个Mock DAO层的场景,数据库还没能正常使用时但是又急需测试功能逻辑是否正确。
mapper---|
UserMapper.java
mapperImpl---|
UserMapperImpl.java
service---|
UserService.java
//UserMapper.java
public interface UserMapper
{
User selectOne(Integer id);
void print();
}
public class UserMapperImpl implements UserMapper{
@Override
public User selectOne(Integer id) {
return null;
}
@Override
public void print() {
System.out.println("test");
}
}
//UserService.java UserService通过@Configuration配置由Spirng IOC管理
public class UserService
{
@Autowired
UserMapper userMapper;
public User getOne(Integer id)
{
return userMapper.selectOne(id);
}
}
第一步 添加依赖
Java 环境依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.4.0</version>
</dependency>
SpringBoot 环境依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
第二步 mock对象与Stubbing
因为数据库无法正常使用的原因,但是service由依赖userMapper的selectOne方法,只能去mock一个假的userMapper。mock一个假的userMapper只需要使用注解@MockBean
SpringBoot会自动代替注入一个假的userMapper给service。具体原理是userMapper生成一个代理对象。
when()
静态方法用于Stubbing(方法打桩),参数为非private 、final的方法调用。thenReturn
设置被Stubbing的方法返回值。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.mockito.Mockito.when;
@SpringBootTest
class MockitoDemoTests {
@MockBean
UserMapper userMapper;
@Autowired
UserService userService;
@Test
void mockUserMapper() {
when(userMapper.selectOne(0)).thenReturn(new User()); //这里mock的是userMapper的方法,service间接调用。
User user = userService.getOne(0);
assertThat(user,notNullValue()); //断言不为空
}
}
//Tests passed:1
More
built-in runner
使用Mockito的注解需要在JUnit5构建mock环境
加上类注解@ExtendWith(MockitoExtension.class)
Mock
使用mock创建一个代理对象。代理对象的方法调用返回都是java基本类型默认返回值。
1、使用mock方法
UserMapper userMapper= Mockito.mock(UserMapper.class); //不由Spring管理
2、使用@Mock注解 ,需要基于JUnit5环境。
@Mock
UserMapper userMapper; //不由Spring管理
配合@InjectMocks
注解可以注入到userService。
@Mock
UserMapper userMapper;
@InjectMocks
UserService userService;
3、使用Springboot的@MockBean注解,Spring会自动注入到测试环境里所需要依赖的对象。
@MockBean
UserMapper userMapper;
Spy
有时候需要在真实对象上mock方法,使用spy可以创建或者放入一个真实对象进行Stubbing,用spy创建的对象都是真实对象。
1、使用spy方法
UserMapper userMapper= Mockito.Spy(Object);
2、使用@spy注解,需要基于JUnit5环境。
@Spy
UserMapper userMapper=new UserMapperImpl();
配合@InjectMocks
注解可以注入到userService。
@Spy
UserMapper userMapper;
@InjectMocks
UserService userService;
3、使用Springboot的@SpyBean注解
@SpyBean
UserMapper userMapper;
When
when可以对调用方法进行代理Stubbing方法返回值、方法异常
mock selectOne(0) 方法返回值,thenReturn参数为调用方法期望返回值。
when(userMapper.selectOne(0)).thenReturn(new User());
mock selectOne(0) 方法异常,当调用selectOne(0) 时会抛出异常。
when(userMapper.selectOne(0)).thenThrow(new TimeoutException());
当一个void方法不需要执行时可以使用 doNothing().when()
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MockitoDemoTests {
@Spy
UserMapper userMapper=new UserMapperImpl();
@Test
void mockUserMapper() {
doNothing().when(userMapper).print();
userMapper.print();
}
}
//没有任何输出
Argument matchers
当使用when去Stubbing方法时可以根据被调用方法传参做不同的返回值或异常。
when(userMapper.selectOne(0)).thenReturn(new User());
when(userMapper.selectOne(1)).thenReturn(new User());
User userId0 = userService.getOne(0);
User userId1 = userService.getOne(1);
assertThat(userId0,not(equalTo(userId1))); //断言是否是一个对象
//passed:1
//可以看到getOne(0)和getOne(1)不一致。
官方内置了很多类型的静态方法包含了很多场景。比如anyInt()
表示传参任何int类型数字都可以。
any() //任何对象
any{基本类型}()
@Test
void mockUserMapper() {
User user=new User();
when(userMapper.selectOne(anyInt())).thenReturn(user);
User userId0 = userService.getOne(0);
assertThat(userId0,equalTo(user));//断言是否是一个对象
}
//passed:1
//可以看到user和userId0是一致。
需要注意的是 when(userMapper.selectOne(anyInt()))
会被when(userMapper.selectOne(0))
条件覆盖。
如果when里使用了官方内置类型any()
方法其参数也应该使用。如果有指定参数可以使用eq()
when(xxx.xxx(any(),"text"))
❌
when(xxx.xxx(any(),eq("text"))
✔
Verification
验证被mock方法的动作
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MockitoDemoTests {
@Spy
UserMapper userMapper=new UserMapperImpl();
@Test
void mockUserMapper() {
doNothing().when(userMapper).print();
userMapper.print();
verify(userMapper,times(1)).print(); //验证print()至少调用一次。
}
}
//passed:1
参考文档:
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.mocking-beans