What’s Mockito ?
Mockito是Java单元测试开发框架。在写测试单元时它可以Mock(模拟)开发中一些未完成的接口或者网络断开、数据库连接错误等方法调用。Mockito很强大文中有限还有很多使用方式未提及,请参考官方文档。官方文档链接在文章尾部给出。
eq:when() 多个参数或者搭配List根据不同index设置返回结果。
QuickStart
为了更好的表达使用一个Mock DAO层的场景,数据库还没能正常使用时但是又急需测试功能逻辑是否正确。
1 2 3 4 5 6
| mapper---| UserMapper.java mapperImpl---| UserMapperImpl.java service---| UserService.java
|
1 2 3 4 5 6 7 8
| public interface UserMapper { User selectOne(Integer id); void print(); }
|
1 2 3 4 5 6 7 8 9 10 11
| public class UserMapperImpl implements UserMapper{ @Override public User selectOne(Integer id) { return null; }
@Override public void print() { System.out.println("test"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class UserService { @Autowired UserMapper userMapper; public User getOne(Integer id) { return userMapper.selectOne(id); } }
|
第一步 添加依赖
Java 环境依赖
1 2 3 4 5
| <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.4.0</version> </dependency>
|
SpringBoot 环境依赖
1 2 3 4
| <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的方法返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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()); User user = userService.getOne(0); assertThat(user,notNullValue()); }
}
|
More
built-in runner
使用Mockito的注解需要在JUnit5构建mock环境
加上类注解@ExtendWith(MockitoExtension.class)
Mock
使用mock创建一个代理对象。代理对象的方法调用返回都是java基本类型默认返回值。
1、使用mock方法
1
| UserMapper userMapper= Mockito.mock(UserMapper.class);
|
2、使用@Mock注解 ,需要基于JUnit5环境。
1 2
| @Mock UserMapper userMapper;
|
配合@InjectMocks
注解可以注入到userService。
1 2 3 4 5
| @Mock UserMapper userMapper;
@InjectMocks UserService userService;
|
3、使用Springboot的@MockBean注解,Spring会自动注入到测试环境里所需要依赖的对象。
1 2
| @MockBean UserMapper userMapper;
|
Spy
有时候需要在真实对象上mock方法,使用spy可以创建或者放入一个真实对象进行Stubbing,用spy创建的对象都是真实对象。
1、使用spy方法
1
| UserMapper userMapper= Mockito.Spy(Object);
|
2、使用@spy注解,需要基于JUnit5环境。
1 2
| @Spy UserMapper userMapper=new UserMapperImpl();
|
配合@InjectMocks
注解可以注入到userService。
1 2 3 4 5
| @Spy UserMapper userMapper;
@InjectMocks UserService userService;
|
3、使用Springboot的@SpyBean注解
1 2
| @SpyBean UserMapper userMapper;
|
When
when可以对调用方法进行代理Stubbing方法返回值、方法异常
mock selectOne(0) 方法返回值,thenReturn参数为调用方法期望返回值。
1
| when(userMapper.selectOne(0)).thenReturn(new User());
|
mock selectOne(0) 方法异常,当调用selectOne(0) 时会抛出异常。
1
| when(userMapper.selectOne(0)).thenThrow(new TimeoutException());
|
当一个void方法不需要执行时可以使用 doNothing().when()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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方法时可以根据被调用方法传参做不同的返回值或异常。
1 2 3 4 5 6 7
| 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)));
|
官方内置了很多类型的静态方法包含了很多场景。比如anyInt()
表示传参任何int类型数字都可以。
1 2 3 4 5 6 7 8 9
| @Test void mockUserMapper() { User user=new User(); when(userMapper.selectOne(anyInt())).thenReturn(user); User userId0 = userService.getOne(0); assertThat(userId0,equalTo(user)); }
|
需要注意的是 when(userMapper.selectOne(anyInt()))
会被when(userMapper.selectOne(0))
条件覆盖。
如果when里使用了官方内置类型any()
方法其参数也应该使用。如果有指定参数可以使用eq()
when(xxx.xxx(any(),"text"))
❌
when(xxx.xxx(any(),eq("text"))
✔
Verification
验证被mock方法的动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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(); } }
|
参考文档:
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