Migrate "Failing" test cases to regular tests

This commit replaces the use of EngineTestKit to test scenarios where
bean override could not process the test class. There is no need to
run an actual test for this as:

1. Regular integration tests are already validating that adding the
annotation triggers the processing as part of executing the test.
2. The entry point is a ContextCustomizer that can easily be invoked
in a regular test.

As part of harmonizing this, AbstractTestBeanIntegrationTestCase can
be nested, and BeanOverrideTestSuite serve no purpose. The tests can
be executed in an IDE without any special setup.
This commit is contained in:
Stéphane Nicoll 2024-06-12 11:03:14 +02:00
parent 1318f65223
commit 3f2d9c372d
18 changed files with 433 additions and 896 deletions

View File

@ -18,8 +18,12 @@ package org.springframework.test.context.bean.override;
import java.util.Collections;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import static org.mockito.Mockito.mock;
/**
* Test utilities for {@link BeanOverrideContextCustomizer} that are public so
@ -44,4 +48,17 @@ public abstract class BeanOverrideContextCustomizerTestUtils {
return factory.createContextCustomizer(testClass, Collections.emptyList());
}
/**
* Configure the given {@linkplain ConfigurableApplicationContext application
* context} for the given {@code testClass}.
* @param testClass the test to process
* @param context the context to configure
*/
public static void configureApplicationContext(Class<?> testClass, ConfigurableApplicationContext context) {
ContextCustomizer contextCustomizer = createContextCustomizer(testClass);
if (contextCustomizer != null) {
contextCustomizer.customizeContext(context, mock(MergedContextConfiguration.class));
}
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.ExcludeTags;
import org.junit.platform.suite.api.IncludeClassNamePatterns;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;
/**
* JUnit Platform based test suite for tests that involve bean override support
* in the Spring TestContext Framework.
*
* <p><strong>This suite is only intended to be used manually within an IDE.</strong>
*
* <h3>Logging Configuration</h3>
*
* <p>In order for our log4j2 configuration to be used in an IDE, you must
* set the following system property before running any tests &mdash; for
* example, in <em>Run Configurations</em> in Eclipse.
*
* <pre style="code">
* -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager
* </pre>
*
* @author Sam Brannen
* @since 6.2
*/
@Suite
@IncludeEngines("junit-jupiter")
@SelectPackages("org.springframework.test.context.bean.override")
@IncludeClassNamePatterns(".*Tests$")
@ExcludeTags("failing-test-case")
@ConfigurationParameter(
key = ClassOrderer.DEFAULT_ORDER_PROPERTY_NAME,
value = "org.junit.jupiter.api.ClassOrderer$ClassName"
)
public class BeanOverrideTestSuite {
}

View File

@ -1,99 +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.convention;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig
abstract class AbstractTestBeanIntegrationTestCase {
@TestBean(name = "someBean")
Pojo someBean;
@TestBean(name = "otherBean")
Pojo otherBean;
@TestBean(name = "thirdBean")
Pojo anotherBean;
static Pojo otherBean() {
return new FakePojo("otherBean in superclass");
}
static Pojo thirdBean() {
return new FakePojo("third in superclass");
}
static Pojo commonBeanOverride() {
return new FakePojo("in superclass");
}
interface Pojo {
default String getValue() {
return "Prod";
}
}
static class ProdPojo implements Pojo { }
static class FakePojo implements Pojo {
final String value;
protected FakePojo(String value) {
this.value = value;
}
@Override
public String getValue() {
return this.value;
}
@Override
public String toString() {
return getValue();
}
}
@Configuration
static class Config {
@Bean
Pojo someBean() {
return new ProdPojo();
}
@Bean
Pojo otherBean() {
return new ProdPojo();
}
@Bean
Pojo thirdBean() {
return new ProdPojo();
}
@Bean
Pojo pojo() {
return new ProdPojo();
}
@Bean
Pojo pojo2() {
return new ProdPojo();
}
}
}

View File

@ -1,121 +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.convention;
import java.util.List;
import org.junit.jupiter.api.Test;
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.EngineTestKitUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* {@link TestBean @TestBean} "by type" integration tests for failure scenarios.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class FailingTestBeanByTypeIntegrationTests {
@Test
void zeroCandidates() {
Class<?> testClass = NoMatchingBeansTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to override bean: no bean definitions of type \
%s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@Test
void tooManyCandidates() {
Class<?> testClass = TooManyBeansTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}
@SpringJUnitConfig
static class NoMatchingBeansTestCase {
@TestBean
ExampleService example;
@Test
void test() {
}
static ExampleService example() {
return fail("unexpected override");
}
@Configuration
static class Config {
}
}
@SpringJUnitConfig
static class TooManyBeansTestCase {
@TestBean
ExampleService example;
@Test
void test() {
}
static ExampleService example() {
return fail("unexpected override");
}
@Configuration
static class Config {
@Bean
ExampleService bean1() {
return new RealExampleService("1 Hello");
}
@Bean
ExampleService bean2() {
return new RealExampleService("2 Hello");
}
}
}
}

View File

@ -1,80 +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.convention;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.bean.override.convention.AbstractTestBeanIntegrationTestCase.Pojo;
import org.springframework.test.context.junit.EngineTestKitUtils;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* {@link TestBean @TestBean} inheritance integration tests for failure scenarios.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class FailingTestBeanInheritanceIntegrationTests {
@Test
void failsIfFieldInSupertypeButNoMethod() {
Class<?> testClass = FieldInSupertypeButNoMethodTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
instanceOf(IllegalStateException.class),
message("No static method found named someBean() in %s with return type %s"
.formatted(FieldInSupertypeButNoMethodTestCase.class.getName(), Pojo.class.getName()))));
}
@Test
void failsIfMethod1InSupertypeAndMethod2InType() {
Class<?> testClass = Method1InSupertypeAndMethod2InTypeTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
instanceOf(IllegalStateException.class),
message("Found 2 competing static methods named anotherBean() or thirdBean() in %s with return type %s"
.formatted(Method1InSupertypeAndMethod2InTypeTestCase.class.getName(), Pojo.class.getName()))));
}
static class FieldInSupertypeButNoMethodTestCase extends AbstractTestBeanIntegrationTestCase {
@Test
void test() {
}
}
static class Method1InSupertypeAndMethod2InTypeTestCase extends AbstractTestBeanIntegrationTestCase {
static Pojo someBean() {
return new FakePojo("ignored");
}
static Pojo anotherBean() {
return new FakePojo("sub2");
}
@Test
void test() {
}
}
}

