Filter scoped target proxy beans from Mockito
Update MockitoPostProcessor to filter bean names that match `ScopedProxyUtils.isScopedTarget` from the candidates list. Fixes gh-5724
This commit is contained in:
parent
682f20ebf7
commit
f0b6d346d7
|
|
@ -18,14 +18,18 @@ package org.springframework.boot.test.mock.mockito;
|
||||||
|
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.PropertyValues;
|
import org.springframework.beans.PropertyValues;
|
||||||
|
|
@ -208,8 +212,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
|
||||||
if (StringUtils.hasLength(mockDefinition.getName())) {
|
if (StringUtils.hasLength(mockDefinition.getName())) {
|
||||||
return mockDefinition.getName();
|
return mockDefinition.getName();
|
||||||
}
|
}
|
||||||
String[] existingBeans = beanFactory
|
String[] existingBeans = getExistingBeans(beanFactory,
|
||||||
.getBeanNamesForType(mockDefinition.getClassToMock());
|
mockDefinition.getClassToMock());
|
||||||
if (ObjectUtils.isEmpty(existingBeans)) {
|
if (ObjectUtils.isEmpty(existingBeans)) {
|
||||||
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
|
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
|
||||||
}
|
}
|
||||||
|
|
@ -224,8 +228,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
|
||||||
|
|
||||||
private void registerSpy(ConfigurableListableBeanFactory beanFactory,
|
private void registerSpy(ConfigurableListableBeanFactory beanFactory,
|
||||||
BeanDefinitionRegistry registry, SpyDefinition definition, Field field) {
|
BeanDefinitionRegistry registry, SpyDefinition definition, Field field) {
|
||||||
String[] existingBeans = beanFactory
|
String[] existingBeans = getExistingBeans(beanFactory,
|
||||||
.getBeanNamesForType(definition.getClassToSpy());
|
definition.getClassToSpy());
|
||||||
if (ObjectUtils.isEmpty(existingBeans)) {
|
if (ObjectUtils.isEmpty(existingBeans)) {
|
||||||
createSpy(registry, definition, field);
|
createSpy(registry, definition, field);
|
||||||
}
|
}
|
||||||
|
|
@ -234,6 +238,27 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory,
|
||||||
|
Class<?> type) {
|
||||||
|
List<String> beans = new ArrayList<String>(
|
||||||
|
Arrays.asList(beanFactory.getBeanNamesForType(type)));
|
||||||
|
for (Iterator<String> iterator = beans.iterator(); iterator.hasNext();) {
|
||||||
|
if (isScopedTarget(iterator.next())) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return beans.toArray(new String[beans.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScopedTarget(String beanName) {
|
||||||
|
try {
|
||||||
|
return ScopedProxyUtils.isScopedTarget(beanName);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createSpy(BeanDefinitionRegistry registry, SpyDefinition definition,
|
private void createSpy(BeanDefinitionRegistry registry, SpyDefinition definition,
|
||||||
Field field) {
|
Field field) {
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
|
|
@ -309,7 +334,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
|
||||||
Assert.state(ReflectionUtils.getField(field, target) == null,
|
Assert.state(ReflectionUtils.getField(field, target) == null,
|
||||||
"The field " + field + " cannot have an existing value");
|
"The field " + field + " cannot have an existing value");
|
||||||
Object bean = this.beanFactory.getBean(beanName, field.getType());
|
Object bean = this.beanFactory.getBean(beanName, field.getType());
|
||||||
if (definition.isProxyTargetAware() && AopUtils.isAopProxy(bean)) {
|
if (definition.isProxyTargetAware() && isAopProxy(bean)) {
|
||||||
MockitoAopProxyTargetInterceptor.applyTo(bean);
|
MockitoAopProxyTargetInterceptor.applyTo(bean);
|
||||||
}
|
}
|
||||||
ReflectionUtils.setField(field, target, bean);
|
ReflectionUtils.setField(field, target, bean);
|
||||||
|
|
@ -319,6 +344,15 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAopProxy(Object object) {
|
||||||
|
try {
|
||||||
|
return AopUtils.isAopProxy(object);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return Ordered.LOWEST_PRECEDENCE - 10;
|
return Ordered.LOWEST_PRECEDENCE - 10;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.test.mock.mockito;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.mock.mockito.example.ExampleService;
|
||||||
|
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
|
||||||
|
import org.springframework.boot.test.mock.mockito.example.FailingExampleService;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test {@link MockBean} when used in combination with scoped proxy targets.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @see <a href="https://github.com/spring-projects/spring-boot/issues/5724">gh-5724</a>
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
public class MockBeanOnScopedProxyTests {
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private ExampleService exampleService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExampleServiceCaller caller;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMocking() throws Exception {
|
||||||
|
given(this.caller.getService().greeting()).willReturn("Boot");
|
||||||
|
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ ExampleServiceCaller.class })
|
||||||
|
static class Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
|
public ExampleService exampleService() {
|
||||||
|
return new FailingExampleService();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue