Mockito是Java单元测试开发框架。在写测试单元时它可以Mock(模拟)开发中一些未完成的接口或者网络断开、数据库连接错误等方法调用。Mockito很强大文中有限还有很多使用方式未提及,请参考官方文档。官方文档链接在文章尾部给出。 eq:when() 多个参数或者搭配List根据不同index设置返回结果。
QuickStart为了更好的表达使用一个Mock DAO层的场景,数据库还没能正常使用时但是又急需测试功能逻辑是否正确。
mapper---| UserMapper.javamapperImpl---| UserMapperImpl.javaservice---| UserService.java
//UserMapper.javapublic 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 环境依赖
org.mockito mockito-core 4.4.0
SpringBoot 环境依赖
org.springframework.boot spring-boot-starter-test
第二步 mock对象与Stubbing
因为数据库无法正常使用的原因,但是service由依赖userMapper的selectOne方法,只能去mock一个假的userMapper。mock一个假的userMapper只需要使用注解@MockBeanSpringBoot会自动代替注入一个假的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;@SpringBootTestclass 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:1More
built-in runner
加上类注解@ExtendWith(MockitoExtension.class)
Mock
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
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
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(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{基本类型}()
@Testvoid 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
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