diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java index 089f5a64801..7237b8cfbaf 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java @@ -93,9 +93,17 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList * a corresponding bean override instance. */ private static void injectFields(TestContext testContext) { - List handlers = BeanOverrideHandler.forTestClass(testContext.getTestClass()); + Object testInstance = testContext.getTestInstance(); + // Since JUnit Jupiter 5.12, if the SpringExtension is used with Jupiter's + // ExtensionContextScope.TEST_METHOD mode, the value returned from + // testContext.getTestClass() may refer to the declaring class of the test + // method which is about to be invoked (which may be in a @Nested class + // within the class for the test instance). Thus, we use the class for the + // test instance as the "test class". + Class testClass = testInstance.getClass(); + + List handlers = BeanOverrideHandler.forTestClass(testClass); if (!handlers.isEmpty()) { - Object testInstance = testContext.getTestInstance(); ApplicationContext applicationContext = testContext.getApplicationContext(); Assert.state(applicationContext.containsBean(BeanOverrideRegistry.BEAN_NAME), () -> """ diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java index c34587cfb1c..fb8c93a32fe 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java @@ -302,18 +302,19 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { Parameter parameter = parameterContext.getParameter(); + Class parameterType = parameter.getType(); Executable executable = parameter.getDeclaringExecutable(); PropertyProvider junitPropertyProvider = propertyName -> extensionContext.getConfigurationParameter(propertyName).orElse(null); return (TestConstructorUtils.isAutowirableConstructor(executable, junitPropertyProvider) || - ApplicationContext.class.isAssignableFrom(parameter.getType()) || - supportsApplicationEvents(parameterContext) || + ApplicationContext.class.isAssignableFrom(parameterType) || + supportsApplicationEvents(parameterType, executable) || ParameterResolutionDelegate.isAutowirable(parameter, parameterContext.getIndex())); } - private boolean supportsApplicationEvents(ParameterContext parameterContext) { - if (ApplicationEvents.class.isAssignableFrom(parameterContext.getParameter().getType())) { - Assert.isTrue(parameterContext.getDeclaringExecutable() instanceof Method, + private boolean supportsApplicationEvents(Class parameterType, Executable executable) { + if (ApplicationEvents.class.isAssignableFrom(parameterType)) { + Assert.isTrue(executable instanceof Method, "ApplicationEvents can only be injected into test and lifecycle methods"); return true; } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java similarity index 96% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupIntegrationTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java index 0a0699d0581..6eba56e7bfb 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java @@ -19,6 +19,7 @@ package org.springframework.test.context.bean.override.convention; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; @@ -30,14 +31,15 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link TestBean} that use by-name lookup. + * Integration tests for {@link TestBean} that use by-name lookup with test class + * {@link ExtensionContextScope}. * * @author Simon Baslé * @author Sam Brannen * @since 6.2 */ @SpringJUnitConfig -public class TestBeanByNameLookupIntegrationTests { +public class TestBeanByNameLookupTestClassScopedExtensionContextIntegrationTests { @TestBean(name = "field") String field; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java new file mode 100644 index 00000000000..2b41f116f46 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java @@ -0,0 +1,205 @@ +/* + * Copyright 2002-present 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 org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + + +/** + * Integration tests for {@link TestBean} that use by-name lookup with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Simon Baslé + * @author Sam Brannen + * @since 6.2.13 + */ +public class TestBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(12).succeeded(12).failed(0)); + } + + + @SpringJUnitConfig + public static class TestCase { + + @TestBean(name = "field") + String field; + + @TestBean(name = "methodRenamed1", methodName = "field") + String methodRenamed1; + + static String field() { + return "fieldOverride"; + } + + static String nestedField() { + return "nestedFieldOverride"; + } + + @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"); + } + + + @Nested + @DisplayName("With @TestBean in enclosing class and in @Nested class") + public class TestBeanFieldInEnclosingClassTestCase { + + @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 nestedFieldWithMethodNameHasOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("methodRenamed2")).as("applicationContext").isEqualTo("nestedFieldOverride"); + assertThat(methodRenamed2).isEqualTo("nestedFieldOverride"); + } + + @Nested + @DisplayName("With @TestBean in the enclosing classes") + public class TestBeanFieldInEnclosingClassLevel2TestCase { + + @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 nestedFieldWithMethodNameHasOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("methodRenamed2")).as("applicationContext").isEqualTo("nestedFieldOverride"); + assertThat(methodRenamed2).isEqualTo("nestedFieldOverride"); + } + } + } + + @Nested + @DisplayName("With factory method in enclosing class") + public class TestBeanFactoryMethodInEnclosingClassTestCase { + + @TestBean(methodName = "nestedField", name = "nestedField") + String nestedField; + + @Test + void nestedFieldHasOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("nestedField")).as("applicationContext").isEqualTo("nestedFieldOverride"); + assertThat(nestedField).isEqualTo("nestedFieldOverride"); + } + + @Nested + @DisplayName("With factory method in the enclosing class of the enclosing class") + public class TestBeanFactoryMethodInEnclosingClassLevel2TestCase { + + @TestBean(methodName = "nestedField", name = "nestedNestedField") + String nestedNestedField; + + @Test + void nestedFieldHasOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("nestedNestedField")).as("applicationContext").isEqualTo("nestedFieldOverride"); + assertThat(nestedNestedField).isEqualTo("nestedFieldOverride"); + } + } + } + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean("field") + String bean1() { + return "prod"; + } + + @Bean("nestedField") + String bean2() { + return "nestedProd"; + } + + @Bean("methodRenamed1") + String bean3() { + return "Prod"; + } + + @Bean("methodRenamed2") + String bean4() { + return "NestedProd"; + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java similarity index 95% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupIntegrationTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java index 277e7f1ea5a..0ff4d3d5df8 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java @@ -19,6 +19,7 @@ package org.springframework.test.context.bean.override.mockito; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -35,14 +36,15 @@ import org.springframework.test.mockito.MockitoAssertions; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link MockitoBean} that use by-name lookup. + * Integration tests for {@link MockitoBean} that use by-name lookup with test class + * {@link ExtensionContextScope}. * * @author Simon Baslé * @author Sam Brannen * @since 6.2 */ @SpringJUnitConfig -public class MockitoBeanByNameLookupIntegrationTests { +public class MockitoBeanByNameLookupTestClassScopedExtensionContextIntegrationTests { @MockitoBean("field") ExampleService field; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java new file mode 100644 index 00000000000..e2c5986d948 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java @@ -0,0 +1,164 @@ +/* + * Copyright 2002-present 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 org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +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; +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 org.springframework.test.mockito.MockitoAssertions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +/** + * Integration tests for {@link MockitoBean} that use by-name lookup with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Simon Baslé + * @author Sam Brannen + * @since 6.2.13 + */ +public class MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(6).succeeded(6).failed(0)); + } + + + @SpringJUnitConfig + public static class TestCase { + + @MockitoBean("field") + ExampleService field; + + @MockitoBean("nonExistingBean") + ExampleService nonExisting; + + + @Test + void fieldAndRenamedFieldHaveSameOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("field")) + .isInstanceOf(ExampleService.class) + .satisfies(MockitoAssertions::assertIsMock) + .isSameAs(field); + + assertThat(field.greeting()).as("mocked greeting").isNull(); + } + + @Test + void fieldIsMockedWhenNoOriginalBean(ApplicationContext ctx) { + assertThat(ctx.getBean("nonExistingBean")) + .isInstanceOf(ExampleService.class) + .satisfies(MockitoAssertions::assertIsMock) + .isSameAs(nonExisting); + + assertThat(nonExisting.greeting()).as("mocked greeting").isNull(); + } + + + @Nested + @DisplayName("With @MockitoBean in enclosing class and in @Nested class") + public class MockitoBeanNestedTestCase { + + @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("field")) + .isInstanceOf(ExampleService.class) + .satisfies(MockitoAssertions::assertIsMock) + .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(nestedNonExisting); + } + } + } + + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean("field") + ExampleService bean1() { + return new RealExampleService("Hello Field"); + } + + @Bean("nestedField") + ExampleService bean2() { + return new RealExampleService("Hello Nested Field"); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java similarity index 92% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupIntegrationTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java index 68ce463434b..49dc19acab2 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.java @@ -19,6 +19,7 @@ package org.springframework.test.context.bean.override.mockito; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -29,21 +30,22 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.test.context.bean.override.example.ExampleService; import org.springframework.test.context.bean.override.example.RealExampleService; -import org.springframework.test.context.bean.override.mockito.MockitoSpyBeanByNameLookupIntegrationTests.Config; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBeanByNameLookupTestClassScopedExtensionContextIntegrationTests.Config; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.mockito.MockitoAssertions; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link MockitoSpyBean} that use by-name lookup. + * Integration tests for {@link MockitoSpyBean} that use by-name lookup with test class + * {@link ExtensionContextScope}. * * @author Simon Baslé * @author Sam Brannen * @since 6.2 */ @SpringJUnitConfig(Config.class) -public class MockitoSpyBeanByNameLookupIntegrationTests { +public class MockitoSpyBeanByNameLookupTestClassScopedExtensionContextIntegrationTests { @MockitoSpyBean("field1") ExampleService field; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java new file mode 100644 index 00000000000..6aa660a9f5f --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-present 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 org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +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; +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 org.springframework.test.mockito.MockitoAssertions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +/** + * Integration tests for {@link MockitoSpyBean} that use by-name lookup with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Simon Baslé + * @author Sam Brannen + * @since 6.2.13 + */ +public class MockitoSpyBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(3).succeeded(3).failed(0)); + } + + + @SpringJUnitConfig(Config.class) + public static class TestCase { + + @MockitoSpyBean("field1") + ExampleService field; + + + @Test + void fieldHasOverride(ApplicationContext ctx) { + assertThat(ctx.getBean("field1")) + .isInstanceOf(ExampleService.class) + .satisfies(MockitoAssertions::assertIsSpy) + .isSameAs(field); + + assertThat(field.greeting()).isEqualTo("bean1"); + } + + + @Nested + @DisplayName("With @MockitoSpyBean in enclosing class and in @Nested class") + public class MockitoSpyBeanNestedTestCase { + + @Autowired + @Qualifier("field1") + ExampleService localField; + + @MockitoSpyBean("field2") + ExampleService nestedField; + + @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) + .isSameAs(nestedField); + + assertThat(nestedField.greeting()).isEqualTo("bean2"); + } + } + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean("field1") + ExampleService bean1() { + return new RealExampleService("bean1"); + } + + @Bean("field2") + ExampleService bean2() { + return new RealExampleService("bean2"); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestClassScopedExtensionContextNestedTests.java similarity index 91% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestClassScopedExtensionContextNestedTests.java index 2ba54a1f766..1a80d5a8f3c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestClassScopedExtensionContextNestedTests.java @@ -20,6 +20,7 @@ import java.util.List; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -30,7 +31,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.NestedTestConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.junit.jupiter.nested.ActiveProfilesNestedTests.Config1; +import org.springframework.test.context.junit.jupiter.nested.ActiveProfilesTestClassScopedExtensionContextNestedTests.Config1; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; @@ -39,7 +40,8 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing /** * Integration tests that verify support for {@code @Nested} test classes using * {@link ActiveProfiles @ActiveProfiles} in conjunction with the - * {@link SpringExtension} in a JUnit Jupiter environment. + * {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope}. * * @author Sam Brannen * @since 5.3 @@ -47,7 +49,7 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing @SpringJUnitConfig(Config1.class) @ActiveProfiles("1") @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default -class ActiveProfilesNestedTests { +class ActiveProfilesTestClassScopedExtensionContextNestedTests { @Autowired List strings; @@ -69,8 +71,9 @@ class ActiveProfilesNestedTests { @Test void test() { - assertThat(strings).containsExactlyInAnyOrder("X", "A1"); - assertThat(this.localStrings).containsExactlyInAnyOrder("X", "A1"); + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "A1"); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..a8a73be4183 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ActiveProfilesTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,236 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import java.util.List; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; + +/** + * Integration tests that verify support for {@code @Nested} test classes using + * {@link ActiveProfiles @ActiveProfiles} in conjunction with the + * {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + */ +class ActiveProfilesTestMethodScopedExtensionContextNestedTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(7).succeeded(7).failed(0)); + } + + + @SpringJUnitConfig(Config1.class) + @ActiveProfiles("1") + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + static class TestCase { + + @Autowired + List strings; + + + @Test + void test() { + assertThat(this.strings).containsExactlyInAnyOrder("X", "A1"); + } + + + @Nested + @NestedTestConfiguration(INHERIT) + class InheritedConfigTestCase { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "A1"); + } + } + + @Nested + @SpringJUnitConfig(Config2.class) + @ActiveProfiles("2") + class ConfigOverriddenByDefaultTestCase { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("Y", "A2"); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + @ContextConfiguration(classes = Config2.class) + @ActiveProfiles("2") + class InheritedAndExtendedConfigTestCase { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "A1", "Y", "A2"); + } + + + @Nested + @NestedTestConfiguration(OVERRIDE) + @SpringJUnitConfig({ Config1.class, Config2.class, Config3.class }) + @ActiveProfiles("3") + class DoubleNestedWithOverriddenConfigTestCase { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "Y", "Z", "A3"); + } + + + @Nested + @NestedTestConfiguration(INHERIT) + @ActiveProfiles(profiles = "2", inheritProfiles = false) + class TripleNestedWithInheritedConfigButOverriddenProfilesTestCase { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "Y", "Z", "A2"); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedWithInheritedConfigAndTestInterfaceTestCase implements TestInterface { + + @Autowired + List localStrings; + + + @Test + void test() { + assertThat(strings) + .isEqualTo(this.localStrings) + .containsExactlyInAnyOrder("X", "Y", "Z", "A2", "A3"); + } + } + } + } + } + + // ------------------------------------------------------------------------- + + @Configuration + static class Config1 { + + @Bean + String x() { + return "X"; + } + + @Bean + @Profile("1") + String a1() { + return "A1"; + } + } + + @Configuration + static class Config2 { + + @Bean + String y() { + return "Y"; + } + + @Bean + @Profile("2") + String a2() { + return "A2"; + } + } + + @Configuration + static class Config3 { + + @Bean + String z() { + return "Z"; + } + + @Bean + @Profile("3") + String a3() { + return "A3"; + } + } + + @ActiveProfiles("2") + interface TestInterface { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestClassScopedExtensionContextNestedTests.java similarity index 86% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestClassScopedExtensionContextNestedTests.java index d60143d5793..81cf6117107 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestClassScopedExtensionContextNestedTests.java @@ -19,6 +19,7 @@ package org.springframework.test.context.junit.jupiter.nested; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -28,28 +29,29 @@ import org.springframework.context.annotation.Configuration; import org.springframework.test.context.NestedTestConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.junit.jupiter.nested.ConstructorInjectionNestedTests.TopLevelConfig; +import org.springframework.test.context.junit.jupiter.nested.ConstructorInjectionTestClassScopedExtensionContextNestedTests.TopLevelConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; /** * Integration tests that verify support for {@code @Nested} test classes in conjunction - * with the {@link SpringExtension} in a JUnit Jupiter environment ... when using - * constructor injection as opposed to field injection (see SPR-16653). + * with the {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope} ... when using constructor injection as opposed + * to field injection (see SPR-16653). * * @author Sam Brannen * @since 5.0.5 - * @see ContextConfigurationNestedTests + * @see ContextConfigurationTestClassScopedExtensionContextNestedTests * @see org.springframework.test.context.junit4.nested.NestedTestsWithSpringRulesTests */ @SpringJUnitConfig(TopLevelConfig.class) @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default -class ConstructorInjectionNestedTests { +class ConstructorInjectionTestClassScopedExtensionContextNestedTests { final String foo; - ConstructorInjectionNestedTests(TestInfo testInfo, @Autowired String foo) { + ConstructorInjectionTestClassScopedExtensionContextNestedTests(TestInfo testInfo, @Autowired String foo) { this.foo = foo; } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..b6ba04cd91a --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ConstructorInjectionTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,173 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; + +/** + * Integration tests that verify support for {@code @Nested} test classes in conjunction + * with the {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD} ... when using constructor injection + * as opposed to field injection (see SPR-16653). + * + * @author Sam Brannen + * @since 6.2.13 + * @see ContextConfigurationTestClassScopedExtensionContextNestedTests + * @see org.springframework.test.context.junit4.nested.NestedTestsWithSpringRulesTests + */ +class ConstructorInjectionTestMethodScopedExtensionContextNestedTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(5).succeeded(5).failed(0)); + } + + + @SpringJUnitConfig(TopLevelConfig.class) + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + static class TestCase { + + final String foo; + + TestCase(TestInfo testInfo, @Autowired String foo) { + this.foo = foo; + } + + @Test + void topLevelTest() { + assertThat(foo).isEqualTo("foo"); + } + + @Nested + @SpringJUnitConfig(NestedConfig.class) + class AutowiredConstructorTestCase { + + final String bar; + + @Autowired + AutowiredConstructorTestCase(String bar) { + this.bar = bar; + } + + @Test + void nestedTest() { + assertThat(foo).isEqualTo("bar"); + assertThat(bar).isEqualTo("bar"); + } + } + + @Nested + @SpringJUnitConfig(NestedConfig.class) + class AutowiredConstructorParameterTestCase { + + final String bar; + + AutowiredConstructorParameterTestCase(@Autowired String bar) { + this.bar = bar; + } + + @Test + void nestedTest() { + assertThat(foo).isEqualTo("bar"); + assertThat(bar).isEqualTo("bar"); + } + } + + @Nested + @SpringJUnitConfig(NestedConfig.class) + class QualifiedConstructorParameterTestCase { + + final String bar; + + QualifiedConstructorParameterTestCase(TestInfo testInfo, @Qualifier("bar") String s) { + this.bar = s; + } + + @Test + void nestedTest() { + assertThat(foo).isEqualTo("bar"); + assertThat(bar).isEqualTo("bar"); + } + } + + @Nested + @SpringJUnitConfig(NestedConfig.class) + class SpelConstructorParameterTestCase { + + final String bar; + final int answer; + + SpelConstructorParameterTestCase(@Autowired String bar, TestInfo testInfo, @Value("#{ 6 * 7 }") int answer) { + this.bar = bar; + this.answer = answer; + } + + @Test + void nestedTest() { + assertThat(foo).isEqualTo("bar"); + assertThat(bar).isEqualTo("bar"); + assertThat(answer).isEqualTo(42); + } + } + + } + + // ------------------------------------------------------------------------- + + @Configuration + static class TopLevelConfig { + + @Bean + String foo() { + return "foo"; + } + } + + @Configuration + static class NestedConfig { + + @Bean + String bar() { + return "bar"; + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestClassScopedExtensionContextNestedTests.java similarity index 92% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestClassScopedExtensionContextNestedTests.java index 61022e39a4b..6ee181fe100 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestClassScopedExtensionContextNestedTests.java @@ -18,6 +18,7 @@ package org.springframework.test.context.junit.jupiter.nested; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -27,7 +28,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.NestedTestConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.junit.jupiter.nested.ContextConfigurationNestedTests.TopLevelConfig; +import org.springframework.test.context.junit.jupiter.nested.ContextConfigurationTestClassScopedExtensionContextNestedTests.TopLevelConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; @@ -36,16 +37,17 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing /** * Integration tests that verify support for {@code @Nested} test classes using * {@link ContextConfiguration @ContextConfiguration} in conjunction with the - * {@link SpringExtension} in a JUnit Jupiter environment. + * {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope}. * * @author Sam Brannen * @since 5.0 - * @see ConstructorInjectionNestedTests + * @see ConstructorInjectionTestClassScopedExtensionContextNestedTests * @see org.springframework.test.context.junit4.nested.NestedTestsWithSpringRulesTests */ @SpringJUnitConfig(TopLevelConfig.class) @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default -class ContextConfigurationNestedTests { +class ContextConfigurationTestClassScopedExtensionContextNestedTests { private static final String FOO = "foo"; private static final String BAR = "bar"; diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..fecf82498ab --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextConfigurationTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,234 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; + +/** + * Integration tests that verify support for {@code @Nested} test classes using + * {@link ContextConfiguration @ContextConfiguration} in conjunction with the + * {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + * @see ConstructorInjectionTestClassScopedExtensionContextNestedTests + * @see org.springframework.test.context.junit4.nested.NestedTestsWithSpringRulesTests + */ +class ContextConfigurationTestMethodScopedExtensionContextNestedTests { + + private static final String FOO = "foo"; + private static final String BAR = "bar"; + private static final String BAZ = "baz"; + + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(6).succeeded(6).failed(0)); + } + + + @SpringJUnitConfig(TopLevelConfig.class) + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + static class TestCase { + + private static final String FOO = "foo"; + private static final String BAR = "bar"; + private static final String BAZ = "baz"; + + @Autowired(required = false) + @Qualifier("foo") + String foo; + + + @Test + void topLevelTest() { + assertThat(foo).isEqualTo(FOO); + } + + + @Nested + @SpringJUnitConfig(NestedConfig.class) + class NestedTestCase { + + @Autowired(required = false) + @Qualifier("foo") + String localFoo; + + @Autowired + String bar; + + + @Test + void test() { + assertThat(foo).as("foo bean should not be present").isNull(); + assertThat(this.localFoo).as("local foo bean should not be present").isNull(); + assertThat(this.bar).isEqualTo(BAR); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + class NestedTestCaseWithInheritedConfigTestCase { + + @Autowired(required = false) + @Qualifier("foo") + String localFoo; + + @Autowired + String bar; + + + @Test + void test() { + // Since the configuration is inherited, the foo field in the outer instance + // and the bar field in the inner instance should both have been injected + // from the test ApplicationContext for the outer instance. + assertThat(foo).isEqualTo(FOO); + assertThat(this.localFoo).isEqualTo(FOO); + assertThat(this.bar).isEqualTo(FOO); + } + + + @Nested + @NestedTestConfiguration(OVERRIDE) + @SpringJUnitConfig(NestedConfig.class) + class DoubleNestedWithOverriddenConfigTestCase { + + @Autowired(required = false) + @Qualifier("foo") + String localFoo; + + @Autowired + String bar; + + + @Test + void test() { + assertThat(foo).as("foo bean should not be present").isNull(); + assertThat(this.localFoo).as("local foo bean should not be present").isNull(); + assertThat(this.bar).isEqualTo(BAR); + } + + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedWithInheritedConfigTestCase { + + @Autowired(required = false) + @Qualifier("foo") + String localFoo; + + @Autowired + String bar; + + + @Test + void test() { + assertThat(foo).as("foo bean should not be present").isNull(); + assertThat(this.localFoo).as("local foo bean should not be present").isNull(); + assertThat(this.bar).isEqualTo(BAR); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedWithInheritedConfigAndTestInterfaceTestCase implements TestInterface { + + @Autowired(required = false) + @Qualifier("foo") + String localFoo; + + @Autowired + @Qualifier("bar") + String bar; + + @Autowired + String baz; + + + @Test + void test() { + assertThat(foo).as("foo bean should not be present").isNull(); + assertThat(this.localFoo).as("local foo bean should not be present").isNull(); + assertThat(this.bar).isEqualTo(BAR); + assertThat(this.baz).isEqualTo(BAZ); + } + } + } + } + + } + + // ------------------------------------------------------------------------- + + @Configuration + static class TopLevelConfig { + + @Bean + String foo() { + return FOO; + } + } + + @Configuration + static class NestedConfig { + + @Bean + String bar() { + return BAR; + } + } + + @Configuration + static class TestInterfaceConfig { + + @Bean + String baz() { + return BAZ; + } + } + + @ContextConfiguration(classes = TestInterfaceConfig.class) + interface TestInterface { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestClassScopedExtensionContextNestedTests.java similarity index 92% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestClassScopedExtensionContextNestedTests.java index a8608aa7ee5..c47cf2a630b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestClassScopedExtensionContextNestedTests.java @@ -19,6 +19,7 @@ package org.springframework.test.context.junit.jupiter.nested; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -30,7 +31,7 @@ import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.NestedTestConfiguration; import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.junit.jupiter.nested.ContextHierarchyNestedTests.ParentConfig; +import org.springframework.test.context.junit.jupiter.nested.ContextHierarchyTestClassScopedExtensionContextNestedTests.ParentConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; @@ -39,7 +40,8 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing /** * Integration tests that verify support for {@code @Nested} test classes using * {@link ContextHierarchy @ContextHierarchy} in conjunction with the - * {@link SpringExtension} in a JUnit Jupiter environment. + * {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope}. * * @author Sam Brannen * @since 5.3 @@ -48,7 +50,7 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing @ContextHierarchy(@ContextConfiguration(classes = ParentConfig.class)) @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default @DisabledInAotMode("@ContextHierarchy is not supported in AOT") -class ContextHierarchyNestedTests { +class ContextHierarchyTestClassScopedExtensionContextNestedTests { private static final String FOO = "foo"; private static final String BAR = "bar"; @@ -97,7 +99,7 @@ class ContextHierarchyNestedTests { @Nested @NestedTestConfiguration(INHERIT) @ContextConfiguration(classes = Child1Config.class) - class NestedTestCaseWithInheritedConfigTests { + class NestedInheritedCfgTests { @Autowired String bar; @@ -125,7 +127,7 @@ class ContextHierarchyNestedTests { @ContextConfiguration(classes = ParentConfig.class), @ContextConfiguration(classes = Child2Config.class) }) - class DoubleNestedTestCaseWithOverriddenConfigTests { + class DoubleNestedOverriddenCfgTests { @Autowired String bar; @@ -146,7 +148,7 @@ class ContextHierarchyNestedTests { @Nested @NestedTestConfiguration(INHERIT) - class TripleNestedWithInheritedConfigAndTestInterfaceTests implements TestInterface { + class TripleNestedInheritedCfgAndTestInterfaceTests implements TestInterface { @Autowired @Qualifier("foo") diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..dfc0777c33d --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/ContextHierarchyTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,261 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +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; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.aot.DisabledInAotMode; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; + +/** + * Integration tests that verify support for {@code @Nested} test classes using + * {@link ContextHierarchy @ContextHierarchy} in conjunction with the + * {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + */ +class ContextHierarchyTestMethodScopedExtensionContextNestedTests { + + private static final String FOO = "foo"; + private static final String BAR = "bar"; + private static final String BAZ = "baz"; + private static final String QUX = "qux"; + + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(5).succeeded(5).failed(0)); + } + + + @ExtendWith(SpringExtension.class) + @ContextHierarchy(@ContextConfiguration(classes = ParentConfig.class)) + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + @DisabledInAotMode("@ContextHierarchy is not supported in AOT") + static class TestCase { + + @Autowired + String foo; + + @Autowired + ApplicationContext context; + + + @Test + void topLevelTest() { + assertThat(this.context).as("local ApplicationContext").isNotNull(); + assertThat(this.context.getParent()).as("parent ApplicationContext").isNull(); + + assertThat(foo).isEqualTo(FOO); + } + + @Nested + @ContextConfiguration(classes = NestedConfig.class) + class NestedTestCase { + + @Autowired + String bar; + + @Autowired + ApplicationContext context; + + + @Test + void nestedTest() { + assertThat(this.context).as("local ApplicationContext").isNotNull(); + assertThat(this.context.getParent()).as("parent ApplicationContext").isNull(); + + // The foo field in the outer instance should have been injected from + // the test ApplicationContext for NestedTestCase. + assertThat(foo).isEqualTo(BAR); + assertThat(this.bar).isEqualTo(BAR); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + @ContextConfiguration(classes = Child1Config.class) + class NestedInheritedCfgTestCase { + + @Autowired + String bar; + + @Autowired + ApplicationContext context; + + + @Test + void nestedTest() { + assertThat(this.context).as("local ApplicationContext").isNotNull(); + assertThat(this.context.getParent()).as("parent ApplicationContext").isNotNull(); + + // The foo field in the outer instance and the bar field in the inner + // instance should both have been injected from the test ApplicationContext + // for the inner instance. + assertThat(foo).as("foo") + .isEqualTo(this.context.getBean("foo", String.class)) + .isEqualTo(QUX + 1); + assertThat(this.bar).isEqualTo(BAZ + 1); + } + + @Nested + @NestedTestConfiguration(OVERRIDE) + @ContextHierarchy({ + @ContextConfiguration(classes = ParentConfig.class), + @ContextConfiguration(classes = Child2Config.class) + }) + class DoubleNestedOverriddenCfgTestCase { + + @Autowired + String bar; + + @Autowired + ApplicationContext context; + + + @Test + void nestedTest() { + assertThat(this.context).as("local ApplicationContext").isNotNull(); + assertThat(this.context.getParent()).as("parent ApplicationContext").isNotNull(); + + assertThat(foo).as("foo") + .isEqualTo(this.context.getBean("foo", String.class)) + .isEqualTo(QUX + 2); + assertThat(this.bar).isEqualTo(BAZ + 2); + } + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedInheritedCfgAndTestInterfaceTestCase implements TestInterface { + + @Autowired + @Qualifier("foo") + String localFoo; + + @Autowired + String bar; + + @Autowired + ApplicationContext context; + + + @Test + void nestedTest() { + assertThat(this.context).as("local ApplicationContext").isNotNull(); + assertThat(this.context.getParent()).as("parent ApplicationContext").isNotNull(); + assertThat(this.context.getParent().getParent()).as("grandparent ApplicationContext").isNotNull(); + + assertThat(foo).as("foo") + .isEqualTo(this.localFoo) + .isEqualTo(this.context.getBean("foo", String.class)) + .isEqualTo("test interface"); + assertThat(this.bar).isEqualTo(BAZ + 2); + } + } + } + } + + } + + // ------------------------------------------------------------------------- + + @Configuration + static class ParentConfig { + + @Bean + String foo() { + return FOO; + } + } + + @Configuration + static class Child1Config { + + @Bean + String foo() { + return QUX + 1; + } + + @Bean + String bar() { + return BAZ + 1; + } + } + + @Configuration + static class Child2Config { + + @Bean + String foo() { + return QUX + 2; + } + + @Bean + String bar() { + return BAZ + 2; + } + } + + @Configuration + static class NestedConfig { + + @Bean + String bar() { + return BAR; + } + } + + @Configuration + static class TestInterfaceConfig { + + @Bean + String foo() { + return "test interface"; + } + } + + @ContextConfiguration(classes = TestInterfaceConfig.class) + interface TestInterface { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestClassScopedExtensionContextNestedTests.java similarity index 92% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestClassScopedExtensionContextNestedTests.java index 3cda1777d78..6d2da9c7a4f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestClassScopedExtensionContextNestedTests.java @@ -18,6 +18,7 @@ package org.springframework.test.context.junit.jupiter.nested; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -36,7 +37,8 @@ import static org.springframework.test.context.TestConstructor.AutowireMode.ANNO /** * Integration tests that verify support for {@code @Nested} test classes using * {@link TestConstructor @TestConstructor} in conjunction with the - * {@link SpringExtension} in a JUnit Jupiter environment. + * {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope}. * * @author Sam Brannen * @since 5.3 @@ -44,9 +46,9 @@ import static org.springframework.test.context.TestConstructor.AutowireMode.ANNO @SpringJUnitConfig @TestConstructor(autowireMode = ALL) @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default -class TestConstructorNestedTests { +class TestConstructorTestClassScopedExtensionContextNestedTests { - TestConstructorNestedTests(String text) { + TestConstructorTestClassScopedExtensionContextNestedTests(String text) { assertThat(text).isEqualTo("enigma"); } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..21d423155f2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestConstructorTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,190 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.TestConstructor; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; +import static org.springframework.test.context.TestConstructor.AutowireMode.ALL; +import static org.springframework.test.context.TestConstructor.AutowireMode.ANNOTATED; + +/** + * Integration tests that verify support for {@code @Nested} test classes using + * {@link TestConstructor @TestConstructor} in conjunction with the + * {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + */ +class TestConstructorTestMethodScopedExtensionContextNestedTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(8).succeeded(8).failed(0)); + } + + + @SpringJUnitConfig(Config.class) + @TestConstructor(autowireMode = ALL) + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + static class TestCase { + + TestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + + + @Nested + @SpringJUnitConfig(Config.class) + @TestConstructor(autowireMode = ANNOTATED) + class ConfigOverriddenByDefaultTestCase { + + @Autowired + ConfigOverriddenByDefaultTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + class InheritedConfigTestCase { + + InheritedConfigTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + + + @Nested + class DoubleNestedWithImplicitlyInheritedConfigTestCase { + + DoubleNestedWithImplicitlyInheritedConfigTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + + + @Nested + class TripleNestedWithImplicitlyInheritedConfigTestCase { + + TripleNestedWithImplicitlyInheritedConfigTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + } + } + + @Nested + @NestedTestConfiguration(OVERRIDE) + @SpringJUnitConfig(Config.class) + @TestConstructor(autowireMode = ANNOTATED) + class DoubleNestedWithOverriddenConfigTestCase { + + DoubleNestedWithOverriddenConfigTestCase(@Autowired String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedWithInheritedConfigTestCase { + + @Autowired + TripleNestedWithInheritedConfigTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + class TripleNestedWithInheritedConfigAndTestInterfaceTestCase implements TestInterface { + + TripleNestedWithInheritedConfigAndTestInterfaceTestCase(String text) { + assertThat(text).isEqualTo("enigma"); + } + + @Test + void test() { + } + } + } + } + } + + // ------------------------------------------------------------------------- + + @Configuration + static class Config { + + @Bean + String text() { + return "enigma"; + } + } + + @TestConstructor(autowireMode = ALL) + interface TestInterface { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestClassScopedExtensionContextNestedTests.java similarity index 90% rename from spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceNestedTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestClassScopedExtensionContextNestedTests.java index 49e5e282886..369d1734175 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestClassScopedExtensionContextNestedTests.java @@ -18,6 +18,7 @@ package org.springframework.test.context.junit.jupiter.nested; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -26,7 +27,7 @@ import org.springframework.test.context.NestedTestConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.junit.jupiter.nested.TestPropertySourceNestedTests.Config; +import org.springframework.test.context.junit.jupiter.nested.TestPropertySourceTestClassScopedExtensionContextNestedTests.Config; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; @@ -35,7 +36,8 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing /** * Integration tests that verify support for {@code @Nested} test classes using * {@link TestPropertySource @TestPropertySource} in conjunction with the - * {@link SpringExtension} in a JUnit Jupiter environment. + * {@link SpringExtension} in a JUnit Jupiter environment with test class + * {@link ExtensionContextScope}. * * @author Sam Brannen * @since 5.3 @@ -43,7 +45,7 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing @SpringJUnitConfig(Config.class) @TestPropertySource(properties = "p1 = v1") @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default -class TestPropertySourceNestedTests { +class TestPropertySourceTestClassScopedExtensionContextNestedTests { @Autowired Environment env1; @@ -57,7 +59,7 @@ class TestPropertySourceNestedTests { @Nested @NestedTestConfiguration(INHERIT) - class InheritedConfigTests { + class InheritedCfgTests { @Autowired Environment env2; @@ -93,7 +95,7 @@ class TestPropertySourceNestedTests { @NestedTestConfiguration(INHERIT) @TestPropertySource(properties = "p2a = v2a") @TestPropertySource(properties = "p2b = v2b") - class InheritedAndExtendedConfigTests { + class InheritedAndExtendedCfgTests { @Autowired Environment env2; @@ -113,7 +115,7 @@ class TestPropertySourceNestedTests { @NestedTestConfiguration(OVERRIDE) @SpringJUnitConfig(Config.class) @TestPropertySource(properties = "p3 = v3") - class L3WithOverriddenConfigTests { + class L3OverriddenCfgTests { @Autowired Environment env3; @@ -136,7 +138,7 @@ class TestPropertySourceNestedTests { @Nested @NestedTestConfiguration(INHERIT) @TestPropertySource(properties = {"p3 = v34", "p4 = v4"}, inheritProperties = false) - class L4WithInheritedConfigButOverriddenTestPropertiesTests { + class L4InheritedCfgButOverriddenTestPropsTests { @Autowired Environment env4; @@ -161,7 +163,7 @@ class TestPropertySourceNestedTests { } @Nested - class L5WithInheritedConfigAndTestInterfaceTests implements TestInterface { + class L5InheritedCfgAndTestInterfaceTests implements TestInterface { @Autowired Environment env5; diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestMethodScopedExtensionContextNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestMethodScopedExtensionContextNestedTests.java new file mode 100644 index 00000000000..9be4d3db645 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/TestPropertySourceTestMethodScopedExtensionContextNestedTests.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-present 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.junit.jupiter.nested; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.testkit.engine.EngineTestKit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.context.NestedTestConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME; +import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.INHERIT; +import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE; + +/** + * Integration tests that verify support for {@code @Nested} test classes using + * {@link TestPropertySource @TestPropertySource} in conjunction with the + * {@link SpringExtension} in a JUnit Jupiter environment with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + */ +class TestPropertySourceTestMethodScopedExtensionContextNestedTests { + + @Test + void runTests() { + EngineTestKit.engine("junit-jupiter") + .configurationParameter(DEFAULT_SCOPE_PROPERTY_NAME, TEST_METHOD.name()) + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(7).succeeded(7).failed(0)); + } + + + @SpringJUnitConfig(Config.class) + @TestPropertySource(properties = "p1 = v1") + @NestedTestConfiguration(OVERRIDE) // since INHERIT is now the global default + static class TestCase { + + @Autowired + Environment env1; + + + @Test + void propertiesInEnvironment() { + assertThat(env1.getProperty("p1")).isEqualTo("v1"); + } + + + @Nested + @NestedTestConfiguration(INHERIT) + class InheritedCfgTestCase { + + @Autowired + Environment env2; + + + @Test + void propertiesInEnvironment() { + assertThat(env1).isSameAs(env2); + assertThat(env2.getProperty("p1")).isEqualTo("v1"); + } + } + + @Nested + @SpringJUnitConfig(Config.class) + @TestPropertySource(properties = "p2 = v2") + class ConfigOverriddenByDefaultTestCase { + + @Autowired + Environment env2; + + + @Test + void propertiesInEnvironment() { + assertThat(env1).isSameAs(env2); + assertThat(env2.getProperty("p1")).isNull(); + assertThat(env2.getProperty("p2")).isEqualTo("v2"); + } + } + + @Nested + @NestedTestConfiguration(INHERIT) + @TestPropertySource(properties = "p2a = v2a") + @TestPropertySource(properties = "p2b = v2b") + class InheritedAndExtendedCfgTestCase { + + @Autowired + Environment env2; + + + @Test + void propertiesInEnvironment() { + assertThat(env1).isSameAs(env2); + assertThat(env2.getProperty("p1")).isEqualTo("v1"); + assertThat(env2.getProperty("p2a")).isEqualTo("v2a"); + assertThat(env2.getProperty("p2b")).isEqualTo("v2b"); + } + + + @Nested + @NestedTestConfiguration(OVERRIDE) + @SpringJUnitConfig(Config.class) + @TestPropertySource(properties = "p3 = v3") + class L3OverriddenCfgTestCase { + + @Autowired + Environment env3; + + + @Test + void propertiesInEnvironment() { + assertThat(env1).isSameAs(env2).isSameAs(env3); + assertThat(env3.getProperty("p1")).isNull(); + assertThat(env3.getProperty("p2")).isNull(); + assertThat(env3.getProperty("p3")).isEqualTo("v3"); + } + + + @Nested + @NestedTestConfiguration(INHERIT) + @TestPropertySource(properties = {"p3 = v34", "p4 = v4"}, inheritProperties = false) + class L4InheritedCfgButOverriddenTestPropertiesTestCase { + + @Autowired + Environment env4; + + + @Test + void propertiesInEnvironment() { + assertThat(env1).isSameAs(env2).isSameAs(env3).isSameAs(env4); + assertThat(env4.getProperty("p1")).isNull(); + assertThat(env4.getProperty("p2")).isNull(); + assertThat(env4.getProperty("p3")).isEqualTo("v34"); + assertThat(env4.getProperty("p4")).isEqualTo("v4"); + } + + @Nested + class L5InheritedCfgAndTestInterfaceTestCase implements TestInterface { + + @Autowired + Environment env5; + + + @Test + void propertiesInEnvironment() { + assertThat(env4).isSameAs(env5); + assertThat(env5.getProperty("p1")).isNull(); + assertThat(env5.getProperty("p2")).isNull(); + assertThat(env5.getProperty("p3")).isEqualTo("v34"); + assertThat(env5.getProperty("p4")).isEqualTo("v4"); + assertThat(env5.getProperty("foo")).isEqualTo("bar"); + assertThat(env5.getProperty("enigma")).isEqualTo("42"); + } + } + } + } + } + } + + // ------------------------------------------------------------------------- + + @Configuration + static class Config { + /* no user beans required for these tests */ + } + + @TestPropertySource(properties = { "foo = bar", "enigma: 42" }) + interface TestInterface { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/WebAppConfigurationNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/WebAppConfigurationNestedTests.java index c2a472a6b8c..c8ab547ee98 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/WebAppConfigurationNestedTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/WebAppConfigurationNestedTests.java @@ -40,7 +40,8 @@ import static org.springframework.test.context.NestedTestConfiguration.Enclosing * * @author Sam Brannen * @since 5.0 - * @see ConstructorInjectionNestedTests + * @see ConstructorInjectionTestClassScopedExtensionContextNestedTests + * @see ConstructorInjectionTestMethodScopedExtensionContextNestedTests * @see org.springframework.test.context.junit4.nested.NestedTestsWithSpringRulesTests */ @SpringJUnitWebConfig(Config.class) diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/nested/NestedTestsWithSpringRulesTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/nested/NestedTestsWithSpringRulesTests.java index dbb3e2d40e9..a888148a585 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/nested/NestedTestsWithSpringRulesTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/nested/NestedTestsWithSpringRulesTests.java @@ -37,7 +37,8 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Sam Brannen * @since 5.0 - * @see org.springframework.test.context.junit.jupiter.nested.ContextConfigurationNestedTests + * @see org.springframework.test.context.junit.jupiter.nested.ContextConfigurationTestClassScopedExtensionContextNestedTests + * @see org.springframework.test.context.junit.jupiter.nested.ContextConfigurationTestMethodScopedExtensionContextNestedTests */ @RunWith(HierarchicalContextRunner.class) @ContextConfiguration(classes = TopLevelConfig.class)