Skip to content

Commit

Permalink
Use factoryBeanObjectType attribute to find factory bean to replace
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wilkinsona committed Aug 10, 2016
1 parent 5fcadce commit f4985ab
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
Expand All @@ -37,6 +35,8 @@
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
Expand Down Expand Up @@ -70,12 +70,15 @@
* {@link MockBean @MockBean}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
*/
public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor,
Ordered {

private static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";

private static final String BEAN_NAME = MockitoPostProcessor.class.getName();

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

private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory,
Class<?> type) {
List<String> beans = new ArrayList<String>(
Set<String> beans = new LinkedHashSet<String>(
Arrays.asList(beanFactory.getBeanNamesForType(type)));
for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) {
beanName = BeanFactoryUtils.transformedBeanName(beanName);
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (type.getName()
.equals(beanDefinition.getAttribute(FACTORY_BEAN_OBJECT_TYPE))) {
beans.add(beanName);
}
}
for (Iterator<String> iterator = beans.iterator(); iterator.hasNext();) {
if (isScopedTarget(iterator.next())) {
iterator.remove();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.internal.util.MockUtil;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.FailingExampleService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Test for {@link MockitoPostProcessor}. See also the integration tests.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class MockitoPostProcessorTests {

Expand All @@ -49,6 +55,31 @@ public void cannotMockMultipleBeans() {
context.refresh();
}

@Test
public void canMockBeanProducedByFactoryBeanWithObjectTypeAttribute() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
MockitoPostProcessor.register(context);
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(
TestFactoryBean.class);
factoryBeanDefinition.setAttribute("factoryBeanObjectType",
SomeInterface.class.getName());
context.registerBeanDefinition("beanToBeMocked", factoryBeanDefinition);
context.register(MockedFactoryBean.class);
context.refresh();
assertThat(new MockUtil().isMock(context.getBean("beanToBeMocked"))).isTrue();
}

@Configuration
@MockBean(SomeInterface.class)
static class MockedFactoryBean {

@Bean
public TestFactoryBean testFactoryBean() {
return new TestFactoryBean();
}

}

@Configuration
@MockBean(ExampleService.class)
static class MultipleBeans {
Expand All @@ -65,4 +96,31 @@ public ExampleService example2() {

}

static class TestFactoryBean implements FactoryBean<Object> {

@Override
public Object getObject() throws Exception {
return new TestBean();
}

@Override
public Class<?> getObjectType() {
return null;
}

@Override
public boolean isSingleton() {
return true;
}

}

interface SomeInterface {

}

static class TestBean implements SomeInterface {

}

}

2 comments on commit f4985ab

@AnkleKiller
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to user on @MockBean

@snicoll
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkleKiller comments on commits are very likely to be missed. Please review the guidelines for contributing and ask questions on StackOverflow or our Gitter channel.

Please sign in to comment.