Skip to content

@Configuration class is not detected as default in composed annotation in the TestContext framework [SPR-11641] #16264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Apr 1, 2014 · 12 comments
Assignees
Labels
in: test Issues in the test module status: declined A suggestion or change that we don't feel we should currently apply

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Apr 1, 2014

Janne Valkealahti opened SPR-11641 and commented

I have below classes in my tests. This used to work with 4.0.2 but now fails with 4.0.3. I don't know if this is somehow related to work for #16081 and other jira's linked to it.

ComposedAnnotationTests.java

@RunWith(SpringJUnit4ClassRunner.class)
@CustomMiniYarnClusterTest
public class ComposedAnnotationTests {

	@Autowired
	private ApplicationContext ctx;

	@Resource(name = "yarnConfiguration")
	Configuration configuration;

	@Test
	public void testLoaderAndConfig() {
		assertNotNull(ctx);
		assertTrue(ctx.containsBean("yarnCluster"));
		assertTrue(ctx.containsBean("yarnConfiguration"));
		assertTrue(ctx.containsBean("myCustomBean"));
		Configuration config = (Configuration) ctx.getBean("yarnConfiguration");
		assertNotNull(config);
	}

}

CustomMiniYarnClusterTest

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ContextConfiguration(loader=YarnDelegatingSmartContextLoader.class)
@MiniYarnCluster
public @interface CustomMiniYarnClusterTest {

	@Configuration
	public static class Config {
		@Bean
		public String myCustomBean() {
			return "myCustomBean";
		}
	}

}

This fails with message:

java.lang.IllegalStateException: Neither YarnClusterInjectingXmlContextLoader nor YarnClusterInjectingAnnotationConfigContextLoader was able to detect defaults, and no ApplicationContextInitializers were declared for context configuration [ContextConfigurationAttributes@2eedd06a declaringClass = 'org.springframework.yarn.test.context.YarnClusterInjectingMiniYarnClusterTestContextLoaderTests', locations = '{}', classes = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.yarn.test.context.YarnDelegatingSmartContextLoader']
	at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.processContextConfiguration(AbstractDelegatingSmartContextLoader.java:200)
	at org.springframework.test.context.ContextLoaderUtils.buildMergedContextConfiguration(ContextLoaderUtils.java:703)
	at org.springframework.test.context.ContextLoaderUtils.buildMergedContextConfiguration(ContextLoaderUtils.java:656)
	at org.springframework.test.context.DefaultTestContext.<init>(DefaultTestContext.java:93)
	at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:119)

Affects: 4.0.3

Issue Links:

Referenced from: commits spring-attic/spring-hadoop@0ef5c92

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 1, 2014

Sam Brannen commented

Yes, this is a direct result of #16081.

When @ContextConfiguration is used as a meta-annotation, default @Configuration classes are now detected in the annotated test class, not within the composed annotation.

To make your code work again, please declare your @Configuration class explicitly within your composed annotation as follows.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ContextConfiguration(
    classes = CustomMiniYarnClusterTest.Config.class,
    loader = YarnDelegatingSmartContextLoader.class
)
@MiniYarnCluster
public @interface CustomMiniYarnClusterTest {

	@Configuration
	public static class Config {
		@Bean
		public String myCustomBean() {
			return "myCustomBean";
		}
	}
}

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Resolving this issue as Won't Fix since the effect is intentional.

@spring-projects-issues
Copy link
Collaborator Author

Janne Valkealahti commented

Problem is that we created a composed annotation MiniYarnClusterTest based on what worked with 4.0.2 and this annotation is part of our hadoop testing framework in spring hadoop. It's purpose was to ease testing without having a need to create dummy empty @Configuration.

@MiniYarnClusterTest then gave user all he needed in a test class. All our public samples are based on this fact that it worked with 4.0.2.

Is there any workaround other than again start adding empty @Configuration classes?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 1, 2014

Janne Valkealahti commented

In #16081 you wrote:

The behavior you are witnessing is actually by design.
Historically, the look-up mechanism for default resource locations and configuration classes has been to search for the presence of defaults relative to the declaration of @ContextConfiguration. Prior to Spring 4.0 this meant relative to the test class that is directly annotated with either @ContextConfiguration or @ContextHierarchy. However, with the support for using these annotations as meta-annotations that was introduced in Spring 4.0, the semantics for "relative" changed to mean relative to the declaring class, where "class" is one of the following:

