Skip to content
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

Upgrading from 1.4.0 to 1.4.1 breaks @SpringBootTest's ability to find nested config in abstract base class #7031

Closed
btiernay opened this issue Sep 27, 2016 · 11 comments

Comments

@btiernay
Copy link

In 1.4.0, I had the following working base class for integration tests:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT)
public abstract class BaseIntegrationTest {

  @Autowired
  protected TestRestTemplate restTemplate;

  @Configuration
  static class Config {

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
      return new RestTemplateBuilder()
          .requestFactory(SimpleClientHttpRequestFactory.class)
          .basicAuthorization("admin", "adminspasswd");
    }

  }

}

Upon upgrading to 1.4.1 I got exceptions related to not finding the RestTemplateBuilder bean:

Caused by: java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients
    at org.springframework.http.client.HttpComponentsClientHttpRequestFactory.<init>(HttpComponentsClientHttpRequestFactory.java:88)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at java.lang.Class.newInstance(Class.java:438)
    at org.springframework.beans.BeanUtils.instantiate(BeanUtils.java:77)
    at org.springframework.boot.web.client.RestTemplateBuilder.detectRequestFactory(RestTemplateBuilder.java:583)
    at org.springframework.boot.web.client.RestTemplateBuilder.configureRequestFactory(RestTemplateBuilder.java:547)
    at org.springframework.boot.web.client.RestTemplateBuilder.configure(RestTemplateBuilder.java:515)
    at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:503)
    at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:489)
    at org.springframework.boot.test.context.SpringBootTestContextCustomizer$TestRestTemplateFactory.setApplicationContext(SpringBootTestContextCustomizer.java:99)
    at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:121)
    at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:97)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1575)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
    ... 39 common frames omitted

To fix the problem, I needed to explicitly define my config classes :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT, classes = { Config.class, ServerMain.class })
...
}

Has anything changed in the test bean resolution process in 1.4.1 that may have caused this behavior?

@btiernay btiernay changed the title Upgrading from 1.4.0 to 1.4.1 breaks @SpringBootTest's ability to find nested config Upgrading from 1.4.0 to 1.4.1 breaks @SpringBootTest's ability to find nested config in abstract base class Sep 27, 2016
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 27, 2016
@snicoll
Copy link
Member

snicoll commented Sep 27, 2016

Could be introduced by #6768 though that test looks a bit odd to me. If you want Config to be loaded, you must specify it as explained in the documentation.

Also

If you want to customize the primary configuration, you can use a nested @TestConfiguration class. Unlike a nested @configuration class which would be used instead of a your application’s primary configuration, a nested @TestConfiguration class will be used in addition to your application’s primary configuration.

@btiernay Can you please try with @TestConfiguration. Your setup probably worked as a side effect and that fix revealed the intended beahviour.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 27, 2016
@btiernay
Copy link
Author

Yes it does seem related. However adding the following to the base class didn't seem to help:

  @Test
  public void testIgnore() {}

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Sep 27, 2016
@snicoll
Copy link
Member

snicoll commented Sep 27, 2016

That's was the previous situation and that was a bug we fixed. Please use @TestConfiguration as referenced in the doc. I am tempted to close this but I'd like you to confirm first it works that way.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Sep 27, 2016
@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Sep 27, 2016
@btiernay
Copy link
Author

btiernay commented Sep 27, 2016

Adding TestConfiguration to the nested configuration this didn't fix the problem and still results in the inner config not being found. Please see https://github.com/btiernay/spring-boot-7031 for a minimal example showing this behavior.

Note that moving Config to AppTest fixes the issue, but I don't think that this requirement is clear in the current docs.

What is the expected behavior in this case?

@philwebb
Copy link
Member

@btiernay Does changing your base class to use org.springframework.boot.test.context.TestConfiguration fix your issue?

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT)
public abstract class BaseIntegrationTest {

  @Autowired
  protected TestRestTemplate restTemplate;

  @TestConfiguration
  static class Config {

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
      return new RestTemplateBuilder()
          .requestFactory(SimpleClientHttpRequestFactory.class)
          .basicAuthorization("admin", "adminspasswd");
    }

  }

}

@philwebb philwebb added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Oct 11, 2016
@btiernay
Copy link
Author

No, please see referenced project here: https://github.com/btiernay/spring-boot-7031

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Oct 12, 2016
@wilkinsona
Copy link
Member

wilkinsona commented Oct 13, 2016

Unlike a nested @Configuration class which would be used instead of your application’s primary configuration

This is only true if the @Configuration class is nested in the concrete test class. If it's nested in a super-class (AbstractTest in @btiernay's example), it's ignored and the primary configuration is used.

This behaviour is due to AnnotationConfigContextLoaderUtils.detectDefaultConfigurationClasses(Class<?>) which doesn't consider the test class's type hierarchy.

The javadoc states that "the returned class array will contain all static nested classes of the supplied class that meet the requirements for @Configuration class implementations"

@sbrannen Is it intentional that nested configuration classes in a test's super class(es) are not found?

@sbrannen
Copy link
Member

Yes, this is intentional and perfectly in line with default configuration detection for XML and Groovy config files. In other words, the behavior is consistent.

If a user wants configuration to be inherited, the corresponding superclass must be configured to declare the configuration (either implicitly or explicitly). Furthermore, inheritance of configuration can be controlled via the inheritLocations attribute in @ContextConfiguration.

@sbrannen
Copy link
Member

Now, having said that, if a superclass is properly configured to use a static nested @ContextConfiguration class, then that should be discovered via the normal semantics of the test context bootstrapping mechanism when building up the MergedContextConfiguration for the test class hierarchy.

So if that isn't the case, then that sounds like it might be some sort of bug.

@wilkinsona
Copy link
Member

Thanks, @sbrannen. In this case I believe that the superclass was not properly configured as it made no reference to the nested configuration. With a change to the superclass so that it references its nested configuration class things behave as desired.

@wilkinsona
Copy link
Member

wilkinsona commented Oct 13, 2016

@btiernay As indicated by @sbrannen above, your superclass needs to reference the nested class. You can do so using @ContextConfiguration or the classes attribute of @SpringBootTest:

diff --git a/src/test/java/btiernay/AbstractTest.java b/src/test/java/btiernay/AbstractTest.java
index 83e8ec7..27bd102 100644
--- a/src/test/java/btiernay/AbstractTest.java
+++ b/src/test/java/btiernay/AbstractTest.java
@@ -5,8 +5,10 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.test.context.junit4.SpringRunner;

+import btiernay.AbstractTest.Config;
+
 @RunWith(SpringRunner.class)
-@SpringBootTest
+@SpringBootTest(classes=Config.class)
 public abstract class AbstractTest {

        @TestConfiguration

The change from 1.4.0 to 1.4.1 aligns Boot's behaviour with Spring's test framework so I think we should keep the new behaviour as that consistency is an improvement.

@wilkinsona wilkinsona removed the status: feedback-provided Feedback has been provided label Oct 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants