Merge branch '6.2.x'

This commit is contained in:
Sam Brannen 2024-12-09 16:01:07 +01:00
commit cd25a64ac0
6 changed files with 141 additions and 183 deletions

View File

@ -24,6 +24,7 @@ import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.util.Assert;
/**
* {@link ContextCustomizerFactory} implementation that provides support for
@ -51,10 +52,13 @@ class BeanOverrideContextCustomizerFactory implements ContextCustomizerFactory {
}
private void findBeanOverrideHandler(Class<?> testClass, Set<BeanOverrideHandler> handlers) {
handlers.addAll(BeanOverrideHandler.forTestClass(testClass));
if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) {
findBeanOverrideHandler(testClass.getEnclosingClass(), handlers);
}
BeanOverrideHandler.forTestClass(testClass).forEach(handler ->
Assert.state(handlers.add(handler), () ->
"Duplicate BeanOverrideHandler discovered in test class %s: %s"
.formatted(testClass.getName(), handler)));
}
}

View File

@ -19,18 +19,20 @@ 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.DummyBean.DummyBeanOverrideProcessor.DummyBeanOverrideHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link BeanOverrideContextCustomizerFactory}.
*
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
class BeanOverrideContextCustomizerFactoryTests {
@ -65,6 +67,15 @@ class BeanOverrideContextCustomizerFactoryTests {
.hasSize(2);
}
@Test // gh-34054
void failsWithDuplicateBeanOverrides() {
Class<?> testClass = DuplicateOverridesTestCase.class;
assertThatIllegalStateException()
.isThrownBy(() -> createContextCustomizer(testClass))
.withMessageStartingWith("Duplicate BeanOverrideHandler discovered in test class " + testClass.getName())
.withMessageContaining("DummyBeanOverrideHandler");
}
private Consumer<BeanOverrideHandler> dummyHandler(@Nullable String beanName, Class<?> beanType) {
return dummyHandler(beanName, beanType, BeanOverrideStrategy.REPLACE);
@ -80,15 +91,15 @@ class BeanOverrideContextCustomizerFactoryTests {
}
@Nullable
BeanOverrideContextCustomizer createContextCustomizer(Class<?> testClass) {
private BeanOverrideContextCustomizer createContextCustomizer(Class<?> testClass) {
return this.factory.createContextCustomizer(testClass, Collections.emptyList());
}
static class Test1 {
@DummyBean
private String descriptor;
}
static class Test2 {
@ -96,17 +107,25 @@ class BeanOverrideContextCustomizerFactoryTests {
@DummyBean
private String name;
@Nested
// @Nested
class Orange {
}
@Nested
// @Nested
class Green {
@DummyBean(beanName = "counterBean")
private Integer counter;
}
}
static class DuplicateOverridesTestCase {
@DummyBean(beanName = "text")
String text1;
@DummyBean(beanName = "text")
String text2;
}
}

View File

@ -40,21 +40,9 @@ public class TestBeanForByNameLookupIntegrationTests {
@TestBean(name = "field")
String field;
@TestBean(name = "nestedField")
String nestedField;
@TestBean(name = "field")
String renamed1;
@TestBean(name = "nestedField")
String renamed2;
@TestBean(name = "methodRenamed1", methodName = "field")
String methodRenamed1;
@TestBean(name = "methodRenamed2", methodName = "nestedField")
String methodRenamed2;
static String field() {
return "fieldOverride";
}
@ -66,62 +54,75 @@ public class TestBeanForByNameLookupIntegrationTests {
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(this.field).as("injection point").isEqualTo("fieldOverride");
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(this.renamed1).as("injection point").isEqualTo("fieldOverride");
assertThat(field).as("injection point").isEqualTo("fieldOverride");
}
@Test
void fieldWithMethodNameHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("methodRenamed1")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(this.methodRenamed1).as("injection point").isEqualTo("fieldOverride");
assertThat(methodRenamed1).as("injection point").isEqualTo("fieldOverride");
}
@Nested
@DisplayName("With @TestBean in enclosing class")
@DisplayName("With @TestBean in enclosing class and in @Nested class")
public class TestBeanFieldInEnclosingClassTests {
@TestBean(name = "nestedField")
String nestedField;
@TestBean(name = "methodRenamed2", methodName = "nestedField")
String methodRenamed2;
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(field).as("injection point").isEqualTo("fieldOverride");
}
@Test
void fieldWithMethodNameHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("methodRenamed1")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(methodRenamed1).as("injection point").isEqualTo("fieldOverride");
}
@Test
void nestedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(nestedField).isEqualTo("nestedFieldOverride");
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(renamed2).isEqualTo("nestedFieldOverride");
}
@Test
void fieldWithMethodNameHasOverride(ApplicationContext ctx) {
void nestedFieldWithMethodNameHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("methodRenamed2")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(methodRenamed2).isEqualTo("nestedFieldOverride");
}
@Nested
@DisplayName("With @TestBean in the enclosing class of the enclosing class")
@DisplayName("With @TestBean in the enclosing classes")
public class TestBeanFieldInEnclosingClassLevel2Tests {
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(field).as("injection point").isEqualTo("fieldOverride");
}
@Test
void fieldWithMethodNameHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("methodRenamed1")).as("applicationContext").isEqualTo("fieldOverride");
assertThat(methodRenamed1).as("injection point").isEqualTo("fieldOverride");
}
@Test
void nestedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(nestedField).isEqualTo("nestedFieldOverride");
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(renamed2).isEqualTo("nestedFieldOverride");
}
@Test
void fieldWithMethodNameHasOverride(ApplicationContext ctx) {
void nestedFieldWithMethodNameHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("methodRenamed2")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(methodRenamed2).isEqualTo("nestedFieldOverride");
}
@ -133,25 +134,25 @@ public class TestBeanForByNameLookupIntegrationTests {
public class TestBeanFactoryMethodInEnclosingClassTests {
@TestBean(methodName = "nestedField", name = "nestedField")
String nestedField2;
String nestedField;
@Test
void fieldHasOverride(ApplicationContext ctx) {
void nestedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(this.nestedField2).isEqualTo("nestedFieldOverride");
assertThat(nestedField).isEqualTo("nestedFieldOverride");
}
@Nested
@DisplayName("With factory method in the enclosing class of the enclosing class")
public class TestBeanFactoryMethodInEnclosingClassLevel2Tests {
@TestBean(methodName = "nestedField", name = "nestedField")
String nestedField2;
@TestBean(methodName = "nestedField", name = "nestedNestedField")
String nestedNestedField;
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(this.nestedField2).isEqualTo("nestedFieldOverride");
void nestedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedNestedField")).as("applicationContext").isEqualTo("nestedFieldOverride");
assertThat(nestedNestedField).isEqualTo("nestedFieldOverride");
}
}
}

View File

@ -20,6 +20,8 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -32,6 +34,10 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link MockitoBean} that use by-name lookup.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
@SpringJUnitConfig
public class MockitoBeanForByNameLookupIntegrationTests {
@ -39,20 +45,8 @@ public class MockitoBeanForByNameLookupIntegrationTests {
@MockitoBean("field")
ExampleService field;
@MockitoBean("nestedField")
ExampleService nestedField;
@MockitoBean("field")
ExampleService renamed1;
@MockitoBean("nestedField")
ExampleService renamed2;
@MockitoBean("nonExistingBean")
ExampleService nonExisting1;
@MockitoBean("nestedNonExistingBean")
ExampleService nonExisting2;
ExampleService nonExisting;
@Test
@ -60,11 +54,9 @@ public class MockitoBeanForByNameLookupIntegrationTests {
assertThat(ctx.getBean("field"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(this.field)
.isSameAs(this.renamed1);
.isSameAs(field);
assertThat(this.field.greeting()).as("mocked greeting").isNull();
assertThat(this.renamed1.greeting()).as("mocked greeting").isNull();
assertThat(field.greeting()).as("mocked greeting").isNull();
}
@Test
@ -72,31 +64,65 @@ public class MockitoBeanForByNameLookupIntegrationTests {
assertThat(ctx.getBean("nonExistingBean"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(this.nonExisting1);
.isSameAs(nonExisting);
assertThat(this.nonExisting1.greeting()).as("mocked greeting").isNull();
assertThat(nonExisting.greeting()).as("mocked greeting").isNull();
}
@Nested
@DisplayName("With @MockitoBean in enclosing class")
@DisplayName("With @MockitoBean in enclosing class and in @Nested class")
public class MockitoBeanNestedTests {
@Autowired
@Qualifier("field")
ExampleService localField;
@Autowired
@Qualifier("nonExistingBean")
ExampleService localNonExisting;
@MockitoBean("nestedField")
ExampleService nestedField;
@MockitoBean("nestedNonExistingBean")
ExampleService nestedNonExisting;
@Test
void fieldAndRenamedFieldHaveSameOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField"))
assertThat(ctx.getBean("field"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(nestedField)
.isSameAs(renamed2);
.isSameAs(localField);
assertThat(localField.greeting()).as("mocked greeting").isNull();
}
@Test
void fieldIsMockedWhenNoOriginalBean(ApplicationContext ctx) {
assertThat(ctx.getBean("nonExistingBean"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(localNonExisting);
assertThat(localNonExisting.greeting()).as("mocked greeting").isNull();
}
@Test
void nestedFieldAndRenamedFieldHaveSameOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(nestedField);
}
@Test
void nestedFieldIsMockedWhenNoOriginalBean(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedNonExistingBean"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsMock)
.isSameAs(nonExisting2);
.isSameAs(nestedNonExisting);
}
}

View File

@ -1,83 +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.mockito;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.bean.override.example.ExampleService;
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.springframework.test.mockito.MockitoAssertions.assertIsNotSpy;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Integration tests for duplicate {@link MockitoSpyBean @MockitoSpyBean}
* declarations for the same target bean, selected by-name.
*
* @author Sam Brannen
* @since 6.2.1
* @see MockitoBeanDuplicateTypeIntegrationTests
* @see MockitoSpyBeanDuplicateTypeIntegrationTests
*/
@SpringJUnitConfig
public class MockitoSpyBeanDuplicateTypeAndNameIntegrationTests {
@MockitoSpyBean("exampleService1")
ExampleService service1;
@MockitoSpyBean("exampleService1")
ExampleService service2;
@Autowired
ExampleService exampleService2;
@Autowired
List<ExampleService> services;
@Test
void duplicateMocksShouldHaveBeenCreated() {
assertThat(service1).isSameAs(service2);
assertThat(services).containsExactly(service1, exampleService2);
assertIsSpy(service1, "service1");
assertIsNotSpy(exampleService2, "exampleService2");
}
@Configuration(proxyBeanMethods = false)
static class Config {
@Bean
ExampleService exampleService1() {
return new RealExampleService("@Bean 1");
}
@Bean
ExampleService exampleService2() {
return new RealExampleService("@Bean 2");
}
}
}

