Currently it can can be quite awkward to add an application.properties for tests that overrides only specific values of the one in src/main/java. Some folks resort to adding a profile specific property and enabling a profile in all their tests. Some mix .properties and .yaml so that both files load and some use import:.
It would be nice if we had an out-of-the-box solution.
In addition, we could try to address the concern mentioned in https://github.com/spring-projects/spring-boot/issues/5307#issuecomment-1783188324 and provide a way for a global user specific test application.properties file to also be loaded.
Comment From: mhalbritter
I played around a bit and came up with this solution: https://github.com/mhalbritter/spring-boot/tree/mh/38098-investigate-convention-based-applicationproperties-specifically-for-tests
It adds a new applyTestProfile attribute to the @SpringBootTest annotation. It defaults to true, and will then automatically apply the test profile to the test context.
That way users can use an src/test/resources/application-test.properties to override values from the src/main/resources/application.properties.
WDYT?
Comment From: nosan
I think a more generic approach could be applied here instead of using applyTestProfile.
For example:
@ActiveProfiles
public @interface SpringBootTest {
/**
* The profiles to activate.
* @return profiles to activate.
* @since 3.5.0
*/
@AliasFor(annotation = ActiveProfiles.class)
String[] profiles() default { "test" };
}
@SpringBootTest(profiles = {}) // no profiles
@SpringBootTest // 'test' profile
@SpringBootTest( profiles = { "profile1", "profile2" }) // 'profile1' and 'profile2'
The same idea could be applied to slice annotations such as @DataJpaTest.
https://github.com/spring-projects/spring-boot/compare/main...nosan:38098
But it looks like it does not work with @ActiveProfiles:
@SpringBootTest(profiles = { "test1" })
@ActiveProfiles("test2")
assertThat(this.environment.getActiveProfiles()).containsOnly("test1", "test2");
Expecting String[]:
["test2"]
to contain only:
["test1", "test2"]
but could not find the following string(s):
["test1"]
java.lang.AssertionError:
Expecting String[]:
["test2"]
to contain only:
["test1", "test2"]
but could not find the following string(s):
["test1"]
Comment From: sbrannen
@nosan,
@SpringBootTest(profiles = "test1")
@ActiveProfiles("test2")
class MyTests { /* ... */ }
When you declare @ActiveProfiles directly on the test class and as a meta-annotation (on @SpringBootTest), the Spring TestContext Framework only finds the first one, the one closest to the class, which is @ActiveProfiles("test2") in your example.
Please note that @ActiveProfiles is not a repeatable annotation. That's why only a single @ActiveProfiles is supported on a given test class.
However, @ActiveProfiles can be inherited from superclasses.
Comment From: nosan
Thanks, @sbrannen
In that case, @SpringBootTest(profiles = ...) could be managed within SpringBootTestContextBootstrapper, similar to how applyTestProfile is handled.
private String[] getActiveProfiles(Class<?> testClass, String[] activeProfiles) {
SpringBootTest annotation = getAnnotation(testClass);
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(activeProfiles));
if (annotation != null) {
profiles.addAll(Arrays.asList(annotation.profiles()));
}
return profiles.toArray(String[]::new);
}
Comment From: wilkinsona
I wonder if we should keep https://github.com/spring-projects/spring-boot/issues/24688 in mind when looking at this. If you squint a bit, tests could be thought of as an extra "module" that adds to the application's usual properties.