在spring框架中,结合mockito打桩做单元测试

最近研究单元测试,在以前做单元测试的项目中都没用到spring框架,直接是用AspectJ将需要打桩的对象替换为mock对象。于是在这个项目中,也打算用AJ+mockito的方式来做打桩,结果却发现桩对象并没有被AJ替换掉,后来简单地单步跟了一下代码,发现AJ是在属性进行set的时候 触发aop,进行替换(这特么怎么和以前理解的AJ不一样啊,以前在eclipse下,貌似是直接静态编译的时候就替换掉了的,现在用intellij,不是这个原理了么?此外,就算编译的时候将有@Resource注解的属性替换了,但是后面还是可能被spring框架注入框架中的bean,于是放弃了这个方案)。

后来又尝试了Mockito的@InjectMocks注解,试了很多次,都没有成功。最后找到一篇文章(http://pwind.iteye.com/blog/1275159),需要自己实现一个TestExecutionListener

  1. public class MockitoDependencyInjectionTestExecutionListener extends DependencyInjectionTestExecutionListener {  
  2.     private static final Map<String, MockObject> mockObject   = new HashMap<String, MockObject>();  
  3.     private static final List<Field>             injectFields = new ArrayList<Field>();  
  4.     @Override  
  5.     protected void injectDependencies(final TestContext testContext) throws Exception {  
  6.         super.injectDependencies(testContext);  
  7.         init(testContext);  
  8.     }  
  9.     protected void injectMock(final TestContext testContext) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {  
  10.         AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();  
  11.         for (Field field : injectFields) {  
  12.             Object o = beanFactory.getBean(field.getName(), field.getType());  
  13.             if (null != o) {  
  14.                 Method[] methods = o.getClass().getDeclaredMethods();  
  15.                 for (Method method : methods) {  
  16.                     if (method.getName().startsWith("set")) {  
  17.                         for (Iterator it = mockObject.keySet().iterator(); it.hasNext();) {  
  18.                             String key = (String) it.next();  
  19.                             if (method.getName().equalsIgnoreCase("set" + key)) {  
  20.                                 method.invoke(o, mockObject.get(key).getObj());  
  21.                                 break;  
  22.                             }  
  23.                         }  
  24.                     }  
  25.                 }  
  26.             }  
  27.         }  
  28.     }  
  29.   
  30.     private void init(final TestContext testContext) throws Exception {  
  31.         Object bean = testContext.getTestInstance();  
  32.         Field[] fields = bean.getClass().getDeclaredFields();  
  33.         for (Field field : fields) {  
  34.             Annotation[] annotations = field.getAnnotations();  
  35.             for (Annotation antt : annotations) {  
  36.                 if (antt instanceof org.mockito.Mock) {  
  37.                     // 注入mock实例  
  38.                     MockObject obj = new MockObject();  
  39.                     obj.setType(field.getType());  
  40.                     obj.setObj(mock(field.getType()));  
  41.                     field.setAccessible(true);  
  42.                     field.set(bean, obj.getObj());  
  43.                     mockObject.put(field.getName(), obj);  
  44.                 } else if (antt instanceof Autowired) {  
  45.                     // 只对autowire重新注入  
  46.                     injectFields.add(field);  
  47.                 }  
  48.             }  
  49.         }  
  50.         for (Field field : injectFields) {  
  51.             field.setAccessible(true);  
  52.             Object object = field.get(bean);  
  53.             if (object instanceof Proxy) {  
  54.                 // 如果是代理的话,找到真正的对象  
  55.                 Class targetClass = AopUtils.getTargetClass(object);  
  56.                 if (targetClass == null) {  
  57.                     // 可能是远程实现  
  58.                     return;  
  59.                 }  
  60.                 Field[] targetFields = targetClass.getDeclaredFields();  
  61.                 for (int i = 0; i < targetFields.length; i++) {  
  62.                     // 针对每个需要重新注入的字段  
  63.                     for (Map.Entry<String, MockObject> entry : mockObject.entrySet()) {  
  64.                         // 针对每个mock的字段  
  65.                         if (targetFields[i].getName().equals(entry.getKey())) {  
  66.                             targetFields[i].setAccessible(true);  
  67.                             targetFields[i].set(getTargetObject(object, entry.getValue().getType()),  
  68.                                                 entry.getValue().getObj());  
  69.                         }  
  70.                     }  
  71.                 }  
  72.             } else {  
  73.                 injectMock(testContext);  
  74.             }  
  75.         }  
  76.     }  
  77.   
  78.     protected <T> T getTargetObject(Object proxy, Class<T> targetClass) throws Exception {  
  79.         if (AopUtils.isJdkDynamicProxy(proxy)) {  
  80.             return (T) ((Advised) proxy).getTargetSource().getTarget();  
  81.         } else {  
  82.             return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class  
  83.         }  
  84.     }  
  85.   
  86.     public static class MockObject {  
  87.         private Object   obj;  
  88.         private Class<?> type;  
  89.   
  90.         public MockObject(){  
  91.         }  
  92.   
  93.         public Object getObj() {  
  94.             return obj;  
  95.         }  
  96.   
  97.         public void setObj(Object obj) {  
  98.             this.obj = obj;  
  99.         }  
  100.   
  101.         public Class<?> getType() {  
  102.             return type;  
  103.         }  
  104.   
  105.         public void setType(Class<?> type) {  
  106.             this.type = type;  
  107.         }  
  108.     }  
  109.   
  110.     public static Map<String, MockObject> getMockobject() {  
  111.         return mockObject;  
  112.     }  
  113.   
  114.     public static List<Field> getInjectfields() {  
  115.         return injectFields;  
  116.     }  
  117. }  


再在测试基类加上注解:      @TestExecutionListeners

这样就不需要在执行单元测试之前执行MockitoAnnotations.initMocks(this)。 

需要mock dao层的时候,就把repository抓出来,然后加上@InjectMocks注解,这样在单元测试启动的时候,就会将该测试中标注了@Mocks注解的mock对象注入到该bean中,完成属性替换。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值