zoukankan      html  css  js  c++  java
  • 在SpringTest中将Mockito的mock对象通过spring注入使用

    转载: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等方法来模拟输入,去测试相关的代码片段。
  • 相关阅读:
    python学习笔记:遍历目录
    c++笔记:友元函数
    VMware Workstation 9: This virtual machine's policies are too old to be run by this version of VMware
    inet_ntoa内存问题
    python学习笔记:sqlite3查询
    python学习笔记:利用asyncore的端口映射(端口转发)
    编写谷歌浏览器的油猴脚本
    window编译7z
    通过配置nginx的header路由到不同环境的服务器
    用U盘给物理机安装ubuntu20.04
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/9546726.html
Copyright © 2011-2022 走看看