Was there something I totally misunderstood about a usage of composed annotations with @Configuration in Spring 4.0?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Problem is that we created a composed annotation MiniYarnClusterTest based on what worked with 4.0.2 and this annotation is part of our hadoop testing framework in spring hadoop.

I have to apologize for the change in a point release, but it was deemed the appropriate solution.

It's purpose was to ease testing without having a need to create dummy empty @Configuration.

@MiniYarnClusterTest then gave user all he needed in a test class. All our public samples are based on this fact that it worked with 4.0.2.

Is there any workaround other than again start adding empty @Configuration classes?

Did you try the change I proposed in my previous comment (i.e., classes = CustomMiniYarnClusterTest.Config.class)?

You won't need any empty @Configuration classes if you go that route.

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 1, 2014

Sam Brannen commented

Was there something I totally misunderstood about a usage of composed annotations with @Configuration in Spring 4.0?

No, you understood it correctly. However, we decided that the change in #16081 was necessary based on the following rationale.

Without that change it is impossible to create a custom composed annotation that is meta-annotated with @ContextConfiguration where developers can rely on detection of default resource locations or annotated configuration classes for their own test classes.

As the developer of such a composed annotation, you are not limited by this change: you can still have a default @Configuration class, but you now have to be explicit about it instead of relying on default detection within the composed annotation.

Does that clarify things for you?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

I just read the Javadoc for MiniYarnClusterTest in GitHub.

Why would you make people duplicate your entire composed annotation?

That completely defeats the purpose.

Instead, you should make the attributes of @ContextConfiguration (or any other meta-annotations in use on the composed annotation) configurable by redefining them, potentially with custom defaults (like for classes below) -- for example:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ContextConfiguration(loader = YarnDelegatingSmartContextLoader.class)
@MiniYarnCluster
public @interface MiniYarnClusterTest {

	@Configuration
	public static class Config {
	}


	/**
	 * @see ContextConfiguration#locations()
	 */
	String[] locations() default {};

	/**
	 * Defaults to empty configuration.
	 * @see ContextConfiguration#classes()
	 */
	Class<?>[] classes() default { MiniYarnClusterTest.Config.class };

	/**
	 * @see ContextConfiguration#initializers()
	 */
	Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers() default {};

	/**
	 * @see ContextConfiguration#inheritLocations()
	 */
	boolean inheritLocations() default true;

	/**
	 * @see ContextConfiguration#inheritInitializers()
	 */
	boolean inheritInitializers() default true;

	/**
	 * @see ContextConfiguration#name()
	 */
	String name() default "";
}

Note that the loader would not be overridable in such a constellation.

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

Janne Valkealahti commented

Ah, gotcha!!! I first totally misinterpreted what you wrote to that example and then asked if there's a workaround. That is the workaround I was looking for, so sorry for nitpicking it any further.

Yes, just tried it and it works for both 4.0.2 and 4.0.3 and stays same from end-user point of view. We'll just make these changes for Spring Hadoop RC2.

Thanks!

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 1, 2014

Sam Brannen commented

Also, the claim in the following Javadoc from MiniYarnClusterTest is no longer accurate due to #16081. ;)

/**
* ...
* Drawback of using a composed annotation like this is that the
* &#064;{@link Configuration} is then applied from an annotation
* class itself and user can't no longer add a static &#064;{@link Configuration}
* class in a test class itself and expect Spring to pick it up from
* there which is a normal behaviour in Spring testing support.
* ...
*/

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Yes, just tried it and it works for both 4.0.2 and 4.0.3 and stays same from end-user point of view. We'll just make these changes for Spring Hadoop RC2.

Great! I'm glad to hear it, especially the "stays same from end-user point of view" part since that was my intention. :)

@spring-projects-issues
Copy link
Collaborator Author

Janne Valkealahti commented

Thanks for clarification for the correct concept if end-user wants/needs to create a custom composed annotation. The example you just posted indeed looks much better. Being honest I didn't even think we could write it like that to allow much easier way to extend it. :)

Cheers,
Janne

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 1, 2014

Sam Brannen commented

Thanks for clarification for the correct concept if end-user wants/needs to create a custom composed annotation.

You're welcome!

The example you just posted indeed looks much better.

I agree. ;)

Being honest I didn't even think we could write it like that to allow much easier way to extend it.

Yeah, I fear that most people don't yet know all of the possibilities with composed annotations and meta-annotation attribute overrides. That's why we are making it an important topic for Spring Framework 4.1. See #16136 and its subtasks for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

2 participants