您现在的位置是:首页 > 文章详情

使用Mockito修改Bean的依赖

日期:2018-11-28点击:635

概述

在使用单元测试时经常会遇到某些dependency依赖了外部资源,或者想主动绕过真正的方法执行mock返回结果而快速得到单元测试最终的期望结果,可能有以下两种场景,


对于TestCase A,设单元测试的方法是Service A的execute1方法和execute2方法,在执行execute1和execute2方法时都会调用ServiceB的不同方法,即ServiceA依赖了ServiceB;一个场景是完全对ServiceB进行Mock,如单元测试ServiceA#execute1方法时都通过Mock返回结果;一个场景是部分ServiceB的方法执行真实的业务逻辑(如查询数据库),一部分方法执行Mock返回结果,或Spy,如如单元测试ServiceA#execute2方法时,只mock ServiceB#b2结果,真正执行ServiceB#b1方法。


对TestCase的Service的依赖Bean的完全Mock

当对ServiceA的方法执行单元测试时,如ServiceA -> ServiceB,此时对ServiceB进行Mock,然后将其设置到ServiceA的属性中;后续ServiceA调用ServiceB的方法都降得到Mock后的结果;而对于ServiceB对象的本来的依赖本案暂且将其忽略,后续改进;


思路是在TestCase中依赖ServiceA的同时标示Mock ServiceB,待TestCase依赖注入完成后,新建ServiceB的Mock对象替换ServiceA中的ServiceB依赖;

@TestExecutionListeners({MockitoDependencyInjectionTestExecutionListener.class}) public class AServiceMockTest extends BaseTest {     @Mock     private BService bservice;     @Autowired     private AService aservice;     @Before     public void setup(){         doReturn("mock").when(bservice).b1();     }     @Test     public void test() {         a.execute1();     } } @Service public class AServiceImpl implements AService {     @Autowired     private BService bservice;           @Override     public String execute1() {         return bservice.b1(); //will return mock after Mock     } }

当a.execute()执行时将调用aservice的属性bservice的b1方法,返回结果就是在setup方法中指定的结果;


监听TestCase的Service的依赖Bean

当对ServiceA进行单元测试时,依赖了ServiceB,需要获取ServiceB的b1方法的真正执行结果,Mock b2方法的结果,此时可以采用Spy方式;由于ServiceA依赖了ServiceB,而这个属性可能是个AopProxy对象,并不能直接使用Mockito.mock(bservice)或者Mockito.spy(bservice),所以这里@Spy注解指定的是实现类,通过MockitoDependencyInjectionTestExecutionListener处理后,获得一个Spy对象,同时这个Spy对象设置到bservice(AopProxy对象)中去;

@TestExecutionListeners({MockitoDependencyInjectionTestExecutionListener.class}) public class AServiceMockTest extends BaseTest {     @Spy     private BServiceImpl bserviceImpl;     @Autowired     private AService aservice;     @Before     public void setup(){         doReturn(true).when(bserviceImpl).b2(any(String.class));     }     @Test     public void test() {         a.execute();     } } @Service public class AServiceImpl implements AService {     @Autowired     private BService bservice;           @Override     public boolean execute2() {         String str = bservice.b1();         return bservice.b2(str);     } }


MockitoDependencyInjectionTestExecutionListener的实现

public class MockitoDependencyInjectionTestExecutionListener extends DependencyInjectionTestExecutionListener {     private Set<Field> injectFields = new HashSet<>();     private Map<String,Object> mockObjectMap = new HashMap<>();     @Override     protected void injectDependencies(TestContext testContext) throws Exception {         super.injectDependencies(testContext);         init(testContext);     }     /**      * when A dependences on B      * mock B or Spy on targetObject of bean get from Spring IoC Container whose type is B.class or beanName is BImpl      * @param testContext      */     private void init(TestContext testContext) throws Exception {         AutowireCapableBeanFactory factory =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){                     Class<?> clazz = field.getType();                     Object object = Mockito.mock(clazz);                     field.setAccessible(true);                     field.set(bean, object);                     mockObjectMap.put(field.getName(), object);                 } else if(annotation instanceof Spy) {                     Object fb = factory.getBean(field.getName()); //may be a proxy that can not be spy because $Proxy is final                     Object targetSource = AopTargetUtils.getTarget(fb);                     Object spyObject = Mockito.spy(targetSource);                     if (!fb.equals(targetSource)) { //proxy                         if (AopUtils.isJdkDynamicProxy(fb)) {                             setJdkDynamicProxyTargetObject(fb, spyObject);                         } else { //cglib                             setCglibProxyTargetObject(fb, spyObject);                         }                     } else {                         mockObjectMap.put(field.getName(), spyObject);                     }                     field.setAccessible(true);                     field.set(bean, spyObject);                 }else if (annotation instanceof Autowired){                     injectFields.add(field);                 }             }         }         for(Field field: injectFields) {             field.setAccessible(true);             Object fo = field.get(bean);             if (AopUtils.isAopProxy(fo)) {                 Class targetClass = AopUtils.getTargetClass(fo);                 if(targetClass ==null)                     return;                 Object targetSource = AopTargetUtils.getTarget(fo);                 Field[] targetFields =targetClass.getDeclaredFields();                 for(Field targetField : targetFields){                     targetField.setAccessible(true);                     if(mockObjectMap.get(targetField.getName()) ==null){                         continue;                     }                     ReflectionTestUtils.setField(targetSource,targetField.getName(), mockObjectMap.get(targetField.getName()));                 }             } else {                 Object realObject = factory.getBean(field.getType());                 if(null != realObject) {                     Field[] targetFields = realObject.getClass().getDeclaredFields();                     for(Field targetField : targetFields){                         targetField.setAccessible(true);                         if(mockObjectMap.get(targetField.getName()) ==null){                             continue;                         }                         ReflectionTestUtils.setField(fo,targetField.getName(), mockObjectMap.get(targetField.getName()));                     }                 }             }         }     }     private void setCglibProxyTargetObject(Object proxy, Object spyObject) throws NoSuchFieldException, IllegalAccessException {         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);         ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).setTarget(spyObject);     }     private void setJdkDynamicProxyTargetObject(Object proxy, Object spyObject) throws NoSuchFieldException, IllegalAccessException {         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);         ((AdvisedSupport) advised.get(aopProxy)).setTarget(spyObject);     } }


maven依赖

JUnit、Mockito



原文链接:https://blog.roncoo.com/article/133824
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章