Skip to content

@TestBean factory method resolution is incorrect within class hierarchy #34204

Closed
@sbrannen

Description

@sbrannen

Overview

While working on #33925 and #34194, I noticed inconsistencies and peculiarities in the search algorithm for @TestBean factory methods.

Specifically, for a @TestBean field defined in a top-level class, the search for a factory method begins with the current test class (which may be a subclass of or or a @Nested class defined in the class in which the @TestBean field is declared). Whereas, for a @TestBean field defined in a @Nested test class, the search for a factory method begins with the @Nested test class.

The above leads to inconsistent factory method discovery for @TestBean factory methods.

In addition, the algorithm used for top-level classes results in "duplicate bean override" failures for cases which are clearly not duplicates but rather an "override of an override". In other words, a @TestBean declaration in a subclass (which resolves to a factory method in the subclass) currently cannot override a seemingly identical @TestBean declaration in a superclass (which resolves to a factory method in the superclass), which some might consider a bug.

Example

@SpringJUnitConfig
class BaseTests {

	@TestBean
	String enigma;

	static String enigma() {
		return "enigma in superclass";
	}

	@Test
	void test() {
		assertThat(enigma).isEqualTo("enigma in superclass");
	}

	@Nested
	class NestedTests {

		@TestBean
		String enigma;

		static String enigma() {
			return "enigma in nested class";
		}

		@Test
		void test() {
			assertThat(enigma).isEqualTo("enigma in nested class");
		}
	}

	@Configuration
	static class Config {

		@Bean
		String enigma() {
			return "enigma in @Configuration class";
		}
	}
}
class ExtendedTests extends BaseTests {

	@TestBean
	String enigma;

	static String enigma() {
		return "enigma in subclass";
	}

	@Test
	@Override
	void test() {
		assertThat(enigma).isEqualTo("enigma in subclass");
	}
}
  • BaseTests passes as expected.
  • BaseTests.NestedTests currently passes because of the aforementioned inconsistent search algorithm.
  • ExtendedTests fails with the following error.
java.lang.IllegalStateException: Duplicate BeanOverrideHandler discovered in test class example.ExtendedTests: [TestBeanOverrideHandler@2dc3271b field = java.lang.String example.ExtendedTests.enigma, beanType = java.lang.String, beanName = [null], strategy = REPLACE_OR_CREATE, factoryMethod = enigma@ExtendedTests]

The cause of the exception is due to the fact that two @TestBean override handlers resolve the same factory method enigma@ExtendedTests; however, one might expect those to resolve to enigma@BaseTests and enigma@ExtendedTests (i.e., two distinct factory methods).

Proposal

  • Resolve @TestBean factory methods consistently, beginning the search with the declaring class of the @TestBean field.

One downside to the proposal is that @TestBean factory methods would no longer be able to be resolved lazily in subclasses (somewhat like "late binding" to a concrete method), which would be a change in behavior.

Another option is to make the factory method search algorithm for @Nested test classes align with the current semantics for top-level test classes.

Related Issues

Metadata

Metadata

Assignees

Labels

in: testIssues in the test moduletype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions