最近小组要引入单元测试,对Spring、junit配合mockito进行了整合,发现网上的资料比较杂,特此整理备忘:
1、利用spring管理Junit
在测试类中添加一下两个注解,分别指定由spring来管理Junit 和spring配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
2、配置事务自动回滚
以上三个注解用于配置单元测试类的事物控制 defaultRollback = true 事物回滚 defaultRollback = false 事物不回滚
@TransactionConfiguration(transactionManager = "transactionManager",
defaultRollback = true)
@Transactional
@TestExecutionListeners({ TransactionalTestExecutionListener.class})
3、Mockito mock/spy对象注入
通过自定义MockitoDependencyInjectionTestExecutionListener管理mock/spy对象的注入,需要在属性定义上使用@Mock和 @Spy注解
@Mock标注的对象为由mockito生成模拟对象,对象的方法均为模拟方法
@Spy需要和@Autowired或@Resource注解一起配合使用,标注的对象由mockito根据Spring注入对象生成副本,如果指定方法的模拟调用,则调用模拟的方法,否则调用真实处理方法
@TestExecutionListeners({ MockitoDependencyInjectionTestExecutionListener.class })
MockitoDependencyInjectionTestExecutionListener.java 代码如下所示
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
public class MockitoDependencyInjectionTestExecutionListener extends
DependencyInjectionTestExecutionListener {
private static final Map<String, MockObject> mockObject = new HashMap<String, MockObject>();
private static final List<Object> injectFields = new ArrayList<Object>();
private static final List<Object> springInjectFields = new ArrayList<Object>();
@Override
protected void injectDependencies(TestContext testContext) throws Exception {
// 防止多个单元测试打包测试的时候相互干扰
mockObject.clear();
injectFields.clear();
springInjectFields.clear();
super.injectDependencies(testContext);
init(testContext);
}
@SuppressWarnings("rawtypes")
private void init(final TestContext testContext) throws Exception {
AutowireCapableBeanFactory beanFactory = testContext
.getApplicationContext().getAutowireCapableBeanFactory();
Object bean = testContext.getTestInstance();
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Mock) {
MockObject obj = new MockObject();
obj.setType(field.getType());
obj.setObj(Mockito.mock(field.getType()));
field.setAccessible(true);
field.set(bean, obj.getObj());
mockObject.put(field.getName(), obj);
} else if (annotation instanceof Spy) {
// 用spy方法构建spring管理的service对应的真实对象
// @Spy注解需要和@Autowired或@Resource配合使用
MockObject obj = new MockObject();
field.setAccessible(true);
obj.setType(field.getType());
obj.setObj(Mockito.spy(<span style="color:#ff6666;">AopTargetUtils.getTarget</span>(field
.get(bean))));
field.set(bean, obj.getObj());
mockObject.put(field.getName(), obj);
} else if (annotation instanceof TestService) {
field.setAccessible(true);
Object obj = field.getType().newInstance();
field.set(bean, obj);
injectFields.add(obj);
} else if (annotation instanceof Autowired
|| annotation instanceof Resource) {
// 记录spring注入的对象
springInjectFields.add(field);
}
}
}
for (Object obj : injectFields) {
try {
Class destClazz = obj.getClass();
Field[] destFields = destClazz.getDeclaredFields();
for (Annotation annotation : destClazz.getDeclaredAnnotations()) {
Class clazz = annotation.annotationType();
System.out.println(clazz.getName());
}
for (Field deField : destFields) {
System.out.println(deField.getName());
}
for (Field destField : destFields) {
MockObject mockObj = mockObject.get(destField.getName());
if (mockObj != null) {
destField.setAccessible(true);
destField.set(obj, mockObj.getObj());
} else {
Annotation[] annotations = destField.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Autowired
|| annotation instanceof Resource) {
destField.setAccessible(true);
Object o = beanFactory.getBean(
destField.getName(),
destField.getType());
destField.set(obj, o);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 将模拟的对象注入Spring管理的对象中
for (Object obj : springInjectFields) {
try {
Class destClazz;
Field field = (Field) obj;
field.setAccessible(true);
Object object = field.get(bean);
// 如果是Spring代理的话,找到真正的对象
destClazz = AopUtils.getTargetClass(object);
if (destClazz == null) {
// 可能是远程实现
continue;
}
for (Field destField : destClazz.getDeclaredFields()) {
MockObject mockObj = mockObject.get(destField.getName());
if (null != mockObj) {
destField.setAccessible(true);
destField.set(AopTargetUtils.getTarget(object),
mockObj.getObj());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
利用spy构建对象时不能直接用spring代理对象,需要通过AopTargetUtils的getTarget方发获取真实对象进行构建,AopTargetUtils源码如下所示
import java.lang.reflect.Field;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
public class AopTargetUtils {
/**
* 获取 目标对象
*
* @param proxy
* 代理对象
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy) throws Exception {
if (!AopUtils.isAopProxy(proxy)) {
return proxy;// 不是代理对象
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else { // cglib
return getCglibProxyTargetObject(proxy);
}
}
private static Object getCglibProxyTargetObject(Object proxy)
throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField(
"advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised
.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy)
throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy))
.getTargetSource().getTarget();
return target;
}
}
4、定义测试基类
结合以上配置项定义测试基类BaseTestCase
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
@TestExecutionListeners({ TransactionalTestExecutionListener.class,
MockitoDependencyInjectionTestExecutionListener.class })
public abstract class BaseTestCase extends AbstractJUnit4SpringContextTests {
}
5、应用举例
public class ParkingInfoServiceTest extends BaseTestCase{
@Autowired
ParkingInfoService parkingInfoService;
// mock对象所有的方法均为模拟方法
@Mock
BaiduLbsService baiduLbsService;
// Spy对象为spring注入对象的副本只有声明模拟方法时调用模拟的方法
// Spy标签需配合Autowired或Resource标签使用
@Spy
@Resource
BerthInfoDao berthInfoDao;
/**
* mockito调用举例
*/
@Test
public void testSomeMethod(){
Mockito.when(berthInfoDao.get(Mockito.anyInt())).thenReturn(null);
Mockito.doThrow(new RuntimeException()).when(berthInfoDao).get(1);
Mockito.doReturn(null).when(berthInfoDao).get(1);
}
}