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注册到容器中,
    以供依赖注入使用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.代码实现

    注册Mock实例部分
    
    • 1
    • 2
    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());
            }
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    解析@Mock注解部分
    
    • 1
    • 2
    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;
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    3.使用Demo

    MessageSupplier是将要进行Mock的接口
    
    • 1
    • 2
    public interface MessageSupplier {
        String getMessage();
    }
    • 1
    • 2
    • 3
    这个是依赖MessageSupplier的实例类
    
    • 1
    • 2
    @Service
    public class SomeService {
    
        @Autowired
        MessageSupplier messageSupplier;
    
        public void printMessage() {
            System.out.println(messageSupplier.getMessage());
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    单元测试类
    
    • 1
    • 2
    @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();
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.总结

    在使用微服务的系统架构中,做一次单元测试会比较麻烦,可能需要启N多关联服务或者去连接N多关联服务。
    这就使得单元测试很难实行,在这种情况下可以通过上面的方法将在本模块中不存在的实例都通过Mock实例
    使用,这样使用Mockito中的doReturn等方法来模拟输入,去测试相关的代码片段。
  • 相关阅读:
    Ceph rbd删除image遭遇watchers异常处理
    Ceph OSD更换硬盘后遭遇PG Inconsistent异常与处理
    Rook Ceph OSD异常,格式化osd硬盘重新挂载
    Count on an IEnumerable<dynamic>
    [原创] [C#] 转换Excel数字列号为字母列号
    [MAC] Load Crypto.Cipher.ARC4 Failed, Use Pure Python Instead.
    转:Chrome调试工具介绍
    转:一组jQuery插件的连接
    动态的链式传递执行
    C#中克隆随机数的三种方法(为什么我想到了茴字的四种写法
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/9522898.html
Copyright © 2011-2022 走看看