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.