View File

@ -1,179 +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.convention;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.EngineTestKitUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
import static org.springframework.test.context.junit.EngineTestKitUtils.rootCause;
/**
* {@link TestBean @TestBean} integration tests for failure scenarios.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class FailingTestBeanIntegrationTests {
@Test
void testBeanFailingNoFieldNameBean() {
Class<?> testClass = NoOriginalBeanTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to override bean 'noOriginalBean': there is no bean definition \
to replace with that name of type java.lang.String"""))));
}
@Test
void testBeanFailingNoExplicitNameBean() {
Class<?> testClass = BeanDefinitionToOverrideNotPresentTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to override bean 'notPresent': there is no bean definition \
to replace with that name of type java.lang.String"""))));
}
@Test
void testBeanFailingNoImplicitMethod() {
Class<?> testClass = ExplicitOverrideMethodNotPresentTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
instanceOf(IllegalStateException.class),
message("No static method found named notPresent() in %s with return type %s"
.formatted(testClass.getName(), String.class.getName()))));
}
@Test
void testBeanFailingNoExplicitMethod() {
Class<?> testClass = ImplicitOverrideMethodNotPresentTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(instanceOf(IllegalStateException.class),
message("No static method found named field() in %s with return type %s"
.formatted(testClass.getName(), String.class.getName()))));
}
@Test
void testBeanFailingBeanOfWrongType() {
Class<?> testClass = BeanTypeMismatchTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
rootCause(
instanceOf(IllegalStateException.class),
message("""
Unable to override bean 'notString': there is no bean definition to replace \
with that name of type java.lang.String"""))));
}
@SpringJUnitConfig
static class NoOriginalBeanTestCase {
@TestBean(name = "noOriginalBean")
String noOriginalBean;
@Test
void test() {
fail("should fail earlier");
}
static String noOriginalBean() {
return "should be ignored";
}
}
@SpringJUnitConfig
static class BeanDefinitionToOverrideNotPresentTestCase {
@TestBean(name = "notPresent")
String field;
@Test
void test() {
fail("should fail earlier");
}
static String notPresent() {
return "should be ignored";
}
}
@SpringJUnitConfig
static class ExplicitOverrideMethodNotPresentTestCase {
@TestBean(methodName = "notPresent")
String field;
@Test
void test() {
fail("should fail earlier");
}
}
@SpringJUnitConfig
static class ImplicitOverrideMethodNotPresentTestCase {
@TestBean // expects field method
String field;
@Test
void test() {
fail("should fail earlier");
}
}
@SpringJUnitConfig
static class BeanTypeMismatchTestCase {
@TestBean(name = "notString")
String field;
@Test
void test() {
fail("should fail earlier");
}
static String field() {
return "should be ignored";
}
@Configuration
static class Config {
@Bean("notString")
StringBuilder bean1() {
return new StringBuilder("not a String");
}
}
}
}

View File

@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see FailingTestBeanIntegrationTests
*/
@SpringJUnitConfig
public class TestBeanForByNameLookupIntegrationTests {

View File

@ -35,7 +35,6 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see FailingTestBeanByTypeIntegrationTests
*/
@SpringJUnitConfig
class TestBeanForByTypeLookupIntegrationTests {

View File

@ -22,8 +22,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.bean.override.convention.AbstractTestBeanIntegrationTestCase.FakePojo;
import org.springframework.test.context.bean.override.convention.AbstractTestBeanIntegrationTestCase.Pojo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.bean.override.convention.TestBeanForInheritanceIntegrationTests.AbstractTestBeanIntegrationTestCase.FakePojo;
import org.springframework.test.context.bean.override.convention.TestBeanForInheritanceIntegrationTests.AbstractTestBeanIntegrationTestCase.Pojo;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
@ -36,7 +39,6 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see FailingTestBeanInheritanceIntegrationTests
*/
class TestBeanForInheritanceIntegrationTests {
@ -44,10 +46,89 @@ class TestBeanForInheritanceIntegrationTests {
return new FakePojo("in enclosing test class");
}
@SpringJUnitConfig
abstract static class AbstractTestBeanIntegrationTestCase {
@TestBean(name = "someBean")
Pojo someBean;
@TestBean(name = "otherBean")
Pojo otherBean;
@TestBean(name = "thirdBean")
Pojo anotherBean;
static Pojo otherBean() {
return new FakePojo("otherBean in superclass");
}
static Pojo thirdBean() {
return new FakePojo("third in superclass");
}
static Pojo commonBeanOverride() {
return new FakePojo("in superclass");
}
interface Pojo {
default String getValue() {
return "Prod";
}
}
static class ProdPojo implements Pojo { }
static class FakePojo implements Pojo {
final String value;
protected FakePojo(String value) {
this.value = value;
}
@Override
public String getValue() {
return this.value;
}
@Override
public String toString() {
return getValue();
}
}
@Configuration
static class Config {
@Bean
Pojo someBean() {
return new ProdPojo();
}
@Bean
Pojo otherBean() {
return new ProdPojo();
}
@Bean
Pojo thirdBean() {
return new ProdPojo();
}
@Bean
Pojo pojo() {
return new ProdPojo();
}
@Bean
Pojo pojo2() {
return new ProdPojo();
}
}
}
@Nested
@DisplayName("Nested, concrete inherited tests with correct @TestBean setup")
class NestedConcreteTestBeanIntegrationTests extends AbstractTestBeanIntegrationTestCase {
@Autowired
ApplicationContext ctx;
@ -60,7 +141,6 @@ class TestBeanForInheritanceIntegrationTests {
static Pojo someBean() {
return new FakePojo("someBeanOverride");
}
// Hides otherBean() defined in AbstractTestBeanIntegrationTestCase.
static Pojo otherBean() {
return new FakePojo("otherBean in subclass");
@ -83,12 +163,10 @@ class TestBeanForInheritanceIntegrationTests {
assertThat(ctx.getBean("otherBean")).as("applicationContext").hasToString("otherBean in subclass");
assertThat(super.otherBean.getValue()).as("injection point").isEqualTo("otherBean in subclass");
}
@Test
void fieldInNestedClassWithFactoryMethodInEnclosingClass() {
assertThat(ctx.getBean("pojo2")).as("applicationContext").hasToString("in enclosing test class");
assertThat(this.pojo2.getValue()).as("injection point").isEqualTo("in enclosing test class");
}
}
}

View File

@ -0,0 +1,193 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils;
/**
* Tests for {@link TestBean}.
*
* @author Stephane Nicoll
*/
public class TestBeanTests {
@Test
void contextCustomizerCannotBeCreatedWithNoSuchBeanName() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("anotherBean", String.class, () -> "example");
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(FailureByNameLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to override bean 'beanToOverride': there is no bean definition \
to replace with that name of type java.lang.String""");
}
@Test
void contextCustomizerCannotBeCreatedWithNoSuchBeanType() {
GenericApplicationContext context = new GenericApplicationContext();
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(FailureByTypeLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to override bean: no bean definitions of \
type %s (as required by annotated field '%s.example')""".formatted(
String.class.getName(), FailureByTypeLookup.class.getSimpleName()));
}
@Test
void contextCustomizerCannotBeCreatedWithTooManyBeansOfThatType() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", String.class, () -> "example1");
context.registerBean("bean2", String.class, () -> "example2");
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(FailureByTypeLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s""".formatted(
String.class.getName(), FailureByTypeLookup.class.getSimpleName(), List.of("bean1", "bean2")));
}
@Test
void contextCustomizerCannotBeCreatedWithBeanOfWrongType() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("beanToOverride", Integer.class, () -> 42);
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(FailureByNameLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to override bean 'beanToOverride': there is no bean definition \
to replace with that name of type %s""".formatted(
String.class.getName()));
}
@Test
void contextCustomizerCannotBeCreatedWithMissingOverrideMethod() {
GenericApplicationContext context = new GenericApplicationContext();
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> BeanOverrideContextCustomizerTestUtils.configureApplicationContext(
FailureMissingDefaultOverrideMethod.class, context))
.withMessage("No static method found named example() or beanToOverride() in %s with return type %s"
.formatted(FailureMissingDefaultOverrideMethod.class.getName(), String.class.getName()));
}
@Test
void contextCustomizerCannotBeCreatedWithMissingExplicitOverrideMethod() {
GenericApplicationContext context = new GenericApplicationContext();
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> BeanOverrideContextCustomizerTestUtils.configureApplicationContext(
FailureMissingExplicitOverrideMethod.class, context))
.withMessage("No static method found named createExample() in %s with return type %s"
.formatted(FailureMissingExplicitOverrideMethod.class.getName(), String.class.getName()));
}
@Test
void contextCustomizerCannotBeCreatedWithFieldInParentAndMissingOverrideMethod() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("beanToOverride", String.class, () -> "example");
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> BeanOverrideContextCustomizerTestUtils.configureApplicationContext(
FailureOverrideInParentWithoutFactoryMethod.class, context))
.withMessage("No static method found named beanToOverride() in %s with return type %s"
.formatted(FailureOverrideInParentWithoutFactoryMethod.class.getName(), String.class.getName()));
}
@Test
void contextCustomizerCannotBeCreatedWitCompetingOverrideMethods() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean", String.class, () -> "example");
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> BeanOverrideContextCustomizerTestUtils.configureApplicationContext(
FailureCompetingOverrideMethods.class, context))
.withMessage("Found 2 competing static methods named example() or beanToOverride() in %s with return type %s"
.formatted(FailureCompetingOverrideMethods.class.getName(), String.class.getName()));
}
static class FailureByTypeLookup {
@TestBean
private String example;
private static String example() {
throw new IllegalStateException("Should not be called");
}
}
static class FailureByNameLookup {
@TestBean(name = "beanToOverride")
private String example;
private static String example() {
throw new IllegalStateException("Should not be called");
}
}
static class FailureMissingDefaultOverrideMethod {
@TestBean(name = "beanToOverride")
private String example;
// Expected static String example() { ... }
// or static String beanToOverride() { ... }
}
static class FailureMissingExplicitOverrideMethod {
@TestBean(methodName = "createExample")
private String example;
// Expected static String createExample() { ... }
}
abstract static class AbstractByNameLookup {
@TestBean(methodName = "beanToOverride")
protected String beanToOverride;
}
static class FailureOverrideInParentWithoutFactoryMethod extends AbstractByNameLookup {
// No beanToOverride() method
}
abstract static class AbstractCompetingMethods {
@TestBean(name = "beanToOverride")
protected String example;
static String example() {
throw new IllegalStateException("Should not be called");
}
}
static class FailureCompetingOverrideMethods extends AbstractCompetingMethods {
static String beanToOverride() {
throw new IllegalStateException("Should not be called");
}
}
}

