转载:https://blog.csdn.net/m0_38043362/article/details/80111957

1. 原理介绍

通过BeanFactoryPostProcessor向BeanFactory中注册需要进行Mock的对象,使当前Bean容器在依赖注入时使用
我们提供的Mock对象注入到实例中使用。
具体需要交给容器管理的mock实例,是通过TestExecutionListener在容器开始启动前去解析当前测试类中的使用@Mock
注解的字段,然后根据类型创建对应的Mock实例,将创建出来的Mock实例通过BeanFactoryPostProcessor注册到容器中,
以供依赖注入使用。

2.代码实现

注册Mock实例部分
public class MockitoBeansPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<Class<?>, MockitoBeansTestExecutionListener.MockBeanWrapper> allMockBeans = MockitoBeansTestExecutionListener.resolvedAllMockBeans();
for (Map.Entry<Class<?>, MockitoBeansTestExecutionListener.MockBeanWrapper> mockBeanWrapperEntry : allMockBeans.entrySet()) {
beanFactory.registerResolvableDependency(mockBeanWrapperEntry.getKey(), mockBeanWrapperEntry.getValue().getMockObject());
}
} }
解析@Mock注解部分
public class MockitoBeansTestExecutionListener extends AbstractTestExecutionListener {

    private static final Map<Class<?>, MockBeanWrapper> mockBeans = new ConcurrentHashMap<>(30);
private static final Map<Class<?>, List<Field>> injectMockBeans = new ConcurrentHashMap<>(30);
private static boolean hasInitialized = false; public static Map<Class<?>, MockBeanWrapper> resolvedAllMockBeans() {
Assert.isTrue(hasInitialized);
return Collections.unmodifiableMap(mockBeans);
} @Override
public void beforeTestClass(TestContext testContext) throws Exception {
Field[] declaredFields = testContext.getTestClass().getDeclaredFields();
//将需要mock的对象创建出来
for (Field field : declaredFields) {
Mock mockAnnon = field.getAnnotation(Mock.class);
if (mockAnnon != null) {
MockBeanWrapper wrapper = new MockBeanWrapper();
Class<?> type = field.getType();
wrapper.setMockObject(Mockito.mock(type));
wrapper.setBeanType(type);
wrapper.setBeanName(field.getName());
mockBeans.putIfAbsent(wrapper.getBeanType(), wrapper);
injectMockBeans.compute(testContext.getTestClass(), (targetClass, waitInjectFields) -> {
if (waitInjectFields == null) {
waitInjectFields = new ArrayList<>();
}
waitInjectFields.add(field);
return waitInjectFields;
});
}
}
hasInitialized = true;
} @Override
public void beforeTestMethod(TestContext testContext) throws Exception {
Object testInstance = testContext.getTestInstance();
List<Field> fields = injectMockBeans.get(testContext.getTestClass());
if (fields != null) {
for (Field field : fields) {
field.setAccessible(true);
field.set(testInstance, mockBeans.get(field.getType()).getMockObject());
}
}
} public class MockBeanWrapper { private String beanName;
private Class<?> beanType;
private Object mockObject; public String getBeanName() {
return beanName;
} public void setBeanName(String beanName) {
this.beanName = beanName;
} public Class<?> getBeanType() {
return beanType;
} public void setBeanType(Class<?> beanType) {
this.beanType = beanType;
} public Object getMockObject() {
return mockObject;
} public void setMockObject(Object mockObject) {
this.mockObject = mockObject;
}
}
}

3.使用Demo

MessageSupplier是将要进行Mock的接口
public interface MessageSupplier {
String getMessage();
}
这个是依赖MessageSupplier的实例类
@Service
public class SomeService { @Autowired
MessageSupplier messageSupplier; public void printMessage() {
System.out.println(messageSupplier.getMessage());
}
}
单元测试类
@TestExecutionListeners({MockitoBeansTestExecutionListener.class})
@ContextConfiguration(classes = {SimpleTestCase.class})
@ComponentScan(basePackageClasses = {SimpleTestCase.class})
public class SimpleTestCase extends AbstractJUnit4SpringContextTests { @Autowired
private SomeService someService;
@Mock
MessageSupplier messageSupplier;
@Test
public void test() { doReturn("this is mock message.")
.when(messageSupplier)
.getMessage();
someService.printMessage(); //输出this is mock message.
}
@Bean
public BeanFactoryPostProcessor mockBeansPostProcessor(){
return new MockitoBeansPostProcessor();
} }

4.总结

在使用微服务的系统架构中,做一次单元测试会比较麻烦,可能需要启N多关联服务或者去连接N多关联服务。
这就使得单元测试很难实行,在这种情况下可以通过上面的方法将在本模块中不存在的实例都通过Mock实例
使用,这样使用Mockito中的doReturn等方法来模拟输入,去测试相关的代码片段。

