Use name from @SpyBean to disambiguate multiple candidates

Previously, @SpyBean's name attribute was not used when determining
the name of the bean to spy upon. When there were multiple candidates,
none of which were primary, this would lead to a failure to find the
bean to spy upon. This behaviour is also inconsistent with @MockBean
which does use the name attribute to identify the bean to mock.

This commit updates MockitoPostProcessor to use the name attribute,
when set, to identify the bean that should be spied upon. For
consistency with @MockBean it is always used when set. When not set
the previous logic will continue to be used.

Closes gh-8315
pull/8458/head
Andy Wilkinson 8 years ago
parent 1abd91dc23
commit 70e802fba5

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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.
@ -37,7 +37,6 @@ 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.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@ -311,16 +310,9 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
private void registerSpies(BeanDefinitionRegistry registry, SpyDefinition definition,
Field field, String[] existingBeans) {
ResolvableType type = definition.getTypeToSpy();
try {
if (ObjectUtils.isEmpty(existingBeans)) {
throw new NoSuchBeanDefinitionException(type);
}
if (existingBeans.length > 1) {
existingBeans = new String[] {
determinePrimaryCandidate(registry, existingBeans, type) };
}
registerSpy(definition, field, existingBeans[0]);
registerSpy(definition, field,
determineBeanName(existingBeans, definition, registry));
}
catch (RuntimeException ex) {
throw new IllegalStateException(
@ -328,6 +320,18 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
}
}
private String determineBeanName(String[] existingBeans, SpyDefinition definition,
BeanDefinitionRegistry registry) {
if (StringUtils.hasText(definition.getName())) {
return definition.getName();
}
if (existingBeans.length == 1) {
return existingBeans[0];
}
return determinePrimaryCandidate(registry, existingBeans,
definition.getTypeToSpy());
}
private String determinePrimaryCandidate(BeanDefinitionRegistry registry,
String[] candidateBeanNames, ResolvableType type) {
String primaryBeanName = null;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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.
@ -33,12 +33,13 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
/**
* Test {@link SpyBean} on a test class field can be used to inject new spy instances.
* Test {@link SpyBean} on a test class field can be used to inject a spy instance when
* there are multiple candidates and one is primary.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class SpyBeanOnTestFieldForMultipleExistingBeansIntegrationTests {
public class SpyBeanOnTestFieldForMultipleExistingBeansWithOnePrimaryIntegrationTests {
@SpyBean
private SimpleExampleStringGenericService spy;

@ -0,0 +1,64 @@
/*
* Copyright 2012-2017 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.mockito.internal.util.MockUtil;
import org.springframework.boot.test.mock.mockito.example.SimpleExampleStringGenericService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test {@link SpyBean} on a test class field can be used to inject a spy instance when
* there are multiple candidates and one is chosen using the name attribute.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
public class SpyBeanWithNameOnTestFieldForMultipleExistingBeansTests {
@SpyBean(name = "two")
private SimpleExampleStringGenericService spy;
@Test
public void testSpying() throws Exception {
assertThat(new MockUtil().isSpy(this.spy)).isTrue();
assertThat(new MockUtil().getMockName(this.spy).toString()).isEqualTo("two");
}
@Configuration
static class Config {
@Bean
public SimpleExampleStringGenericService one() {
return new SimpleExampleStringGenericService("one");
}
@Bean
public SimpleExampleStringGenericService two() {
return new SimpleExampleStringGenericService("two");
}
}
}
Loading…
Cancel
Save