Spring Boot: How to override default properties in Unit tests

Benjamin Maurer :

I tried loading a second properties file for my unit tests, that would overwrite some properties.

Loading it with @PropertySource on a @Configuration didn't work, loading it with @TestPropertySource didn't work either. Only setting properties directly on @TesPropertySource works, but it does NOT work when I try to make it into a meta-annotation.

Here is an example project: https://github.com/cptwunderlich/SpringTestProperties

I'd prefer to load one file an affect all tests (e.g. with @PropertySource), but if that doesn't work, at least having a custom meta-annotation would be nice, so I don't have to put that on each and every test. Basically I want to not import some data into the DB for the tests (spring.datasource.data) and later also change the database used - without copying the whole config and having to change it in two places each time.

The important bits:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class TestconfigApplicationTests {

    @Value("${my.test.property}")
    private String testproperty;

    @Test
    public void assertValue() {
        Assert.assertEquals("foobar", testproperty);
    }

}

Alternatively the config class in the tests package:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
@PropertySource("classpath:application-test.properties")
public class GlobalTestConfig {
}

Update:

The main suggestion in the answers is to use @ActiveProfile to activate a 'test' profile which will result in loading 'application-test.yaml'. That's bettern than @TestPropertySource, but I still need to put an annotation on each Test Class. I tried creating a meta-annotation - which should work - so at least I only have one custom annotation where I could bundle other settings. But that doesn't work.

The perfect solution would set these setting globally with one config class, instead of having to put an annotation on each test. I'm still looking for that solution, or at least debugging the meta-annotation before closing this question. Edit: I've created a Jira issue: SPR-17531

Edit

OK, I got a bit confused, so I retested all the different combinations:

  • @TestPropertySource(locations = "classpath:application-test.properties") on the Test, actually works now. huh.
  • @ActiveProfiles("test") on the Test works.
  • Meta-annotation with @ActiveProfiles does not work. EDIT: it does...
  • Global config of any sort (TestPropertySource, ActiveProfiles, Propertysource) does not work
  • (Having an application.properties in test/resources also doesn't work, bc. it doesn't overwrite single properties, but the complete file, i.e., I'd need to redefine and duplicate everything.)

EDIT:

OK, my mistake. The meta-annotation DOES work - I forgot to set the retention policy and the default is CLASS. Adding @Retention(RUNTIME) fixes that.

It doesn't seem like there is a way to globally set this in code (i.e., without configuring in my IDE how tests are run), so I'll have to go with the profile for now.

aurelius :

You can use @ActiveProfiles("test"). This will set the application-test.yml properties into test environment.

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class TestconfigApplicationTests {
    ...
}

If we need to target different environments, there’s a build-in mechanism for that in Boot so there is no need for additional libraries or refactorings.

We can simply define an application-environment.properties file in the src/main/resources directory – and then set a Spring profile with the same environment name.

For example, if we define a staging or test environment, that means we’ll have to define a staging or test profile and then application-staging.properties or application-test.properties.

This env file will be loaded and will take precedence over the default property file which is application.properties. Note that the default file will still be loaded, it’s just that when there is a property collision the environment specific property file takes precedence, meaning the properties specified in application-staging.properties or application-test.properties will override the ones in application.properties.

Each test class uses its own profile, so you need to specify the active profile for each class.

One additional thing that might be of interest for you is that you can mock services via configuration classes

@Configuration
@Profile("mockEntityService")
public class EntityServiceMockProvider {

    @Bean
    @Primary
    public EntityService entityService() {
        EntityService mockedEntityService = Mockito.mock(EntityService.class);

        Entity entity= Mockito.mock(Entity.class);
        when(mockedEntityService.save(any(Entity.class)))
                .thenReturn(entity);

        return mockedEntityService ;
    }
}

In test classes you can use multiple active profiles: e.g. @ActiveProfiles({"test", "mockEntityService"})

so instead of using the real implementation of the EntityService you will use the mocked implementation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=95206&siteId=1