在SpringTest中将Mockito的mock对象通过spring注入使用的更多相关文章

  1. 基于spring与mockito单元测试Mock对象注入

    转载:http://www.blogjava.net/qileilove/archive/2014/03/07/410713.html 1.关键词 单元测试.spring.mockito 2.概述 单 ...

  2. Mockito中@Mock与@InjectMock

    Mockito是java单元测试中,最常用的mck工具之一,提供了诸多打桩方法和注解.其中有两个比较常用的注解,@Mock和@InjectMock,名字和在代码中使用 的位置都很像,对于初学者,很容易 ...

  3. Mockito 如何 mock 返回值为 void 的方法

    转载:https://unmi.cc/mockito-how-to-mock-void-method/#more-7748 最初接触 Mockito 还思考并尝试过如何用它来 mock 返回值为 vo ...

  4. dubbo应用程序的单元测试环境搭建(springtest,powermock,mockito)

    转:http://blog.csdn.net/yys79/article/details/66472797 最近,项目中频繁用到dubbo,而且java工程用引用了几十个关联系统的服务(如用户认证,基 ...

  5. Mockito如何mock一条链式调用

    在写单元测试的时候,不免可能需要mock一些对象出来,并且mock一些方法调用去返回一个自己想要的对象.一般的使用是这样的: FinalPumpkin pumpkin = mock(FinalPump ...

  6. java中将list、map对象写入文件

    链接地址:http://blog.sina.com.cn/s/blog_4a4f9fb50101p6jv.html     推荐:凤爪女瓜子男怪象该谁反思伦敦房价为什么持续暴涨 × wvqusrtg个 ...

  7. 简单介绍如何使用PowerMock和Mockito来mock 1. 构造函数 2. 静态函数 3. 枚举实现的单例 4. 选择参数值做为函数的返回值(转)

    本文将简单介绍如何使用PowerMock和Mockito来mock1. 构造函数2. 静态函数3. 枚举实现的单例4. 选择参数值做为函数的返回值5. 在调用mock出来的方法中,改变方法参数的值 一 ...

  8. 持续集成之路 —— Mock对象引起的测试失败

    今天遇到了一个很奇怪的问题,纠结了好久.在和同事念叨这个问题时,突然想到了问题所在. 问题现象: 在一个Service的单元测试类中有八个测试用例,单独运行时都可以正常通过.可是一旦一起运行时,总是会 ...

  9. Mockito不能mock final类的解决办法

    Mockito是很常用的测试工具,使用过程中可能会遇到下面的问题: Mockito cannot mock/spy because : - final class 问题重现: 引入该依赖到项目的mav ...

随机推荐

  1. [Go] defer 语句

    Go 还有一些特有的流程控制语句,其中一个就是 defer 语句.该语句用于延迟调用指定的函数,它只能出现在函数的内部,由 defer 关键字以及针对某个函数的调用表达式组成.这里被调用的函数称为 延 ...

  2. 《完全用Linux工作》作者:王垠

    完全用 GNU/Linux 工作 理解 GNU/Linux 注:本文是清华“牛仔”王垠的“成名作”,在网上引起很大的争议.对他崇拜地五体投地者有,对他嗤之以鼻者也有,总之成了一年多以前Linux 爱好 ...

  3. Modbus读写模拟量寄存器具体解释

    读可读写模拟量寄存器: 发送命令(主机向从机)格式: [设备地址] [命令号03] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] [CRC校验的低8位] [CRC校验的高 ...

  4. [转]过XX游戏驱动保护的代码

    这个是过TX游戏自我保护驱动的源代码.可以过qq堂.DNF.寻仙等QQ游戏. #include <ntddk.h>#include <windef.h>#include < ...

  5. firedac连接WINDOWS的ORACLE

    firedac连接WINDOWS的ORACLE 首先安装OCI FOR WINDOWS.OCI分WIN32和WIN64,且OCI的版本要和ORACLE的版本相一致才行. FIREDAC指定OCI驱动程 ...

  6. 监测uitableview 向上滑动和向下滑动的事件

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat height = _varietyTableView.frame.si ...

  7. WordPress主题开发:制作面包屑导航

    所谓面包屑,就是类似这种:首页 > 公司简介 > 发展历史 展示网站树型结构,并让网站访问者随时知道自己所处的位置,方便返回上几级. 将下面的代码添加到主题的 functions.php ...

  8. Spring Boot中使用JavaMailSender发送邮件

    相信使用过Spring的众多开发者都知道Spring提供了非常好用的JavaMailSender接口实现邮件发送.在Spring Boot的Starter模块中也为此提供了自动化配置.下面通过实例看看 ...

  9. JQuery攻略(四)事件

    jQuery事件处理,鼠标的单击,双击,悬停,键盘按键,文本动画..... 此章节有 1.1被点击的按钮查找 1.2事件的自动触发 1.3点击之后禁用按钮 1.4鼠标事件 1.5焦点事件 1.6CSS ...

  10. 架构模式逻辑层模式之:表模块(Table Model)

    表模块和领域模型比,有两个显著区别: 1:表模块中的类和数据库表基本一一对应,而领域模型则无此要求: 2:表模块中的类的对象处理表中的所有记录,而领域模型的一个对象代表表中的一行记录: 一般情况下,我 ...