叁叁肆2018-10-25 16:49此文已由作者许孟授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
一、问题背景
当接手一个工程,发现其所有的单测都没有进行Mock,依赖数据库的数据,依赖外部Dubbo接口服务,而这些依赖经常因为不稳定导致单测执行失败,非常影响工作效率,而如果逐个去修改所有的单测,无异于愚公移山。
这些已经存在的被单测对象是依赖了A,而A又间接依赖了B,B再依赖了某个外部Dubbo接口,如果想要对间接依赖的服务进行Mock,发现主流的Mock框架并不支持。
在库存的单测中,我尝试使用了一种全局统一Mock外部依赖的方案,并且发现效果很好,并于2017年8月上线,从此再也没有因为外部Dubbo接口不稳定而失败的单测了,因此介绍给大家。
二、解决方案
理论基础:
单测需要的外部Mock服务接口是无状态的,也就是说在给定一定的输入参数,必定期望返回对应的输出,稳定的输入不会产生不稳定的输出。
框架支持:
Spring框架提供统一的Bean管理容器,并提供了BeanPostProcessor扩展点针对Bean的初始化前后进行自定义处理。
Java提供了动态代理可以让我们只需要实现外部接口的部分方法就可以动态创建一个实现类。
具体实现:
1. 使用动态代理实现一个Mock Bean,只需要实现我们需要Mock的部分方法即可
public class TargetApiMocker implements InvocationHandler {
public static Object getMockInstance(Object bean) {
return Proxy.newProxyInstance(bean.getClass().getClassLoader(), new Class[] { TargetApi.class },
new TargetApiMocker());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("mockMethod1")) {
return mockMethod1((String) args[0]);
} else if (method.getName().equals("mockMethod2")) {
return mockMethod2((List<String>) args[0]);
}
throw new UnmockedDubboMethodException();
}
private String mockMethod1(String arg) {
return arg + arg;
}
private List<String> mockMethod2(List<String> list) {
return list;
}
}
2. 实现BeanPostProcessor接口,将Spring容器中的Dubbo Bean替换为我们的Mock Bean
public class MockerFactory implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof TargetApi) {
return TargetApiMocker.getMockInstance(bean);
} else {
return bean;
}
}
}
3. 在test_applicationContext.xml 中添加Bean定义
<bean/>
通过以上代码就可以不侵入原用单测的情况下,全局统一的Mock掉了外部依赖,节省了大量的重构时间,用起来很爽就是了。。。。
网易云免费体验馆,0成本体验20+款云产品!
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 活动预告丨易盾CTO朱浩齐将出席2018 AIIA大会,分享《人工智能在内容安全的应用实践》
【推荐】 Bug是一种财富-------研发同学的错题集、测试同学的遗漏用例集