1. 程式人生 > >在SpringTest中將Mockito的mock對象通過spring註入使用

在SpringTest中將Mockito的mock對象通過spring註入使用

abstract factory tco 實現 instance collect spring lan rri

轉載: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等方法來模擬輸入,去測試相關的代碼片段。

在SpringTest中將Mockito的mock對象通過spring註入使用