View File

@ -20,6 +20,8 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -44,9 +46,6 @@ public class MockitoSpyBeanForByNameLookupIntegrationTests {
@MockitoSpyBean("field1")
ExampleService field;
@MockitoSpyBean("field1")
ExampleService renamed1;
@Test
void fieldHasOverride(ApplicationContext ctx) {
@ -58,28 +57,30 @@ public class MockitoSpyBeanForByNameLookupIntegrationTests {
assertThat(field.greeting()).isEqualTo("bean1");
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field1"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsSpy)
.isSameAs(renamed1);
assertThat(renamed1.greeting()).isEqualTo("bean1");
}
@Nested
@DisplayName("With @MockitoSpyBean in enclosing class and in @Nested class")
public class MockitoSpyBeanNestedTests {
@Autowired
@Qualifier("field1")
ExampleService localField;
@MockitoSpyBean("field2")
ExampleService nestedField;
@MockitoSpyBean("field2")
ExampleService renamed2;
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field1"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsSpy)
.isSameAs(localField);
assertThat(localField.greeting()).isEqualTo("bean1");
}
@Test
void nestedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field2"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsSpy)
@ -87,16 +88,6 @@ public class MockitoSpyBeanForByNameLookupIntegrationTests {
assertThat(nestedField.greeting()).isEqualTo("bean2");
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field2"))
.isInstanceOf(ExampleService.class)
.satisfies(MockitoAssertions::assertIsSpy)
.isSameAs(renamed2);
assertThat(renamed2.greeting()).isEqualTo("bean2");
}
}
@Configuration(proxyBeanMethods = false)