Prevent early initialization in MockitoPostProcessor

Fixes gh-20665
This commit is contained in:
Madhura Bhave 2020-04-22 14:35:50 -07:00
parent 102729b5e1
commit b9c2b7b257
2 changed files with 60 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -247,9 +247,9 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
} }
private Set<String> getExistingBeans(ConfigurableListableBeanFactory beanFactory, ResolvableType type) { private Set<String> getExistingBeans(ConfigurableListableBeanFactory beanFactory, ResolvableType type) {
Set<String> beans = new LinkedHashSet<>(Arrays.asList(beanFactory.getBeanNamesForType(type))); Set<String> beans = new LinkedHashSet<>(Arrays.asList(beanFactory.getBeanNamesForType(type, true, false)));
String typeName = type.resolve(Object.class).getName(); String typeName = type.resolve(Object.class).getName();
for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) { for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class, true, false)) {
beanName = BeanFactoryUtils.transformedBeanName(beanName); beanName = BeanFactoryUtils.transformedBeanName(beanName);
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (typeName.equals(beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE))) { if (typeName.equals(beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE))) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,11 +16,18 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.FailingExampleService; import org.springframework.boot.test.mock.mockito.example.FailingExampleService;
@ -29,6 +36,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@ -39,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Andreas Neiser * @author Andreas Neiser
* @author Madhura Bhave
*/ */
class MockitoPostProcessorTests { class MockitoPostProcessorTests {
@ -123,6 +134,16 @@ class MockitoPostProcessorTests {
assertThat(Mockito.mockingDetails(context.getBean("exampleQualified", ExampleService.class)).isSpy()).isTrue(); assertThat(Mockito.mockingDetails(context.getBean("exampleQualified", ExampleService.class)).isSpy()).isTrue();
} }
@Test
void postProcessorShouldNotTriggerEarlyInitialization() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(FactoryBeanRegisteringPostProcessor.class);
MockitoPostProcessor.register(context);
context.register(TestBeanFactoryPostProcessor.class);
context.register(EagerInitBean.class);
context.refresh();
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@MockBean(SomeInterface.class) @MockBean(SomeInterface.class)
static class MockedFactoryBean { static class MockedFactoryBean {
@ -258,6 +279,14 @@ class MockitoPostProcessorTests {
} }
@Configuration(proxyBeanMethods = false)
static class EagerInitBean {
@MockBean
private ExampleService service;
}
static class TestFactoryBean implements FactoryBean<Object> { static class TestFactoryBean implements FactoryBean<Object> {
@Override @Override
@ -275,6 +304,33 @@ class MockitoPostProcessorTests {
return true; return true;
} }
};
static class FactoryBeanRegisteringPostProcessor implements BeanFactoryPostProcessor, Ordered {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestFactoryBean.class);
((BeanDefinitionRegistry) beanFactory).registerBeanDefinition("test", beanDefinition);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
static class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String, BeanWrapper> cache = (Map<String, BeanWrapper>) ReflectionTestUtils.getField(beanFactory,
"factoryBeanInstanceCache");
Assert.isTrue(cache.isEmpty(), "Early initialization of factory bean triggered.");
}
} }
interface SomeInterface { interface SomeInterface {