Make Mock/Spy qualifiers part of context cache key

Refine @MockBean/@SpyBean qualifier support so that qualifiers form part
of the context cache key. Prior to this commit is was possible that two
different tests could accidentally share the same context if they
defined the same @Mock but with different @Qualifiers.

See gh-6753
This commit is contained in:
Phillip Webb
2016-09-17 18:31:19 -07:00
parent 04448d6bd9
commit aad40093ff
11 changed files with 379 additions and 91 deletions

View File

@@ -24,6 +24,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Answers;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface;
import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
@@ -67,7 +68,6 @@ public class DefinitionsParserTests {
this.parser.parse(MockBeanAttributes.class);
assertThat(getDefinitions()).hasSize(1);
MockDefinition definition = getMockDefinition(0);
assertThat(definition.getElement()).isEqualTo(MockBeanAttributes.class);
assertThat(definition.getName()).isEqualTo("Name");
assertThat(definition.getTypeToMock().resolve()).isEqualTo(ExampleService.class);
assertThat(definition.getExtraInterfaces())
@@ -75,6 +75,7 @@ public class DefinitionsParserTests {
assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS);
assertThat(definition.isSerializable()).isEqualTo(true);
assertThat(definition.getReset()).isEqualTo(MockReset.NONE);
assertThat(definition.getQualifier()).isNull();
}
@Test
@@ -82,14 +83,15 @@ public class DefinitionsParserTests {
this.parser.parse(MockBeanOnClassAndField.class);
assertThat(getDefinitions()).hasSize(2);
MockDefinition classDefinition = getMockDefinition(0);
assertThat(classDefinition.getElement()).isEqualTo(MockBeanOnClassAndField.class);
assertThat(classDefinition.getTypeToMock().resolve())
.isEqualTo(ExampleService.class);
assertThat(classDefinition.getQualifier()).isNull();
MockDefinition fieldDefinition = getMockDefinition(1);
assertThat(fieldDefinition.getElement()).isEqualTo(
ReflectionUtils.findField(MockBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getTypeToMock().resolve())
.isEqualTo(ExampleServiceCaller.class);
QualifierDefinition qualifier = QualifierDefinition.forElement(
ReflectionUtils.findField(MockBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getQualifier()).isNotNull().isEqualTo(qualifier);
}
@Test
@@ -148,11 +150,11 @@ public class DefinitionsParserTests {
this.parser.parse(SpyBeanAttributes.class);
assertThat(getDefinitions()).hasSize(1);
SpyDefinition definition = getSpyDefinition(0);
assertThat(definition.getElement()).isEqualTo(SpyBeanAttributes.class);
assertThat(definition.getName()).isEqualTo("Name");
assertThat(definition.getTypeToSpy().resolve())
.isEqualTo(RealExampleService.class);
assertThat(definition.getReset()).isEqualTo(MockReset.NONE);
assertThat(definition.getQualifier()).isNull();
}
@Test
@@ -160,12 +162,13 @@ public class DefinitionsParserTests {
this.parser.parse(SpyBeanOnClassAndField.class);
assertThat(getDefinitions()).hasSize(2);
SpyDefinition classDefinition = getSpyDefinition(0);
assertThat(classDefinition.getElement()).isEqualTo(SpyBeanOnClassAndField.class);
assertThat(classDefinition.getQualifier()).isNull();
assertThat(classDefinition.getTypeToSpy().resolve())
.isEqualTo(RealExampleService.class);
SpyDefinition fieldDefinition = getSpyDefinition(1);
assertThat(fieldDefinition.getElement()).isEqualTo(
QualifierDefinition qualifier = QualifierDefinition.forElement(
ReflectionUtils.findField(SpyBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getQualifier()).isNotNull().isEqualTo(qualifier);
}
@Test
@@ -232,6 +235,7 @@ public class DefinitionsParserTests {
static class MockBeanOnClassAndField {
@MockBean(ExampleServiceCaller.class)
@Qualifier("test")
private Object caller;
}
@@ -279,6 +283,7 @@ public class DefinitionsParserTests {
static class SpyBeanOnClassAndField {
@SpyBean(ExampleServiceCaller.class)
@Qualifier("test")
private Object caller;
}

View File

@@ -28,6 +28,7 @@ import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.core.ResolvableType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MockDefinition}.
@@ -46,28 +47,28 @@ public class MockDefinitionTests {
public void classToMockMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("TypeToMock must not be null");
new MockDefinition(null, null, null, null, null, false, null);
new MockDefinition(null, null, null, null, false, null, null);
}
@Test
public void createWithDefaults() throws Exception {
MockDefinition definition = new MockDefinition(null, null, EXAMPLE_SERVICE_TYPE,
null, null, false, null);
assertThat(definition.getElement()).isNull();
MockDefinition definition = new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null,
null, false, null, null);
assertThat(definition.getName()).isNull();
assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
assertThat(definition.getExtraInterfaces()).isEmpty();
assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS);
assertThat(definition.isSerializable()).isFalse();
assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
assertThat(definition.getQualifier()).isNull();
}
@Test
public void createExplicit() throws Exception {
MockDefinition definition = new MockDefinition(getClass(), "name",
EXAMPLE_SERVICE_TYPE, new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE);
assertThat(definition.getElement()).isEqualTo(getClass());
QualifierDefinition qualifier = mock(QualifierDefinition.class);
MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE,
new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, qualifier);
assertThat(definition.getName()).isEqualTo("name");
assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
assertThat(definition.getExtraInterfaces())
@@ -76,13 +77,14 @@ public class MockDefinitionTests {
assertThat(definition.isSerializable()).isTrue();
assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
assertThat(definition.isProxyTargetAware()).isFalse();
assertThat(definition.getQualifier()).isEqualTo(qualifier);
}
@Test
public void createMock() throws Exception {
MockDefinition definition = new MockDefinition(null, "name", EXAMPLE_SERVICE_TYPE,
MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE,
new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE);
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, null);
ExampleService mock = definition.createMock();
MockCreationSettings<?> settings = new MockUtil().getMockSettings(mock);
assertThat(mock).isInstanceOf(ExampleService.class);

View File

@@ -53,8 +53,8 @@ public class MockitoContextCustomizerTests {
}
private MockDefinition createTestMockDefinition(Class<?> typeToMock) {
return new MockDefinition(null, null, ResolvableType.forClass(typeToMock), null,
null, false, null);
return new MockDefinition(null, ResolvableType.forClass(typeToMock), null, null,
false, null, null);
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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 java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link QualifierDefinition}.
*
* @author Phillip Webb
*/
public class QualifierDefinitionTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Mock
private ConfigurableListableBeanFactory beanFactory;
@Captor
private ArgumentCaptor<DependencyDescriptor> descriptorCaptor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void forElementFieldIsNullShouldReturnNull() throws Exception {
assertThat(QualifierDefinition.forElement((Field) null)).isNull();
}
@Test
public void forElementWhenElementIsNotFieldShouldReturnNull() throws Exception {
assertThat(QualifierDefinition.forElement(getClass())).isNull();
}
@Test
public void forElementWhenElementIsFieldWithNoQualifiersShouldReturnNull()
throws Exception {
QualifierDefinition definition = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "noQualifier"));
assertThat(definition).isNull();
}
@Test
public void forElementWhenElementIsFieldWithQualifierShouldReturnDefinition()
throws Exception {
QualifierDefinition definition = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "directQualifier"));
assertThat(definition).isNotNull();
}
@Test
public void matchesShouldCallBeanFactory() throws Exception {
Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier");
QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field);
qualifierDefinition.matches(this.beanFactory, "bean");
verify(this.beanFactory).isAutowireCandidate(eq("bean"),
this.descriptorCaptor.capture());
assertThat(this.descriptorCaptor.getValue().getAnnotatedElement())
.isEqualTo(field);
}
@Test
public void applyToShouldSetQualifierElement() throws Exception {
Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier");
QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field);
RootBeanDefinition definition = new RootBeanDefinition();
qualifierDefinition.applyTo(definition);
assertThat(definition.getQualifiedElement()).isEqualTo(field);
}
@Test
public void hashCodeAndEqualsShouldWorkOnDifferentClasses() throws Exception {
QualifierDefinition directQualifier1 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "directQualifier"));
QualifierDefinition directQualifier2 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigB.class, "directQualifier"));
QualifierDefinition differentDirectQualifier1 = QualifierDefinition.forElement(
ReflectionUtils.findField(ConfigA.class, "differentDirectQualifier"));
QualifierDefinition differentDirectQualifier2 = QualifierDefinition.forElement(
ReflectionUtils.findField(ConfigB.class, "differentDirectQualifier"));
QualifierDefinition customQualifier1 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "customQualifier"));
QualifierDefinition customQualifier2 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigB.class, "customQualifier"));
assertThat(directQualifier1.hashCode()).isEqualTo(directQualifier2.hashCode());
assertThat(differentDirectQualifier1.hashCode())
.isEqualTo(differentDirectQualifier2.hashCode());
assertThat(customQualifier1.hashCode()).isEqualTo(customQualifier2.hashCode());
assertThat(differentDirectQualifier1).isEqualTo(differentDirectQualifier1)
.isEqualTo(differentDirectQualifier2).isNotEqualTo(directQualifier2);
assertThat(directQualifier1).isEqualTo(directQualifier1)
.isEqualTo(directQualifier2).isNotEqualTo(differentDirectQualifier1);
assertThat(customQualifier1).isEqualTo(customQualifier1)
.isEqualTo(customQualifier2).isNotEqualTo(differentDirectQualifier1);
}
@Configuration
static class ConfigA {
@MockBean
private Object noQualifier;
@MockBean
@Qualifier("test")
private Object directQualifier;
@MockBean
@Qualifier("different")
private Object differentDirectQualifier;
@MockBean
@CustomQualifier
private Object customQualifier;
}
static class ConfigB {
@MockBean
@Qualifier("test")
private Object directQualifier;
@MockBean
@Qualifier("different")
private Object differentDirectQualifier;
@MockBean
@CustomQualifier
private Object customQualifier;
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomQualifier {
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.boot.test.mock.mockito.example.RealExampleService;
import org.springframework.core.ResolvableType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link SpyDefinition}.
@@ -47,35 +48,36 @@ public class SpyDefinitionTests {
public void classToSpyMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("TypeToSpy must not be null");
new SpyDefinition(null, null, null, null, true);
new SpyDefinition(null, null, null, true, null);
}
@Test
public void createWithDefaults() throws Exception {
SpyDefinition definition = new SpyDefinition(null, null, REAL_SERVICE_TYPE, null,
true);
assertThat(definition.getElement()).isNull();
SpyDefinition definition = new SpyDefinition(null, REAL_SERVICE_TYPE, null, true,
null);
assertThat(definition.getName()).isNull();
assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE);
assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
assertThat(definition.isProxyTargetAware()).isTrue();
assertThat(definition.getQualifier()).isNull();
}
@Test
public void createExplicit() throws Exception {
SpyDefinition definition = new SpyDefinition(getClass(), "name",
REAL_SERVICE_TYPE, MockReset.BEFORE, false);
assertThat(definition.getElement()).isEqualTo(getClass());
QualifierDefinition qualifier = mock(QualifierDefinition.class);
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, false, qualifier);
assertThat(definition.getName()).isEqualTo("name");
assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE);
assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
assertThat(definition.isProxyTargetAware()).isFalse();
assertThat(definition.getQualifier()).isEqualTo(qualifier);
}
@Test
public void createSpy() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true);
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true, null);
RealExampleService spy = definition.createSpy(new RealExampleService("hello"));
MockCreationSettings<?> settings = new MockUtil().getMockSettings(spy);
assertThat(spy).isInstanceOf(ExampleService.class);
@@ -87,8 +89,8 @@ public class SpyDefinitionTests {
@Test
public void createSpyWhenNullInstanceShouldThrowException() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true);
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true, null);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Instance must not be null");
definition.createSpy(null);
@@ -96,8 +98,8 @@ public class SpyDefinitionTests {
@Test
public void createSpyWhenWrongInstanceShouldThrowException() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true);
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true, null);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("must be an instance of");
definition.createSpy(new ExampleServiceCaller(null));
@@ -105,8 +107,8 @@ public class SpyDefinitionTests {
@Test
public void createSpyTwice() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true);
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true, null);
Object instance = new RealExampleService("hello");
instance = definition.createSpy(instance);
instance = definition.createSpy(instance);