Skip to content
This repository was archived by the owner on Mar 8, 2019. It is now read-only.

Demo of Issue #5724 @MockBean does not work for @RefreshScope bean #48

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions gh-5724/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>issues.spring.shine.colin</groupId>
<artifactId>mockbean-issue</artifactId>
<version>0-SNAPSHOT</version>

<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring.boot.version>1.4.0.M2</spring.boot.version>
<spring.cloud.version>1.1.0.RC2</spring.cloud.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.springframework.boot.test.mock.mockito;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Bean
public BeanA beanA(BeanB beanB) {
return new BeanA(beanB);
}

@RefreshScope
@Bean
public BeanB beanB() {
return new BeanB();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.springframework.boot.test.mock.mockito;

public class BeanA {

private final BeanB beanB;

public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.springframework.boot.test.mock.mockito;

public class BeanB {
}
1 change: 1 addition & 0 deletions gh-5724/src/main/resources/bootstrap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spring.cloud.config.enabled: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.springframework.boot.test.mock.mockito;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;

import java.util.Set;

public class MockitoScopedProxyContextCustomizer implements ContextCustomizer {

private final Set<Class> mockedTypes;

public MockitoScopedProxyContextCustomizer(Set<Class> mockedTypes) {
this.mockedTypes = mockedTypes;
}

public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
MockitoScopedProxyPostProcessor.register((BeanDefinitionRegistry) context, mockedTypes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.springframework.boot.test.mock.mockito;

import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MockitoScopedProxyContextCustomizerFactory implements ContextCustomizerFactory {

public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
DefinitionsParser parser = new DefinitionsParser();
parser.parse(testClass);
return new MockitoScopedProxyContextCustomizer(getMockAndSpyTypes(parser));
}

private static Set<Class> getMockAndSpyTypes(DefinitionsParser parser) {
Set<Class> mockAndSpyTypes = new HashSet<>();

for (Definition mockDef : parser.getDefinitions()) {
if (mockDef instanceof MockDefinition) {
mockAndSpyTypes.add(((MockDefinition) mockDef).getClassToMock());
}
if (mockDef instanceof SpyDefinition) {
mockAndSpyTypes.add(((SpyDefinition) mockDef).getClassToSpy());
}
}

return mockAndSpyTypes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.springframework.boot.test.mock.mockito;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;

import java.util.LinkedHashSet;
import java.util.Set;

public class MockitoScopedProxyPostProcessor implements BeanFactoryPostProcessor, Ordered {

private static final String BEAN_NAME = MockitoScopedProxyPostProcessor.class.getName();
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";

private final Set<Class> mockedTypes;

public MockitoScopedProxyPostProcessor(Set<Class> mockedTypes) {
this.mockedTypes = mockedTypes;
}

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry bdr = (BeanDefinitionRegistry) beanFactory;

for (Class mockedType : mockedTypes) {
String[] mockedBeans = beanFactory.getBeanNamesForType(mockedType);

for (String mockedBean : mockedBeans) {
if (isScopedProxy(mockedBean)) {
bdr.removeBeanDefinition(mockedBean);
}
}
}
}

private static boolean isScopedProxy(String mockedBean) {
return mockedBean.startsWith(SCOPED_TARGET_PREFIX);
}

public static void register(BeanDefinitionRegistry registry, Set<Class> mockedTypes) {
BeanDefinition definition = getOrAddBeanDefinition(registry);

if (mockedTypes != null) {
getConstructorArgs(definition).addAll(mockedTypes);
}
}

@SuppressWarnings("unchecked")
private static Set<Class> getConstructorArgs(BeanDefinition definition) {
ConstructorArgumentValues.ValueHolder constructorArg = definition.getConstructorArgumentValues()
.getIndexedArgumentValue(0, Set.class);
return (Set<Class>) constructorArg.getValue();
}

private static BeanDefinition getOrAddBeanDefinition(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
addBeanDefinition(registry);
}

return registry.getBeanDefinition(BEAN_NAME);
}

private static void addBeanDefinition(BeanDefinitionRegistry registry) {
RootBeanDefinition def = new RootBeanDefinition(MockitoScopedProxyPostProcessor.class);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ConstructorArgumentValues constructorArguments = def.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, new LinkedHashSet<Class>());
registry.registerBeanDefinition(BEAN_NAME, def);
}

public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.springframework.boot.test.mock.mockito;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MockitoScopedProxyTest {

@MockBean
private BeanB mockBeanB;

@Test
public void testStuff() {
System.out.println(mockBeanB);
}
}
2 changes: 2 additions & 0 deletions gh-5724/src/test/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.mock.mockito.MockitoScopedProxyContextCustomizerFactory