Replace @Value property within @Configuration during Spring Boot test

16,127

Solution 1

You can use constructor injection in production cases, which allows it to set the configuration manually:

@Configuration
public class SomeConfiguration {

    private final String someUsername;
    private final String somePassword;

    @Autowired
    public SomeConfiguration(@Value("${some.username}") String someUsername,
       @Value("${some.password}") String somePassword) {
       this.someUsername = someUsername;
       this.somePassword = somePassword;
    }
...
)
}

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {

    private SomeConfiguration config;

    @Before
    public init() {
      config = new SomeConfiguration("foo", "bar");
    }
}

Solution 2

You can override properties directly in the @SpringBootTest annotation using the properties parameter:

@SpringBootTest(properties = {"some.username=user", "some.password=pwd"},
                webEnvironment = SpringBootTest.WebEnvironment.NONE)

Solution 3

You can use @TestPropertySource

@TestPropertySource(
    properties = {
        "some.username=validate",
        "some.password=false"
    }
)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
    //...
}
Share:
16,127
Markus Ratzer
Author by

Markus Ratzer

I'm a upper (I guess?) mid-range Java developer, mainly focused on back end technologies.

Updated on June 17, 2022

Comments

  • Markus Ratzer
    Markus Ratzer almost 2 years

    Scenario

    I've got a Spring Boot application with a @Configuration annotated Spring configuration class which contains some @Value annotated fields. For testing I want to replace these field values with custom test values.

    Unfortunately these test values cannot be overridden using a simple properties file, (String) constants or similar, instead I must use some custom written property resolving Java class (e.g. TargetProperties.getProperty("some.username")).

    The problem I have is that when I add a custom PropertySource to the ConfigurableEnvironment within my test configuration, it's already too late because this PropertySource will be added after the e.g. RestTemplate has been created.

    Question

    How can I override @Value annotated fields within a @Configuration class with properties obtained programmatically via custom Java code before anything else gets initialized?

    Code

    Production Configuration Class

    @Configuration
    public class SomeConfiguration {
    
        @Value("${some.username}")
        private String someUsername;
    
        @Value("${some.password}")
        private String somePassword;
    
        @Bean
        public RestTemplate someRestTemplate() {
            RestTemplate restTemplate = new RestTemplate();
    
            restTemplate.getInterceptors().add(
                new BasicAuthorizationInterceptor(someUsername, somePassword));
    
            return restTemplate;
        }
    
    }
    

    Test Configuration Class

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
    public class SomeTest {
    
        @SpringBootConfiguration
        @Import({MySpringBootApp.class, SomeConfiguration.class})
        static class TestConfiguration {
    
            @Autowired
            private ConfigurableEnvironment configurableEnvironment;
    
            // This doesn't work:
    
            @Bean
            @Lazy(false)
            // I also tried a @PostConstruct method
            public TargetPropertiesPropertySource targetPropertiesPropertySource() {
                TargetPropertiesPropertySource customPropertySource =
                    new TargetPropertiesPropertySource();
                configurableEnvironment.getPropertySources().addFirst(customPropertySource);
                return customPropertySource;
            }
        }
    }