在SpringTest中将Mockito的mock对象通过spring注入使用

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等方法来模拟输入,去测试相关的代码片段。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值