Filter scoped target proxy beans from Mockito

Update MockitoPostProcessor to filter bean names that match
`ScopedProxyUtils.isScopedTarget` from the candidates list.

Fixes gh-5724
pull/5980/head
Phillip Webb 9 years ago
parent 682f20ebf7
commit f0b6d346d7

@ -18,14 +18,18 @@ package org.springframework.boot.test.mock.mockito;
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;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
@ -208,8 +212,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
if (StringUtils.hasLength(mockDefinition.getName())) {
return mockDefinition.getName();
}
String[] existingBeans = beanFactory
.getBeanNamesForType(mockDefinition.getClassToMock());
String[] existingBeans = getExistingBeans(beanFactory,
mockDefinition.getClassToMock());
if (ObjectUtils.isEmpty(existingBeans)) {
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
}
@ -224,8 +228,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
private void registerSpy(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry, SpyDefinition definition, Field field) {
String[] existingBeans = beanFactory
.getBeanNamesForType(definition.getClassToSpy());
String[] existingBeans = getExistingBeans(beanFactory,
definition.getClassToSpy());
if (ObjectUtils.isEmpty(existingBeans)) {
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,
Field field) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(
@ -309,7 +334,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
Assert.state(ReflectionUtils.getField(field, target) == null,
"The field " + field + " cannot have an existing value");
Object bean = this.beanFactory.getBean(beanName, field.getType());
if (definition.isProxyTargetAware() && AopUtils.isAopProxy(bean)) {
if (definition.isProxyTargetAware() && isAopProxy(bean)) {
MockitoAopProxyTargetInterceptor.applyTo(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
public int getOrder() {
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…
Cancel
Save