Spring boot not resolving custom properties from application.properties during integration tests

15,464

Solution 1

The issue was actually with the following line of code:

@SpringApplicationConfiguration(classes = {TestEnd2EndIntegrationConfiguration.class})

TestEnd2EndIntegrationConfiguration is not a Spring Boot Application with a main and most importantly it is not annotated with a @EnableAutoConfiguration annotation.

I changed to:

@Configuration
@EnableAutoConfiguration(exclude = {
        ManagementWebSecurityAutoConfiguration.class,
        ...
        MailSenderAutoConfiguration.class
})
@ComponentScan(basePackages = {"com.bignibou.configuration", "com.bignibou.it.configuration"})
public class TestApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(TestApplication.class, args);
    }
}

and my abstract test class:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {TestApplication.class})//Notice the change
@WebAppConfiguration
@ActiveProfiles(Profiles.TEST)
@Transactional
public abstract class AbstractIntegrationTest {

}

Now the tests run as a spring boot application and custom properties in application.properties are also resolved.

Solution 2

You need 3 things to get your properties to be autowired in your integration test classes:

  1. The PropertySource annotation. It looks like you're missing that. You can add something like this to your Abstract Integration Test class:
@PropertySource(value= {"classpath:*.properties"})
  1. The property file must be in your classpath. I recommend adding a separate folder for test and integration test properties. Create an integration-test-resources directory within src/main and copy your property files into there, then configure your build manager to add them as classpath resources.

Here's an example how to do that in Gradle:

sourceSets {
  integrationTest {
     resources {
       srcDir "integration-test-resources"
     }
  }
}
  1. Annotate your property with @Value. Looks like you are already doing this.

Solution 3

I believe you should use spring.jpa.hibernate.dll-auto if you want to place your Hibernate properties in application-${profile}.properties, i.e. Spring properties file.

Likewise, spring.jpa.database-platform=org.hibernate.dialect.H2Dialect should be used.

I use Spring Boot with such configuration and I never had a need for any @PropertySource annotations.

Share:
15,464
balteo
Author by

balteo

Updated on June 18, 2022

