Review TCF cache support for bean override
This commit reviews how bean override support can influence the key of an application context cached by the TCF. OverrideMetadata and its subclasses now implement a proper equals/hashCode pair that is tested in various scenarios. Due to how the TCF operates, OverrideMetadata has to be computed in two locations: 1. In a ContextCustomizerFactory, using the metadata in the enclosing class if any. This determines whether a customizer is needed in the first place. The computed set of unique metadata identifies the customizer and participates in the application context cache's key. 2. In the TestExecutionListener so that it knows the override points it has to process. Parsing of the metadata based on a test class has been greatly simplified and moved to OverrideMetadata proper as we don't need several flavors. 1 and 2 are using the same algorithm with the former wrapping that in a Set to compute a proper key. BeanOverrideContextCustomizerEqualityTests provides a framework for testing edge cases as we only care about whether the created ContextCustomizer behaves correctly against the identity of another. Closes gh-32884
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.springframework.test.context.bean.override;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
@@ -203,7 +204,8 @@ class BeanOverrideBeanFactoryPostProcessorTests {
|
||||
|
||||
private AnnotationConfigApplicationContext createContext(Class<?> testClass) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
new BeanOverrideContextCustomizer(Set.of(testClass)).customizeContext(context, mock(MergedContextConfiguration.class));
|
||||
Set<OverrideMetadata> metadata = new LinkedHashSet<>(OverrideMetadata.forTestClass(testClass));
|
||||
new BeanOverrideContextCustomizer(metadata).customizeContext(context, mock(MergedContextConfiguration.class));
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Answers;
|
||||
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.bean.override.convention.TestBean;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Bean override tests that validate the behavior with the TCF context cache.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class BeanOverrideContextCustomizerEqualityTests {
|
||||
|
||||
private final BeanOverrideContextCustomizerFactory factory = new BeanOverrideContextCustomizerFactory();
|
||||
|
||||
@Nested
|
||||
class SameContextTests {
|
||||
|
||||
@Test
|
||||
void testsWithOneIdenticalTestBean() {
|
||||
assertThat(createContextCustomizer(Case1.class)).isEqualTo(createContextCustomizer(Case2.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsWithOneIdenticalMockitoMockBean() {
|
||||
assertThat(createContextCustomizer(Case4.class)).isEqualTo(createContextCustomizer(Case5.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsWithOneIdenticalMockitoSpyBean() {
|
||||
assertThat(createContextCustomizer(Case7.class)).isEqualTo(createContextCustomizer(Case8.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class DifferentContextTests {
|
||||
|
||||
@Test
|
||||
void testsWithSimilarTestBeanButDifferentMethod() {
|
||||
assertThat(createContextCustomizer(Case1.class)).isNotEqualTo(createContextCustomizer(Case3.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsWithSimilarMockitoMockButDifferentAnswers() {
|
||||
assertThat(createContextCustomizer(Case4.class)).isNotEqualTo(createContextCustomizer(Case6.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsWithSimilarMockitoSpyButDifferentProxyTargetClass() {
|
||||
assertThat(createContextCustomizer(Case8.class)).isNotEqualTo(createContextCustomizer(Case9.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsWithSameConfigurationButOneIsMockitoBeanAndTheOtherMockitoSpy() {
|
||||
assertThat(createContextCustomizer(Case4.class)).isNotEqualTo(createContextCustomizer(Case7.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private ContextCustomizer createContextCustomizer(Class<?> testClass) {
|
||||
BeanOverrideContextCustomizer customizer = this.factory.createContextCustomizer(
|
||||
testClass, Collections.emptyList());
|
||||
assertThat(customizer).isNotNull();
|
||||
return customizer;
|
||||
}
|
||||
|
||||
interface DescriptionProvider {
|
||||
|
||||
static String createDescription() {
|
||||
return "override";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Case1 implements DescriptionProvider {
|
||||
|
||||
@TestBean(methodName = "createDescription")
|
||||
private String description;
|
||||
|
||||
}
|
||||
|
||||
static class Case2 implements DescriptionProvider {
|
||||
|
||||
@TestBean(methodName = "createDescription")
|
||||
private String description;
|
||||
|
||||
}
|
||||
|
||||
static class Case3 implements DescriptionProvider {
|
||||
|
||||
@TestBean(methodName = "createDescription")
|
||||
private String description;
|
||||
|
||||
static String createDescription() {
|
||||
return "another value";
|
||||
}
|
||||
}
|
||||
|
||||
static class Case4 {
|
||||
|
||||
@MockitoBean
|
||||
private String exampleService;
|
||||
|
||||
}
|
||||
|
||||
static class Case5 {
|
||||
|
||||
@MockitoBean
|
||||
private String serviceToMock;
|
||||
|
||||
}
|
||||
|
||||
static class Case6 {
|
||||
|
||||
@MockitoBean(answers = Answers.RETURNS_MOCKS)
|
||||
private String exampleService;
|
||||
|
||||
}
|
||||
|
||||
static class Case7 {
|
||||
|
||||
@MockitoSpyBean
|
||||
private String exampleService;
|
||||
|
||||
}
|
||||
|
||||
static class Case8 {
|
||||
|
||||
@MockitoSpyBean
|
||||
private String serviceToMock;
|
||||
|
||||
}
|
||||
|
||||
static class Case9 {
|
||||
|
||||
@MockitoSpyBean(proxyTargetAware = false)
|
||||
private String serviceToMock;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerFactoryTests.Test2.Green;
|
||||
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerFactoryTests.Test2.Orange;
|
||||
import org.springframework.test.context.bean.override.convention.TestBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanOverrideContextCustomizerFactory}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanOverrideContextCustomizerFactoryTests {
|
||||
|
||||
private final BeanOverrideContextCustomizerFactory factory = new BeanOverrideContextCustomizerFactory();
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenTestHasNoBeanOverride() {
|
||||
assertThat(createContextCustomizer(String.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenTestHasSingleBeanOverride() {
|
||||
BeanOverrideContextCustomizer customizer = createContextCustomizer(Test1.class);
|
||||
assertThat(customizer).isNotNull();
|
||||
assertThat(customizer.getMetadata()).singleElement().satisfies(testMetadata(null, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenNestedTestHasSingleBeanOverrideInParent() {
|
||||
BeanOverrideContextCustomizer customizer = createContextCustomizer(Orange.class);
|
||||
assertThat(customizer).isNotNull();
|
||||
assertThat(customizer.getMetadata()).singleElement().satisfies(testMetadata(null, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenNestedTestHasBeanOverrideAsWellAsTheParent() {
|
||||
BeanOverrideContextCustomizer customizer = createContextCustomizer(Green.class);
|
||||
assertThat(customizer).isNotNull();
|
||||
assertThat(customizer.getMetadata())
|
||||
.anySatisfy(testMetadata(null, String.class))
|
||||
.anySatisfy(testMetadata("counterBean", Integer.class))
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenTestHasInvalidTestBeanTargetMethod() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> createContextCustomizer(InvalidTestMissingMethod.class))
|
||||
.withMessageContaining("Failed to find a static test bean factory method");
|
||||
}
|
||||
|
||||
|
||||
private Consumer<OverrideMetadata> testMetadata(@Nullable String beanName, Class<?> beanType) {
|
||||
return overrideMetadata(beanName, beanType, BeanOverrideStrategy.REPLACE_DEFINITION);
|
||||
}
|
||||
|
||||
private Consumer<OverrideMetadata> overrideMetadata(@Nullable String beanName, Class<?> beanType, BeanOverrideStrategy strategy) {
|
||||
return metadata -> {
|
||||
assertThat(metadata.getBeanName()).isEqualTo(beanName);
|
||||
assertThat(metadata.getBeanType().toClass()).isEqualTo(beanType);
|
||||
assertThat(metadata.getStrategy()).isEqualTo(strategy);
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
BeanOverrideContextCustomizer createContextCustomizer(Class<?> testClass) {
|
||||
return this.factory.createContextCustomizer(testClass, Collections.emptyList());
|
||||
}
|
||||
|
||||
static class Test1 {
|
||||
|
||||
@TestBean(methodName = "descriptor")
|
||||
private String descriptor;
|
||||
|
||||
private static String descriptor() {
|
||||
return "Overridden descriptor";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Test2 {
|
||||
|
||||
@TestBean(methodName = "name")
|
||||
private String name;
|
||||
|
||||
private static String name() {
|
||||
return "Overridden name";
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Orange {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Green {
|
||||
|
||||
@TestBean(name = "counterBean", methodName = "counter")
|
||||
private Integer counter;
|
||||
|
||||
private static Integer counter() {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class InvalidTestMissingMethod {
|
||||
|
||||
@TestBean(methodName = "descriptor")
|
||||
private String descriptor;
|
||||
|
||||
private String descriptor() {
|
||||
return "Never called, not static";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanOverrideContextCustomizer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanOverrideContextCustomizerTests {
|
||||
|
||||
@Test
|
||||
void customizerIsEqualWithIdenticalMetadata() {
|
||||
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyOverrideMetadata("key"));
|
||||
BeanOverrideContextCustomizer customizer2 = createCustomizer(new DummyOverrideMetadata("key"));
|
||||
assertThat(customizer).isEqualTo(customizer2);
|
||||
assertThat(customizer).hasSameHashCodeAs(customizer2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizerIsEqualWithIdenticalMetadataInDifferentOrder() {
|
||||
BeanOverrideContextCustomizer customizer = createCustomizer(
|
||||
new DummyOverrideMetadata("key1"), new DummyOverrideMetadata("key2"));
|
||||
BeanOverrideContextCustomizer customizer2 = createCustomizer(
|
||||
new DummyOverrideMetadata("key2"), new DummyOverrideMetadata("key1"));
|
||||
assertThat(customizer).isEqualTo(customizer2);
|
||||
assertThat(customizer).hasSameHashCodeAs(customizer2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizerIsNotEqualWithDifferentMetadata() {
|
||||
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyOverrideMetadata("key"));
|
||||
BeanOverrideContextCustomizer customizer2 = createCustomizer(
|
||||
new DummyOverrideMetadata("key"), new DummyOverrideMetadata("another"));
|
||||
assertThat(customizer).isNotEqualTo(customizer2);
|
||||
}
|
||||
|
||||
private BeanOverrideContextCustomizer createCustomizer(OverrideMetadata... metadata) {
|
||||
return new BeanOverrideContextCustomizer(new LinkedHashSet<>(Arrays.asList(metadata)));
|
||||
}
|
||||
|
||||
private static class DummyOverrideMetadata extends OverrideMetadata {
|
||||
|
||||
private final String key;
|
||||
|
||||
public DummyOverrideMetadata(String key) {
|
||||
super(mock(Field.class), ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE_DEFINITION);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createOverride(String beanName, BeanDefinition existingBeanDefinition,
|
||||
Object existingBeanInstance) {
|
||||
return existingBeanInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DummyOverrideMetadata that = (DummyOverrideMetadata) o;
|
||||
return Objects.equals(this.key, that.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.test.context.bean.override.example.ExampleBeanOverrideAnnotation;
|
||||
import org.springframework.test.context.bean.override.example.TestBeanOverrideMetaAnnotation;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BeanOverrideParsingUtils}.
|
||||
*
|
||||
* @since 6.2
|
||||
*/
|
||||
class BeanOverrideParsingUtilsTests {
|
||||
|
||||
// Metadata built from a String that starts with DUPLICATE_TRIGGER are considered equal
|
||||
private static final String DUPLICATE_TRIGGER1 = ExampleBeanOverrideAnnotation.DUPLICATE_TRIGGER + "-v1";
|
||||
private static final String DUPLICATE_TRIGGER2 = ExampleBeanOverrideAnnotation.DUPLICATE_TRIGGER + "-v2";
|
||||
|
||||
@Test
|
||||
void findsOnField() {
|
||||
assertThat(BeanOverrideParsingUtils.parse(SingleAnnotationOnField.class))
|
||||
.map(Object::toString)
|
||||
.containsExactly("onField");
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsMultipleProcessorsOnDifferentElements() {
|
||||
assertThat(BeanOverrideParsingUtils.parse(AnnotationsOnMultipleFields.class))
|
||||
.map(Object::toString)
|
||||
.containsExactlyInAnyOrder("onField1", "onField2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMultipleAnnotationsOnSameElement() {
|
||||
Field field = ReflectionUtils.findField(MultipleAnnotationsOnField.class, "message");
|
||||
assertThatRuntimeException()
|
||||
.isThrownBy(() -> BeanOverrideParsingUtils.parse(MultipleAnnotationsOnField.class))
|
||||
.withMessage("Multiple @BeanOverride annotations found on field: " + field);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsFirstOccurrenceOfEqualMetadata() {
|
||||
assertThat(BeanOverrideParsingUtils.parse(DuplicateConf.class))
|
||||
.map(Object::toString)
|
||||
.containsExactly("{DUPLICATE-v1}");
|
||||
}
|
||||
|
||||
|
||||
static class SingleAnnotationOnField {
|
||||
|
||||
@ExampleBeanOverrideAnnotation("onField")
|
||||
String message;
|
||||
|
||||
static String onField() {
|
||||
return "OK";
|
||||
}
|
||||
}
|
||||
|
||||
static class MultipleAnnotationsOnField {
|
||||
|
||||
@ExampleBeanOverrideAnnotation("foo")
|
||||
@TestBeanOverrideMetaAnnotation
|
||||
String message;
|
||||
|
||||
static String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
static class AnnotationsOnMultipleFields {
|
||||
|
||||
@ExampleBeanOverrideAnnotation("onField1")
|
||||
String message;
|
||||
|
||||
@ExampleBeanOverrideAnnotation("onField2")
|
||||
String messageOther;
|
||||
|
||||
static String onField1() {
|
||||
return "OK1";
|
||||
}
|
||||
|
||||
static String onField2() {
|
||||
return "OK2";
|
||||
}
|
||||
}
|
||||
|
||||
static class DuplicateConf {
|
||||
|
||||
@ExampleBeanOverrideAnnotation(DUPLICATE_TRIGGER1)
|
||||
String message1;
|
||||
|
||||
@ExampleBeanOverrideAnnotation(DUPLICATE_TRIGGER2)
|
||||
String message2;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,47 +16,266 @@
|
||||
|
||||
package org.springframework.test.context.bean.override;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.bean.override.example.CustomQualifier;
|
||||
import org.springframework.test.context.bean.override.example.ExampleService;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link OverrideMetadata}.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.2
|
||||
*/
|
||||
class OverrideMetadataTests {
|
||||
public class OverrideMetadataTests {
|
||||
|
||||
@Test
|
||||
void implicitConfigurations() throws Exception {
|
||||
OverrideMetadata metadata = exampleOverride();
|
||||
assertThat(metadata.getBeanName()).as("expectedBeanName").isNull();
|
||||
void forTestClassWithSingleField() {
|
||||
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(SingleAnnotation.class);
|
||||
assertThat(overrideMetadata).singleElement().satisfies(hasTestBeanMetadata(
|
||||
field(SingleAnnotation.class, "message"), String.class, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassWithMultipleFields() {
|
||||
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(MultipleAnnotations.class);
|
||||
assertThat(overrideMetadata).hasSize(2)
|
||||
.anySatisfy(hasTestBeanMetadata(
|
||||
field(MultipleAnnotations.class, "message"), String.class, null))
|
||||
.anySatisfy(hasTestBeanMetadata(
|
||||
field(MultipleAnnotations.class, "counter"), Integer.class, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassWithMultipleFieldsSameMetadata() {
|
||||
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(MultipleAnnotationsDuplicate.class);
|
||||
assertThat(overrideMetadata).hasSize(2)
|
||||
.anySatisfy(hasTestBeanMetadata(
|
||||
field(MultipleAnnotationsDuplicate.class, "message1"), String.class, null))
|
||||
.anySatisfy(hasTestBeanMetadata(
|
||||
field(MultipleAnnotationsDuplicate.class, "message2"), String.class, null));
|
||||
assertThat(new HashSet<>(overrideMetadata)).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassWithDifferentOverrideMetadataOnSameField() {
|
||||
Field faultyField = field(MultipleAnnotationsOnSameField.class, "message");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> OverrideMetadata.forTestClass(MultipleAnnotationsOnSameField.class))
|
||||
.withMessageStartingWith("Multiple @BeanOverride annotations found")
|
||||
.withMessageContaining(faultyField.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameIsNullByDefault() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
|
||||
assertThat(metadata.getBeanName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameInstance() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
|
||||
assertThat(metadata).isEqualTo(metadata);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadata() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataAndBeanNames() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataAndDifferentBeaName() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"), "testBean2");
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataButDifferentFields() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "noQualifier"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataAndSameQualifierValues() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "directQualifier"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataAndDifferentQualifierValues() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "differentDirectQualifier"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataAndDifferentQualifiers() {
|
||||
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
|
||||
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "customQualifier"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
private OverrideMetadata createMetadata(Field field) {
|
||||
return createMetadata(field, null);
|
||||
}
|
||||
|
||||
private OverrideMetadata createMetadata(Field field, @Nullable String name) {
|
||||
return new DummyOverrideMetadata(field, field.getType(), name);
|
||||
}
|
||||
|
||||
private Field field(Class<?> target, String fieldName) {
|
||||
Field field = ReflectionUtils.findField(target, fieldName);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
private Consumer<OverrideMetadata> hasTestBeanMetadata(Field field, Class<?> beanType, @Nullable String beanName) {
|
||||
return hasOverrideMetadata(field, beanType, BeanOverrideStrategy.REPLACE_DEFINITION, beanName);
|
||||
}
|
||||
|
||||
private Consumer<OverrideMetadata> hasOverrideMetadata(Field field, Class<?> beanType, BeanOverrideStrategy strategy, @Nullable String beanName) {
|
||||
return metadata -> {
|
||||
assertThat(metadata.getField()).isEqualTo(field);
|
||||
assertThat(metadata.getBeanType().toClass()).isEqualTo(beanType);
|
||||
assertThat(metadata.getStrategy()).isEqualTo(strategy);
|
||||
assertThat(metadata.getBeanName()).isEqualTo(beanName);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
String annotated = "exampleField";
|
||||
static class SingleAnnotation {
|
||||
|
||||
@DummyBean
|
||||
String message;
|
||||
|
||||
private static OverrideMetadata exampleOverride() throws Exception {
|
||||
Field field = OverrideMetadataTests.class.getDeclaredField("annotated");
|
||||
return new ConcreteOverrideMetadata(field, ResolvableType.forClass(String.class),
|
||||
BeanOverrideStrategy.REPLACE_DEFINITION);
|
||||
}
|
||||
|
||||
static class ConcreteOverrideMetadata extends OverrideMetadata {
|
||||
static class MultipleAnnotations {
|
||||
|
||||
ConcreteOverrideMetadata(Field field, ResolvableType typeToOverride,
|
||||
BeanOverrideStrategy strategy) {
|
||||
@DummyBean
|
||||
String message;
|
||||
|
||||
super(field, typeToOverride, null, strategy);
|
||||
@DummyBean
|
||||
Integer counter;
|
||||
}
|
||||
|
||||
static class MultipleAnnotationsDuplicate {
|
||||
|
||||
@DummyBean
|
||||
String message1;
|
||||
|
||||
@DummyBean
|
||||
String message2;
|
||||
|
||||
}
|
||||
|
||||
static class MultipleAnnotationsOnSameField {
|
||||
|
||||
@MetaDummyBean()
|
||||
@DummyBean
|
||||
String message;
|
||||
|
||||
static String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfigA {
|
||||
|
||||
private ExampleService noQualifier;
|
||||
|
||||
@Qualifier("test")
|
||||
private ExampleService directQualifier;
|
||||
|
||||
@Qualifier("different")
|
||||
private ExampleService differentDirectQualifier;
|
||||
|
||||
@CustomQualifier
|
||||
private ExampleService customQualifier;
|
||||
|
||||
}
|
||||
|
||||
public static class ConfigB {
|
||||
|
||||
private ExampleService noQualifier;
|
||||
|
||||
@Qualifier("test")
|
||||
private ExampleService directQualifier;
|
||||
|
||||
}
|
||||
|
||||
// Simple OverrideMetadata implementation
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@DummyBean
|
||||
public @interface MetaDummyBean {}
|
||||
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@BeanOverride(DummyBeanOverrideProcessor.class)
|
||||
public @interface DummyBean {}
|
||||
|
||||
static class DummyBeanOverrideProcessor implements BeanOverrideProcessor {
|
||||
|
||||
@Override
|
||||
public OverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
|
||||
return new DummyOverrideMetadata(field, field.getType(), null);
|
||||
}
|
||||
}
|
||||
|
||||
static class DummyOverrideMetadata extends OverrideMetadata {
|
||||
|
||||
@Nullable
|
||||
private final String beanName;
|
||||
|
||||
DummyOverrideMetadata(Field field, Class<?> typeToOverride, @Nullable String beanName) {
|
||||
super(field, ResolvableType.forClass(typeToOverride), beanName, BeanOverrideStrategy.REPLACE_DEFINITION);
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.test.context.junit.EngineTestKitUtils;
|
||||
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
|
||||
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
|
||||
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
|
||||
import static org.springframework.test.context.junit.EngineTestKitUtils.rootCause;
|
||||
|
||||
/**
|
||||
* {@link TestBean @TestBean} inheritance integration tests for failure scenarios.
|
||||
@@ -40,12 +39,11 @@ class FailingTestBeanInheritanceIntegrationTests {
|
||||
Class<?> testClass = FieldInSupertypeButNoMethodTestCase.class;
|
||||
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
|
||||
finishedWithFailure(
|
||||
rootCause(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message("""
|
||||
Failed to find a static test bean factory method in %s with return type %s \
|
||||
whose name matches one of the supported candidates [someBeanTestOverride]"""
|
||||
.formatted(testClass.getName(), AbstractTestBeanIntegrationTestCase.Pojo.class.getName())))));
|
||||
.formatted(testClass.getName(), AbstractTestBeanIntegrationTestCase.Pojo.class.getName()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,13 +51,12 @@ class FailingTestBeanInheritanceIntegrationTests {
|
||||
Class<?> testClass = Method1InSupertypeAndMethod2InTypeTestCase.class;
|
||||
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
|
||||
finishedWithFailure(
|
||||
rootCause(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message("""
|
||||
Found 2 competing static test bean factory methods in %s with return type %s \
|
||||
whose name matches one of the supported candidates \
|
||||
[anotherBeanTestOverride, thirdBeanTestOverride]"""
|
||||
.formatted(testClass.getName(), AbstractTestBeanIntegrationTestCase.Pojo.class.getName())))));
|
||||
.formatted(testClass.getName(), AbstractTestBeanIntegrationTestCase.Pojo.class.getName()))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -69,25 +69,22 @@ class FailingTestBeanIntegrationTests {
|
||||
Class<?> testClass = ExplicitTestOverrideMethodNotPresentTestCase.class;
|
||||
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
|
||||
finishedWithFailure(
|
||||
rootCause(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message("""
|
||||
Failed to find a static test bean factory method in %s with return type \
|
||||
java.lang.String whose name matches one of the supported candidates \
|
||||
[notPresent]""".formatted(testClass.getName())))));
|
||||
[notPresent]""".formatted(testClass.getName()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanFailingNoExplicitMethod() {
|
||||
Class<?> testClass = ImplicitTestOverrideMethodNotPresentTestCase.class;
|
||||
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
|
||||
finishedWithFailure(
|
||||
rootCause(
|
||||
instanceOf(IllegalStateException.class),
|
||||
finishedWithFailure(instanceOf(IllegalStateException.class),
|
||||
message("""
|
||||
Failed to find a static test bean factory method in %s with return type \
|
||||
java.lang.String whose name matches one of the supported candidates \
|
||||
[fieldTestOverride]""".formatted(testClass.getName())))));
|
||||
[fieldTestOverride]""".formatted(testClass.getName()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override.convention;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestBeanOverrideMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TestBeanOverrideMetadataTests {
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneOverride.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToAnnotationName() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneOverrideWithName.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassWithMissingMethod() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() ->OverrideMetadata.forTestClass(SampleMissingMethod.class))
|
||||
.withMessageStartingWith("Failed to find a static test bean factory method")
|
||||
.withMessageContaining("messageTestOverride");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameInstance() {
|
||||
TestBeanOverrideMetadata metadata = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
assertThat(metadata).isEqualTo(metadata);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadata() {
|
||||
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
assertThat(metadata1).isEqualTo(metadata2);
|
||||
assertThat(metadata1).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataButDifferentField() {
|
||||
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message2"), sampleMethod("message"));
|
||||
assertThat(metadata1).isEqualTo(metadata2);
|
||||
assertThat(metadata1).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentBeanName() {
|
||||
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message3"), sampleMethod("message"));
|
||||
assertThat(metadata1).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentMethod() {
|
||||
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message"), sampleMethod("description"));
|
||||
assertThat(metadata1).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentAnnotations() {
|
||||
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
|
||||
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message4"), sampleMethod("message"));
|
||||
assertThat(metadata1).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
private Field sampleField(String fieldName) {
|
||||
Field field = ReflectionUtils.findField(Sample.class, fieldName);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
private Method sampleMethod(String noArgMethodName) {
|
||||
Method method = ReflectionUtils.findMethod(Sample.class, noArgMethodName);
|
||||
assertThat(method).isNotNull();
|
||||
return method;
|
||||
}
|
||||
|
||||
private TestBeanOverrideMetadata createMetadata(Field field, Method overrideMethod) {
|
||||
TestBean annotation = field.getAnnotation(TestBean.class);
|
||||
String beanName = (StringUtils.hasText(annotation.value()) ? annotation.value() : null);
|
||||
return new TestBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), beanName, overrideMethod);
|
||||
}
|
||||
|
||||
static class SampleOneOverride {
|
||||
|
||||
@TestBean(methodName = "message")
|
||||
String message;
|
||||
|
||||
static String message() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SampleOneOverrideWithName {
|
||||
|
||||
@TestBean(name = "anotherBean", methodName = "message")
|
||||
String message;
|
||||
|
||||
static String message() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SampleMissingMethod {
|
||||
|
||||
@TestBean
|
||||
String message;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class Sample {
|
||||
|
||||
@TestBean
|
||||
private String message;
|
||||
|
||||
@TestBean
|
||||
private String message2;
|
||||
|
||||
@TestBean(name = "anotherBean")
|
||||
private String message3;
|
||||
|
||||
@Qualifier("anotherBean")
|
||||
@TestBean
|
||||
private String message4;
|
||||
|
||||
static String message() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
static String description() {
|
||||
return message();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.springframework.test.context.bean.override.mockito;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -30,7 +31,7 @@ import org.springframework.test.context.bean.override.example.RealExampleService
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatException;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.BDDMockito.when;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -80,9 +81,9 @@ public class MockitoBeanByTypeIntegrationTests {
|
||||
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
|
||||
.isSameAs(ctx.getBean("ambiguous2"));
|
||||
|
||||
assertThatException()
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> ctx.getBean(StringBuilder.class))
|
||||
.withMessageEndingWith("but found 2: ambiguous2,ambiguous1");
|
||||
.satisfies(ex -> assertThat(ex.getBeanNamesFound()).containsOnly("ambiguous1", "ambiguous2"));
|
||||
|
||||
assertThat(this.ambiguous).isEmpty();
|
||||
assertThat(this.ambiguous.substring(0)).isNull();
|
||||
@@ -97,9 +98,9 @@ public class MockitoBeanByTypeIntegrationTests {
|
||||
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
|
||||
.isSameAs(ctx.getBean("ambiguous1"));
|
||||
|
||||
assertThatException()
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> ctx.getBean(StringBuilder.class))
|
||||
.withMessageEndingWith("but found 2: ambiguous2,ambiguous1");
|
||||
.satisfies(ex -> assertThat(ex.getBeanNamesFound()).containsOnly("ambiguous1", "ambiguous2"));
|
||||
|
||||
assertThat(this.ambiguousMeta).isEmpty();
|
||||
assertThat(this.ambiguousMeta.substring(0)).isNull();
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override.mockito;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Answers;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link MockitoBeanOverrideMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MockitoBeanOverrideMetadataTests {
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneMock.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToAnnotationName() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneMockWithName.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameInstance() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
assertThat(metadata).isEqualTo(metadata);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadata() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataButDifferentField() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service2"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentBeanName() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service3"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentExtraInterfaces() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service4"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentAnswers() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service5"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentSerializableFlag() {
|
||||
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service6"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
|
||||
private Field sampleField(String fieldName) {
|
||||
Field field = ReflectionUtils.findField(Sample.class, fieldName);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
private MockitoBeanOverrideMetadata createMetadata(Field field) {
|
||||
MockitoBean annotation = field.getAnnotation(MockitoBean.class);
|
||||
return new MockitoBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
|
||||
}
|
||||
|
||||
|
||||
static class SampleOneMock {
|
||||
|
||||
@MockitoBean
|
||||
String service;
|
||||
|
||||
}
|
||||
|
||||
static class SampleOneMockWithName {
|
||||
|
||||
@MockitoBean(name = "anotherService")
|
||||
String service;
|
||||
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
@MockitoBean
|
||||
private String service;
|
||||
|
||||
@MockitoBean
|
||||
private String service2;
|
||||
|
||||
@MockitoBean(name = "beanToMock")
|
||||
private String service3;
|
||||
|
||||
@MockitoBean(extraInterfaces = Externalizable.class)
|
||||
private String service4;
|
||||
|
||||
@MockitoBean(answers = Answers.RETURNS_MOCKS)
|
||||
private String service5;
|
||||
|
||||
@MockitoBean(serializable = true)
|
||||
private String service6;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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
|
||||
*
|
||||
* https://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.test.context.bean.override.mockito;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link MockitoSpyBeanOverrideMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MockitoSpyBeanOverrideMetadataTests {
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneSpy.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTestClassSetsNameToAnnotationName() {
|
||||
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneSpyWithName.class);
|
||||
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameInstance() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
assertThat(metadata).isEqualTo(metadata);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadata() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithSameMetadataButDifferentField() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service2"));
|
||||
assertThat(metadata).isEqualTo(metadata2);
|
||||
assertThat(metadata).hasSameHashCodeAs(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentBeanName() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service3"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentReset() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service4"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWithSameMetadataButDifferentProxyTargetAwareFlag() {
|
||||
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
|
||||
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service5"));
|
||||
assertThat(metadata).isNotEqualTo(metadata2);
|
||||
}
|
||||
|
||||
|
||||
private Field sampleField(String fieldName) {
|
||||
Field field = ReflectionUtils.findField(Sample.class, fieldName);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
private MockitoSpyBeanOverrideMetadata createMetadata(Field field) {
|
||||
MockitoSpyBean annotation = field.getAnnotation(MockitoSpyBean.class);
|
||||
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
|
||||
}
|
||||
|
||||
|
||||
static class SampleOneSpy {
|
||||
|
||||
@MockitoSpyBean
|
||||
String service;
|
||||
|
||||
}
|
||||
|
||||
static class SampleOneSpyWithName {
|
||||
|
||||
@MockitoSpyBean(name = "anotherService")
|
||||
String service;
|
||||
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
@MockitoSpyBean
|
||||
private String service;
|
||||
|
||||
@MockitoSpyBean
|
||||
private String service2;
|
||||
|
||||
@MockitoSpyBean(name = "beanToMock")
|
||||
private String service3;
|
||||
|
||||
@MockitoSpyBean(reset = MockReset.BEFORE)
|
||||
private String service4;
|
||||
|
||||
@MockitoSpyBean(proxyTargetAware = false)
|
||||
private String service5;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user