@TestConfiguration annotation doesn't override bean from production configuration

502 views Asked by At

Based on

I've created test example where I want to replace production bean in tests.

Production configuration requires special properties in application.yaml(for demo purpose)

package com.example.demo.app
...

@Configuration
@EnableConfigurationProperties(MyProperties::class)
open class MyConfiguration(
    private val myProperties: MyProperties
) {
    @Bean
    open fun propertiesHolder(): PropertiesHolder {
        return PropertiesHolder(arrayOf(myProperties.prop1, myProperties.prop2, myProperties.prop3))
    }
}

In tests

package com.example.demo.app
...

@TestConfiguration
open class MyConfiguration {
    @Bean
    open fun propertiesHolder(): PropertiesHolder = mockk()
}

And test looks like this:

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    classes = [MyConfiguration::class]
)
class DemoControllerTest {
    @Autowired
    lateinit var testRestTemplate: TestRestTemplate

    @Test
    fun getItems_success() {

    }
}

This works. But if I change name of package or name of class

@TestConfiguration
    open class MyConfigurationNewName {

In both cases I see the error:

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'propertiesHolder', defined in com.example.demo.app.MyConfigurationNewName, could not be registered. A bean with that name has already been defined in class path resource [com/example/demo/app/MyConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

I also tried to use @Import annotation but result is the same:

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
@Import(MyConfigurationNewName::class)

I tried to follow the error message

spring:
  main:
    allow-bean-definition-overriding: true

But another error arised after that:

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'my.props' to com.example.demo.app.MyProperties:

    Reason: java.lang.NullPointerException: Parameter specified as non-null is null: method com.example.demo.app.MyProperties.<init>, parameter prop1

Action:

Update your application's configuration

I am out of ideas. Could you please help ?

The whole codebase could be found here:

https://github.com/gredwhite/springboottestconfigissue_demo

Branch with issue is: https://github.com/gredwhite/springboottestconfigissue_demo/tree/bug/config_issue

P.S.

I tried spring boot versions

  • 3.0.1
  • 3.1.0
  • 3.1.4
1

There are 1 answers

10
Phil Webb On

The problem you are having is essentially a duplicate of Override default Spring-Boot application.properties settings in Junit Test.

You have defined an application.yml file at src/main/resources and also at src/test/resources. This means when your tests run there is only a single file available on the classpath (the one in src/test/resources).

When MyConfiguration is processed the @EnableConfigurationProperties(MyProperties::class) annotation is respected and the MyProperties data class is bound. This class requires non-null values but since your test application.yml file does not define my.property values you get the following exception:

Reason: java.lang.NullPointerException: Parameter specified as non-null is null: method com.example.demo.app.MyProperties.<init>, parameter prop1

The test passes if I update your code by renaming src/test/resources/application.yml to src/test/resources/application-test.yml and adding properties = ["spring.config.additional-location=classpath:application-test.yml"] to @SpringBootTest annotation.