Spring、junit、mockito单元测试总结

最近小组要引入单元测试,对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代理对象,需要通过AopTargetUtilsgetTarget方发获取真实对象进行构建,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);
 	}
}
 
 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值