测试工作中,比较耗费时间的工作:
对远程数据库的依赖
调用第三方应用的耗时(主要是网络交互错误、响应时间)
运行较慢的系统(需要等待)
真实对象尚不存在(比如需要用到其他产品的部分模块)
对于第三方模拟模块的生成一直是个考验开发人员耐力、积极性的,为了提高测试驱动开发的效率,我们选择了眼下比较常用的测试框架:
Mockito
英文文档地址:http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html
mock 限制:对于final类、匿名类和Java的基本类型是无法进行mock。
项目最新版本pom依赖:
<!-- pom依赖 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
mockito 中annonation
@Mock:被该注解标识的对象为mock对象。如: @Mock private ArticleCalculator calculator;
@InjectMocks:此注解是将已有的mock对象,通过类型、名字来注入到被测试类中的使用的。
@Spy:表示该实际对象的行为是被mockito所监视。
@Captor: 参数捕获器捕获方法参数进行验证
要使annonation起到作用:
1、在测试类中初始化annonation的配置:MockitoAnnotations.initMocks(testClass); 即测试类中有@Before注解标注的方法中。
2、可以使用测试类中使用运行器:@RunWith(MockitoJUnitRunner.class) 。
mockito 使用Demo:
远程数据库的demo:
// 业务测试类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.pm.dao.BaseDao;
@Component("baseBiz")
public class BaseBiz {
@Autowired
private BaseDao baseDao;
public Map<String, Object> getUser(String userName){
return baseDao.getUser(userName);
}
public Map<String, Object> getUserThrow(String userName) throws Exception {
return baseDao.getUser(userName);
}
public String getUserCard(String userName){
return baseDao.getUserCard(userName);
}
}
// 操作数据库类
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component("baseDao")
public class BaseDao {
public Map<String, Object> getUser(String userName){
Map<String, Object> map = new HashMap<String, Object>();
map.put("userName", userName);
return map;
}
public Map<String, Object> getUserThrow(String userName) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("userName", userName);
return map;
}
public String getUserCard(String userName) {
return userName == null || userName.length() ==0 ? "userName is null" : userName + "4502399489548283848";
}
}
测试用例:
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.ReflectionTestUtils;
import com.pm.dao.BaseDao;
//ReflectionTestUtils.setField(baseBiz, "baseDao", baseDao); 设置mock对象
@RunWith(MockitoJUnitRunner.class)
public class BaseBizTest {
private Logger logger = LoggerFactory.getLogger(getClass());
@InjectMocks
private BaseBiz baseBiz;
@Mock
private BaseDao baseDao;
@Spy
private BaseDao baseDaoSpy;
@Captor
private ArgumentCaptor<String> captor;
@Before
public void setUp() {
// baseDao = mock(BaseDao.class);
// MockitoAnnotations.initMocks(this);
}
// 用于返回值设定
@Test
public void testGetUser() {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
when(baseDao.getUser("xiaoxiao")).thenReturn(returnMap);
Map<String, Object> user = baseBiz.getUser("xiaoxiao");
logger.info(user.toString());
verify(baseDao).getUser("xiaoxiao");
}
@Test
public void testGetUserAnswer() {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
String userName = "abc";
when(baseDao.getUser(userName)).thenAnswer(new Answer<Map<String, Object>>() {
/**
* invocation 中方法
* getArguments() 调用后会以Object数组的方式返回mock方法调用的参数。
* getMethod() 返回java.lang.reflect.Method 对象
* getMock() 返回mock对象
* callRealMethod() 真实方法调用,如果mock的是接口它将会抛出异常
*/
public Map<String, Object> answer(InvocationOnMock invocation) throws Throwable {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "xiaoxiao");
Object[] argus = invocation.getArguments();
if (!argus[0].equals("xiaoxiao")){
returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
}
return returnMap;
}
});
Map<String, Object> user = baseBiz.getUser(userName);
logger.info(user.toString());
verify(baseDao).getUser(userName);
}
// 设置方法调用时产生异常
@Test(expected=Exception.class)
public void testGetUserThrowThenThrow() throws Exception {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
when(baseDao.getUser("xiaoxiao")).thenThrow(new Exception());
Map<String, Object> user = baseBiz.getUser("xiaoxiao");
logger.info(user.toString());
verify(baseDao).getUser("xiaoxiao");
}
@Test
public void testGetUserThrowDoThrow() throws Exception {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
doThrow(new Exception()).when(baseDao).getUserThrow("xiaoxiao");
Map<String, Object> user = baseBiz.getUser("xiaoxiao");
logger.info(user.toString());
verify(baseDao).getUser("xiaoxiao");
}
// 主要用于验证
@Test
public void testArgumentCaptor() {
baseBiz.getUser("xiaoxiao");
baseBiz.getUser("mengmeng");
// 验证参数是否传递正确 ,是否被调用两次
// captor.capture() 捕获方法参数
// captor.getValue() 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参数值
// captor.getAllValues() 方法进行多次调用后,返回多个参数值
verify(baseDao, times(2)).getUser(captor.capture());
logger.info(captor.getValue());
// assertEquals("xiaoxiao", captor.getValue());
assertEquals("mengmeng", captor.getValue());
}
// 用于参数匹配,无需关心参数值
@Test
public void testArgumentMatcher() {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
when(baseDao.getUser(anyString())).thenReturn(returnMap);
logger.info(baseBiz.getUser("hello").toString());
verify(baseDao).getUser("hello");
verify(baseDao).getUser(eq("hello"));
}
// 用于参数判断是否符合方法需要
// 自定义参数匹配规则 如果不是XiaoXiao将匹配失败
@Test
public void testArgumentMatcherCustom() {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
class XiaoXiaoFalse extends ArgumentMatcher<String> {
@Override
public boolean matches(Object argument) {
return ((String)argument).equals("XiaoXiao");
}
}
// argThat(Matcher<T> matcher)方法用来应用自定义的规则
when(baseDao.getUser(argThat(new XiaoXiaoFalse()))).thenReturn(returnMap);
logger.info(baseBiz.getUser("XiaoXiao").toString());
verify(baseDao).getUser(argThat(new XiaoXiaoFalse()));
}
// spy 真实Object,只stub其中部分的方法,其他方法继续真实的跑。 针对spy【监控】 真实Object 时少用、慎用when 的语法, 尽量用doReturn语法
@Test
public void testSpy() {
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("userName", "returnMap");
doReturn(returnMap).when(baseDaoSpy).getUser("xiaojian");
// when(baseDaoSpy.getUser("xiaojian")).thenReturn(returnMap);
ReflectionTestUtils.setField(baseBiz, "baseDao", baseDaoSpy);
Map<String, Object> user = baseBiz.getUser("xiaojian");
logger.info(user.toString());
logger.info(baseBiz.getUserCard("xiaojian"));
verify(baseDaoSpy).getUser("xiaojian");
}
}