赞
踩
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
Mockito是比较出名且方便的Mock工具,它的API都比较直观,易于理解。
如果普通Java项目,不依赖SpringBoot,则直接引入
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
如果是SpringBoot项目
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
先创建几个非常简单的文件,这个结构大伙一看就懂。
DictTypeServiceImpl 的具体实现如下:
可以看到DictTypeServiceImpl中是依赖DictTypeDao的。
@Service public class DictTypeServiceImpl extends CommonServiceImpl<DictType> implements DictTypeService { //这里依赖了DictTypeDao @Autowired private DictTypeDao dictTypeDao; public DictType save(DictType dictType) { return dictTypeDao.save(dictType); } public void deleteById(String id){ dictTypeDao.deleteById(id); } public Optional<DictType> getById(String id) { return dictTypeDao.findById(id); } public Iterable<DictType> listByQuery(QueryBuilder queryBuilder) { return search(queryBuilder, DictType.class); } public boolean existById(String id) { return dictTypeDao.existsById(id); } }
举例说明:
此时我就想写UT测试Service层的代码,不测试Dao层的代码,那就需要虚拟一个Dao层的对象(Mock对象)给Service用。
比如模拟Dao层中getById(“1”)的返回值,指定返回一个我们new的对象,而不是让Dao去查询数据库。
代码如下:
//我们自己定义一个返回值
DictType dictType = new DictType();
dictType.setId("1");
dictType.setCode("code1");
dictType.setName("name1");
Optional<DictType> dictTypeOptional = Optional.of(dictType);
//Stubbing,指定当执行到findById("1")时,返回上面定义的值,而不是执行真正的代码去查数据库
when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional);
写这个UT时,可以创建3种对象:被测试对象、Mock对象、Spy对象。
@InjectMocks的作用和@Autowired比较类似,但是它的成员变量将被@Mock和@Spy注解的字段注入。
DictTypeServiceImpl 的定义如下:
@Service
public class DictTypeServiceImpl extends CommonServiceImpl<DictType> implements DictTypeService {
//这里依赖了DictTypeDao
@Autowired
private DictTypeDao dictTypeDao;
//省略其他方法
}
测试类中用@InjectMocks修饰在DictTypeServiceImpl上,那么@Mock或者@Spy修饰的对象会注入到@InjectMocks修饰的对象里。
注意@InjectMocks修饰在实现类上,而不是DictTypeService接口层,这个和@Autowired有不同。
被测试的DictTypeServiceImpl中代码
public Optional<DictType> getById(String id) {
return dictTypeDao.findById(id);
}
用@Mock修饰在对象上,就可以实现Mock对象。
@SpringBootTest public class MockitoWebTest { @InjectMocks DictTypeServiceImpl dictTypeService; @Mock private DictTypeDao dictTypeDao; @Test public void testMockObject() { //我们自己定义一个返回值 DictType dictType = new DictType(); dictType.setId("1"); dictType.setCode("mock_code1"); dictType.setName("mock_name1"); Optional<DictType> dictTypeOptional = Optional.of(dictType); //Stubbing,指定当执行到findById("1")时,返回上面定义的值 when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional); Optional<DictType> dictTypeById_1 = dictTypeService.getById("1"); Optional<DictType> dictTypeById_2 = dictTypeService.getById("2"); System.out.println(dictTypeById_1.get().getCode()); if (dictTypeById_2.isEmpty()){ System.out.println("dictTypeById_2为空"); } else { System.out.println(dictTypeById_2.get().getCode()); } } }
运行结果:
mock_code1
dictTypeById_2为空
when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional);
when(dictTypeDao.findById("1")).thenThrow(new RuntimeException());
doThrow(new RuntimeException()).when(dictTypeDao).deleteById("1");
doNothing().when(dictTypeRepository).deleteById("1");
上面的都是指定参数的,如果要匹配任意参数可以用anyString()、anyInt()、any(Class type)等等。
doThrow(new RuntimeException()).when(dictTypeRepository).deleteById(anyString());
verify()是验证方法执行的次数。
when(mockedList.get(anyInt())).thenReturn("element");
System.out.println(mockedList.get(999));
System.out.println(mockedList.get(99));
verify(mockedList, times(1)).get(anyInt());
verify(mockedList).get(anyInt());//1次的也可以简写成这样
verify(mockedList, times(2)).get(anyInt());
verify(mockedList, atMost(2)).get(anyInt());
verify(mockedList, atLeast(2)).get(anyInt());
verify(mockedList, atLeastOnce()).get(anyInt());
verify(mockedList, never()).get(9);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。