Skip to content

Commit f4985ab

Browse files
committed
Use factoryBeanObjectType attribute to find factory bean to replace
Previously, MockitoPostProcessor would fail to replace a factory bean with a mock if the factory bean didn't return a matching type from getObjectType(). This prevented Spring Data respoitories from being replaced with a mock as Spring Data's repository factory beans generally do not know the specific repository type that they will produce when MockPostProcesser (a bean factory post-processor) is running. Spring Data has been updated to add a factoryBeanObjectType attribute to its factory bean definitions. MockitoPostProcessor has been updated to look for FactoryBeans with this attribute and to use its value to determine whether or not the factory bean produces a bean of the required type and, therefore, should be replaced with a mock. Closes gh-6541
1 parent 5fcadce commit f4985ab

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@
1818

1919
import java.beans.PropertyDescriptor;
2020
import java.lang.reflect.Field;
21-
import java.util.ArrayList;
2221
import java.util.Arrays;
2322
import java.util.HashMap;
2423
import java.util.Iterator;
2524
import java.util.LinkedHashMap;
2625
import java.util.LinkedHashSet;
27-
import java.util.List;
2826
import java.util.Map;
2927
import java.util.Set;
3028
import java.util.TreeSet;
@@ -37,6 +35,8 @@
3735
import org.springframework.beans.factory.BeanCreationException;
3836
import org.springframework.beans.factory.BeanFactory;
3937
import org.springframework.beans.factory.BeanFactoryAware;
38+
import org.springframework.beans.factory.BeanFactoryUtils;
39+
import org.springframework.beans.factory.FactoryBean;
4040
import org.springframework.beans.factory.config.BeanDefinition;
4141
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
4242
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -70,12 +70,15 @@
7070
* {@link MockBean @MockBean}.
7171
*
7272
* @author Phillip Webb
73+
* @author Andy Wilkinson
7374
* @since 1.4.0
7475
*/
7576
public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
7677
implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor,
7778
Ordered {
7879

80+
private static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
81+
7982
private static final String BEAN_NAME = MockitoPostProcessor.class.getName();
8083

8184
private static final String CONFIGURATION_CLASS_ATTRIBUTE = Conventions
@@ -240,8 +243,16 @@ private void registerSpy(ConfigurableListableBeanFactory beanFactory,
240243

241244
private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory,
242245
Class<?> type) {
243-
List<String> beans = new ArrayList<String>(
246+
Set<String> beans = new LinkedHashSet<String>(
244247
Arrays.asList(beanFactory.getBeanNamesForType(type)));
248+
for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) {
249+
beanName = BeanFactoryUtils.transformedBeanName(beanName);
250+
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
251+
if (type.getName()
252+
.equals(beanDefinition.getAttribute(FACTORY_BEAN_OBJECT_TYPE))) {
253+
beans.add(beanName);
254+
}
255+
}
245256
for (Iterator<String> iterator = beans.iterator(); iterator.hasNext();) {
246257
if (isScopedTarget(iterator.next())) {
247258
iterator.remove();

spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,23 @@
1919
import org.junit.Rule;
2020
import org.junit.Test;
2121
import org.junit.rules.ExpectedException;
22+
import org.mockito.internal.util.MockUtil;
2223

24+
import org.springframework.beans.factory.FactoryBean;
25+
import org.springframework.beans.factory.support.RootBeanDefinition;
2326
import org.springframework.boot.test.mock.mockito.example.ExampleService;
2427
import org.springframework.boot.test.mock.mockito.example.FailingExampleService;
2528
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2629
import org.springframework.context.annotation.Bean;
2730
import org.springframework.context.annotation.Configuration;
2831

32+
import static org.assertj.core.api.Assertions.assertThat;
33+
2934
/**
3035
* Test for {@link MockitoPostProcessor}. See also the integration tests.
3136
*
3237
* @author Phillip Webb
38+
* @author Andy Wilkinson
3339
*/
3440
public class MockitoPostProcessorTests {
3541

@@ -49,6 +55,31 @@ public void cannotMockMultipleBeans() {
4955
context.refresh();
5056
}
5157

58+
@Test
59+
public void canMockBeanProducedByFactoryBeanWithObjectTypeAttribute() {
60+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
61+
MockitoPostProcessor.register(context);
62+
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(
63+
TestFactoryBean.class);
64+
factoryBeanDefinition.setAttribute("factoryBeanObjectType",
65+
SomeInterface.class.getName());
66+
context.registerBeanDefinition("beanToBeMocked", factoryBeanDefinition);
67+
context.register(MockedFactoryBean.class);
68+
context.refresh();
69+
assertThat(new MockUtil().isMock(context.getBean("beanToBeMocked"))).isTrue();
70+
}
71+
72+
@Configuration
73+
@MockBean(SomeInterface.class)
74+
static class MockedFactoryBean {
75+
76+
@Bean
77+
public TestFactoryBean testFactoryBean() {
78+
return new TestFactoryBean();
79+
}
80+
81+
}
82+
5283
@Configuration
5384
@MockBean(ExampleService.class)
5485
static class MultipleBeans {
@@ -65,4 +96,31 @@ public ExampleService example2() {
6596

6697
}
6798

99+
static class TestFactoryBean implements FactoryBean<Object> {
100+
101+
@Override
102+
public Object getObject() throws Exception {
103+
return new TestBean();
104+
}
105+
106+
@Override
107+
public Class<?> getObjectType() {
108+
return null;
109+
}
110+
111+
@Override
112+
public boolean isSingleton() {
113+
return true;
114+
}
115+
116+
}
117+
118+
interface SomeInterface {
119+
120+
}
121+
122+
static class TestBean implements SomeInterface {
123+
124+
}
125+
68126
}

0 commit comments

Comments
 (0)