View File

@ -1,85 +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.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.EngineTestKitUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* {@link MockitoBean @MockitoBean} "by type" integration tests for failure scenarios.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class FailingMockitoBeanByTypeIntegrationTests {
@Test
void tooManyCandidates() {
Class<?> testClass = TooManyCandidatesTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}
@SpringJUnitConfig
static class TooManyCandidatesTestCase {
@MockitoBean
ExampleService example;
@Test
void test() {
assertThat(example).isNotNull();
}
@Configuration
static class Config {
@Bean
ExampleService bean1() {
return new RealExampleService("1 Hello");
}
@Bean
ExampleService bean2() {
return new RealExampleService("2 Hello");
}
}
}
}

View File

@ -1,113 +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.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.EngineTestKitUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* {@link MockitoSpyBean @MockitoSpyBean} "by type" integration tests for failure scenarios.
*
* @author Sam Brannen
* @since 6.2
*/
class FailingMockitoSpyBeanByTypeIntegrationTests {
@Test
void zeroCandidates() {
Class<?> testClass = ZeroCandidatesTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: found 0 bean instances of \
type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@Test
void tooManyCandidates() {
Class<?> testClass = TooManyCandidatesTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: found 2 bean instances of \
type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}
@SpringJUnitConfig
static class ZeroCandidatesTestCase {
@MockitoSpyBean
ExampleService example;
@Test
void test() {
assertThat(example).isNotNull();
}
@Configuration
static class Config {
}
}
@SpringJUnitConfig
static class TooManyCandidatesTestCase {
@MockitoSpyBean
ExampleService example;
@Test
void test() {
assertThat(example).isNotNull();
}
@Configuration
static class Config {
@Bean
ExampleService bean1() {
return new RealExampleService("1 Hello");
}
@Bean
ExampleService bean2() {
return new RealExampleService("2 Hello");
}
}
}
}

View File

@ -1,86 +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 org.junit.jupiter.api.Test;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.junit.EngineTestKitUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* {@link MockitoSpyBean @MockitoSpyBean} integration tests for failure scenarios.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class FailingMockitoSpyBeanIntegrationTests {
@Test
void failWhenBeanNotPresentByType() {
Class<?> testClass = BeanNotPresentByTypeTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: found 0 bean instances \
of type %s (as required by annotated field '%s.notPresent')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@Test
void failWhenBeanNotPresentByExplicitName() {
Class<?> testClass = BeanNotPresentByExplicitNameTestCase.class;
EngineTestKitUtils.executeTestsForClass(testClass).assertThatEvents().haveExactly(1,
finishedWithFailure(
cause(instanceOf(IllegalStateException.class),
message("""
Unable to override bean 'notPresentAtAll' by wrapping: \
there is no existing bean instance with that name of type %s"""
.formatted(ExampleService.class.getName())))));
}
@SpringJUnitConfig
static class BeanNotPresentByTypeTestCase {
@MockitoSpyBean
ExampleService notPresent;
@Test
void test() {
}
}
@SpringJUnitConfig
static class BeanNotPresentByExplicitNameTestCase {
@MockitoSpyBean(name = "notPresentAtAll")
ExampleService field;
@Test
void test() {
}
}
}

View File

@ -44,7 +44,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see FailingMockitoBeanByTypeIntegrationTests
*/
@SpringJUnitConfig
public class MockitoBeanForByTypeLookupIntegrationTests {

View File

@ -0,0 +1,54 @@
/*
* 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.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils;
/**
* Tests for {@link MockitoBean}.
*
* @author Stephane Nicoll
*/
class MockitoMockBeanTests {
@Test
void contextCustomizerCannotBeCreatedWithTooManyCandidates() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", String.class, () -> "example1");
context.registerBean("bean2", String.class, () -> "example2");
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(ByTypeSingleLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s""".formatted(
String.class.getName(), ByTypeSingleLookup.class.getSimpleName(), List.of("bean1", "bean2")));
}
static class ByTypeSingleLookup {
@MockitoBean
String example;
}
}

View File

@ -41,7 +41,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see FailingMockitoSpyBeanByTypeIntegrationTests
*/
@SpringJUnitConfig
public class MockitoSpyBeanForByTypeLookupIntegrationTests {

View File

@ -0,0 +1,85 @@
/*
* 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.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils;
/**
* Tests for {@link MockitoSpyBean}.
*
* @author Stephane Nicoll
*/
class MockitoSpyBeanTests {
@Test
void contextCustomizerCannotBeCreatedWithNoSuchBeanName() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("present", String.class, () -> "example");
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(ByNameSingleLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to override bean 'beanToSpy' by wrapping: \
there is no existing bean instance with that name of type %s""".formatted(
String.class.getName()));
}
@Test
void contextCustomizerCannotBeCreatedWithNoSuchBeanType() {
GenericApplicationContext context = new GenericApplicationContext();
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(ByTypeSingleLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to select a bean to override by wrapping: found 0 bean instances of \
type %s (as required by annotated field '%s.example')""".formatted(
String.class.getName(), ByTypeSingleLookup.class.getSimpleName()));
}
@Test
void contextCustomizerCannotBeCreatedWithTooManyBeansOfThatType() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", String.class, () -> "example1");
context.registerBean("bean2", String.class, () -> "example2");
BeanOverrideContextCustomizerTestUtils.configureApplicationContext(ByTypeSingleLookup.class, context);
Assertions.assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessage("""
Unable to select a bean to override by wrapping: found 2 bean instances \
of type %s (as required by annotated field '%s.example'): %s""".formatted(
String.class.getName(), ByTypeSingleLookup.class.getSimpleName(), List.of("bean1", "bean2")));
}
static class ByTypeSingleLookup {
@MockitoSpyBean
String example;
}
static class ByNameSingleLookup {
@MockitoSpyBean(name = "beanToSpy")
String example;
}
}

View File

@ -1,67 +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.junit;
import java.util.Arrays;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Events;
import org.springframework.core.NestedExceptionUtils;
import static java.util.stream.Collectors.toList;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
/**
* Utilities for tests that use JUnit's {@link EngineTestKit}.
*
* @author Sam Brannen
* @since 6.2
*/
public class EngineTestKitUtils {
public static Events executeTestsForClass(Class<?> testClass) {
return EngineTestKit.engine("junit-jupiter")
.selectors(selectClass(testClass))
.execute()
.allEvents();
}
/**
* Create a new {@link Condition} that matches if and only if a
* {@link Throwable}'s root {@linkplain Throwable#getCause() cause} matches
* all supplied conditions.
*/
@SafeVarargs
@SuppressWarnings("varargs")
public static Condition<Throwable> rootCause(Condition<Throwable>... conditions) {
List<Condition<Throwable>> list = Arrays.stream(conditions)
.map(EngineTestKitUtils::rootCause)
.collect(toList());
return Assertions.allOf(list);
}
private static Condition<Throwable> rootCause(Condition<Throwable> condition) {
return new Condition<>(throwable -> condition.matches(NestedExceptionUtils.getRootCause(throwable)),
"throwable root cause matches %s", condition);
}
}