4

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
  • 3,946
  • 7
  • 40
  • 73
Benjamin Maurer
  • 3,602
  • 5
  • 28
  • 49
  • 1
    I suggest a read of the Spring Boot reference guide on how properties work in Spring Boot (see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html). In short use `@ActiveProfiles("test")` and spring boot will load both `application.properties` and `application-test.properties`. The latter will override properties from the former. – M. Deinum Nov 21 '18 at 11:41

2 Answers2

5

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.

aurelius
  • 3,946
  • 7
  • 40
  • 73
  • TY for your answer. Will this use 'application-test.yml' by default, or do I need to set the name somewhere? Are these ADDITIONAL properties, e.g., they can overwrite existing ones, or do I need to duplicate my whole config? And lastly - where do I put the annotation? On each test class, or on a Config..? – Benjamin Maurer Nov 21 '18 at 11:23
  • I edited my answer to cover your question and some additional material that might help you – aurelius Nov 21 '18 at 12:33
  • Thanks. Well, it seems like putting it on a @Configuration class doesn't do the trick. You have to put it onto the test and creating a meta-annotation fails, even though it should be possible according to docs: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/ActiveProfiles.html – Benjamin Maurer Nov 21 '18 at 12:55
  • the @Profile on a configuration creates a new profile configuration like the existence of the application-test property file, that one will create the test profile. – aurelius Nov 21 '18 at 13:03
  • I inadvertedly clicked 'edit' on your answer instead of my question x| But I can't retract it, it's up for review. Hopefully the reviewer catches that... – Benjamin Maurer Nov 22 '18 at 12:09
1

You can add application.properties to

src/test/resources

Then all properties which are in this file override these from src/main/resources/application.properties.

So you don't need profiles and any additional annotations like @TestPropertySource

marok
  • 1,996
  • 1
  • 14
  • 20
  • 1
    No, this will _replace_ application.properties completely, i.e., you can't just put the ones you want to overwrite in there, you need to duplicate and change the whole config. I want to avoid this, bc. this will inevitably lead to a divergence and bugs. – Benjamin Maurer Nov 22 '18 at 10:50
  • Could you try it? I have this configuration for app with spring-boot 1.5.12 and application.properties in test/resources have only few property. And all properties which are in main/resourceshave their original value – marok Nov 22 '18 at 10:56
  • I've tested it before, I retested it again - just for you - and it still doesn't work. You can try it yourself. I linked a Github repo. Check it out, rename the application-test.properties and see for yourself. – Benjamin Maurer Nov 22 '18 at 12:05
  • This does indeed work at least within the IDE I am working with. – Speckpgh Oct 22 '19 at 18:25