Comments

  • balteo
    balteo almost 2 years

    My Spring Boot test configuration is as follows:

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = {TestEnd2EndIntegrationConfiguration.class})
    @WebAppConfiguration
    @ActiveProfiles(Profiles.TEST)//=="test"
    @Transactional
    public abstract class AbstractEnd2EndIntegrationTest {
    
    }
    

    Then from application-test.properties I have the following custom properties:

    database.hibernate.dialect=org.hibernate.dialect.H2Dialect
    database.hibernate.hbm2ddl.auto=create
    database.hibernate.logSqlInfo=true
    

    Which I use as follows:

    @Value("${database.hibernate.dialect}")
    private String hibernateDialect;
    

    However, the custom properties are not resolved and I get exceptions such as:

    com.bignibou.it.controller.signup.SignupRestControllerTest > shouldRejectUnavailableEmailAddress FAILED
        java.lang.IllegalStateException
            Caused by: org.springframework.beans.factory.BeanCreationException
                Caused by: org.hibernate.boot.registry.selector.spi.StrategySelectionException
    

    edit:

    Here is my gradle configuration:

     sourceSets {
    
            main {
                output.resourcesDir = output.classesDir
            }
    
            integrationTest {
                java.srcDirs = ['src/it/java']
                resources.srcDirs = ['src/it/resources', 'src/main/resources']
                compileClasspath = sourceSets.main.output + configurations.testRuntime
                runtimeClasspath = output + compileClasspath
            }
        }
    
       task integrationTest(type: Test) {
            description "Run the integration tests."
            testClassesDir = sourceSets.integrationTest.output.classesDir
            classpath = sourceSets.integrationTest.runtimeClasspath
            reports.html.destination = file("$reports.html.destination/integration")
            reports.junitXml.destination = file("$reports.junitXml.destination/integration")
        }
    

    edit 2:

    When I run the tests from the IDE (intellij), I get a different error:

    ava:342)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:273)
        at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:103)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
        ... 36 more
    
    java.lang.IllegalStateException: Failed to load ApplicationContext
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
        at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
        at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:183)
        at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:123)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:228)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:217)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:276)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:278)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:236)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
        at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
        at org.junit.runners.Suite.runChild(Suite.java:128)
        at org.junit.runners.Suite.runChild(Suite.java:27)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
    Caused by: java.lang.ClassCastException: org.springframework.boot.context.config.ConfigFileApplicationListener$ConfigurationPropertySources cannot be cast to org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor$ConfigurationPropertySources
        at org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor$ConfigurationPropertySources.finishAndRelocate(ConfigFileEnvironmentPostProcessor.java:528)
        at org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor$PropertySourceOrderingPostProcessor.reorderSources(ConfigFileEnvironmentPostProcessor.java:239)
        at org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor$PropertySourceOrderingPostProcessor.postProcessBeanFactory(ConfigFileEnvironmentPostProcessor.java:235)
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:284)
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:131)
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:673)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:519)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:667)
        at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:342)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:273)
        at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:103)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
        ... 36 more
    

    edit 3:

    Here is the detailed cause from the Gradle integrationTest

    Caused by: org.hibernate.boot.registry.selector.spi.StrategySelectionException: Unable to resolve name [${database.hibernate.ejb.naming_strategy}] as strategy [org.hibernate.cfg.NamingStrategy]
        at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:128)
        at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveDefaultableStrategy(StrategySelectorImpl.java:155)
        at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:136)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.processProperties(EntityManagerFactoryBuilderImpl.java:925)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:840)
        at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:152)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
    

    edit 4: from a debug within: EntityManagerFactoryBuilderImpl:

    configurationValues = {HashMap@4101}  size = 14
     0 = {HashMap$Node@4092} "hibernate.format_sql" -> "${database.hibernate.logSqlInfo}"
     1 = {HashMap$Node@4096} "hibernate.use_sql_comments" -> "${database.hibernate.logSqlInfo}"
     2 = {HashMap$Node@4107} "javax.persistence.nonJtaDataSource" -> 
     3 = {HashMap$Node@4108} "hibernate.hbm2ddl.auto" -> "${database.hibernate.hbm2ddl.auto}"
     4 = {HashMap$Node@4109} "javax.persistence.sharedCache.mode" -> "ENABLE_SELECTIVE"
     5 = {HashMap$Node@4110} "hibernate.dialect" -> "${database.hibernate.dialect}"
     6 = {HashMap$Node@4111} "hibernate.ejb.naming_strategy" -> "${database.hibernate.ejb.naming_strategy}"
     7 = {HashMap$Node@4112} "hibernate.cache.use_second_level_cache" -> "${database.hibernate.cache.use_second_level_cache}"
     8 = {HashMap$Node@4113} "hibernate.ejb.persistenceUnitName" -> "default"
     9 = {HashMap$Node@4114} "hibernate.connection.charSet" -> "${database.hibernate.connection.charSet}"
     10 = {HashMap$Node@4115} "hibernate.show_sql" -> "${database.hibernate.logSqlInfo}"
     11 = {HashMap$Node@4116} "hibernate.cache.region.factory_class" -> "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"
     12 = {HashMap$Node@4117} "hibernate.generate_statistics" -> "${database.hibernate.generate_statistics}"
     13 = {HashMap$Node@4118} "javax.persistence.validation.mode" -> "AUTO"
    

    One can see that the properties are not expanded... Why is that?

    • Sam Brannen
      Sam Brannen over 8 years
      Where does application-test.properties reside (e.g., src/test/resources)?
    • Sam Brannen
      Sam Brannen over 8 years
      How are you executing the tests: Eclipse, IDEA, Maven, Gradle?
    • balteo
      balteo over 8 years
      Hi Sam! application-test.properties is located in src/main/resources as well as application.properties that is not picked up by my spring boot tests either. I use ./gradlew integrationTest with a custom integrationTest task.
    • Sam Brannen
      Sam Brannen over 8 years
      Does it work if you run the test in the IDE?
    • balteo
      balteo over 8 years
      No it does not work either. I have edited my post (see edit 2 above).
    • balteo
      balteo over 8 years
      Any advice or help to debug this issue would be very welcome.
    • balteo
      balteo over 8 years
      I have edited my post with relevant information.
    • Lucas Ross
      Lucas Ross over 8 years
      org.springframework.boot.context.config.ConfigFileApplicatio‌​nListener$Configurat‌​ionPropertySources cannot be cast to org.springframework.boot.context.config.ConfigFileEnvironmen‌​tPostProcessor$Confi‌​gurationPropertySour‌​ces is strange because there is no ConfigFileApplicationListener in spring-boot 1.3. Maybe you need to run a gradle clean.
    • balteo
      balteo over 8 years
      Yes That's the error I got from the IDE. I don t get that from gradle.
  • Sam Brannen
    Sam Brannen over 8 years
    I assumed your TestEnd2EndIntegrationConfiguration class was annotated with @EnableAutoConfiguration. Sorry I didn't inquire in that regard, but I'm glad you figured it out! ;)
  • Sam Brannen
    Sam Brannen over 8 years
    FYI: TestApplication does not need a main method. That's only necessary for launching the app from the command line.
  • balteo
    balteo over 8 years
    OK. Thanks for the info. It actually makes more sense without the main!
  • cosbor11
    cosbor11 over 8 years
    @balteo, you do not need @SpringApplicationConfiguration for your integration tests, you can simply use @ContextConfiguration and specify all of your configuration classes like this: @ContextConfiguration(classes = {Application.class, SecurityConfig.class, MVCConfig.class, PersistenceConfig.class})