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 等方法来模拟输入, 去测试相关的代码片段.
来源: http://www.bubuko.com/infodetail-2745458.html