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.
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.