With version Spring Boot version 2.3.0.RELEASE, tests annotated with @ActiveProfiles("test") fail to resolve variable spring.profiles.active.

The issue is not present on version 2.2.7.RELEASE.

It can be reproduced with a simple Spring Boot app created from Spring Initializr with options: - Maven Project - Java - Spring Boot 2.3.0 - Packaging Jar - Java 8 - No dependencies

The code to add are a Config class, @ActiveProfiles on the test class, and a application-test.properties file (empty contents).

Config class

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(
    ignoreResourceNotFound = false,
    value = "classpath:application-${spring.profiles.active}.properties")
public class Config {}

Test file with @ActiveProfiles

@SpringBootTest
@ActiveProfiles("test")
class DemoApplicationTests {

    @Test
    void contextLoads() {}
}

Comment From: mbhave

This is a side-effect of #21006. The use of the spring.profiles.active property is an implementation detail of @ActiveProfiles and you should consider not relying on that. In the example above, once the profile is active, the profile specific application-{profile}.properties file should automatically be picked up. What's the reason for adding that via the @PropertySource annotation?

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: aratnam86

Hi,

We also have same issue after we have migrated from 2.2.7-RELEASE to 2.3.0-RELEASE.

We have been using Spring as our core framework for as long as we remember. We have continued to gain benefit from it and we are grateful to the Spring team for their efforts. Because Spring framework has taken care of most of the bottleneck issues, all there was left for us were to focus on resolving next level challenges of improving performance in building software. One of the challenges of software design is how to build re-usable services that can be build once and used in multiple projects without modification.

To resolve the aforementioned challenge, we have taken the following Services-Oriented approach. In the development of software, it is quite common to find some "basic" functions that are used very often. The common practice is to "capsulize" them into so-called "services", and prepare them for future use. These basic well prepared services then can be very simply and seamlessly used in different cases and circumstances. "Well-prepared" here means those services should be self-contained, yet environment configurable.

The underlying challenge with writing self-contained services is the usage of properties that are both common as well as specific to certain software. Imagine the following services with the below mentioned property files in each services project,

SpringBoot Tests with ActiveProfiles annotation cannot resolve placeholder 'spring.profiles.active'

application.properties
application-dev.properties
application-test.properties
application-prod.properties
application-junit.properties

If we were to build projects, "Necklace" and "Bracelet" from the services projects, without the self-contained approach, each of those projects would need to have all the properties for respective environments

Project: Necklace
pearl-services.gem.color=White
ruby-services.gem.cut=Step Cut
diamond-services.gem.shape=Oval
Project: Bracelet
pearl-services.gem.color=White
ruby-services.gem.cut=Mixed Cut
diamond-services.gem.shape=Princess

To make those services fully self-contained, yet environment configurable, the approach we have taken is as follows.

package com.jewellery.pearl;

@Configuration
@PropertySources({
    @PropertySource(value= "classpath:/pearl-services.properties", ignoreResourceNotFound = true ),
    @PropertySource(value = "classpath:/pearl-services-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class PearlServiceConfiguration {}
package com.jewellery.ruby;

@Configuration
@PropertySources({
    @PropertySource(value= "classpath:/ruby-services.properties", ignoreResourceNotFound = true ),
    @PropertySource(value = "classpath:/ruby-services-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class RubyServiceConfiguration {}
package com.jewellery.diamond;

@Configuration
@PropertySources({
    @PropertySource(value= "classpath:/diamond-services.properties", ignoreResourceNotFound = true ),
    @PropertySource(value = "classpath:/diamond-services-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class DiamondServiceConfiguration {}
package com.jewellery.necklace;

@SpringBootApplication(scanBasePackages = "com.jewellery")
public class NecklaceApplication {

    public static void main(String[] args) {
        SpringApplication.run(NecklaceApplication.class, args);
    }
}

The above approach perfectly works and it enables us to have self-contained, yet environment configurable services, but the problem arises if we write test for Necklace Application using 2.3.0-RELEASE

package com.jewellery.necklace;
@SpringBootTest
@ActiveProfiles("junit")
public class NecklaceApplicationTests {

    @Test
    public void contextLoads() {
    }

}

The Log showing the problem is

The following profiles are active: junit
Properties location [classpath:/pearl-services-${spring.profiles.active}.properties] not resolvable: Could not resolve placeholder 'spring.profiles.active' in value "classpath:/pearl-services-${spring.profiles.active}.properties"
Properties location [classpath:/ruby-services-${spring.profiles.active}.properties] not resolvable: Could not resolve placeholder 'spring.profiles.active' in value "classpath:/ruby-services-${spring.profiles.active}.properties"
Properties location [classpath:/diamond-services-${spring.profiles.active}.properties] not resolvable: Could not resolve placeholder 'spring.profiles.active' in value "classpath:/diamond-services-${spring.profiles.active}.properties"

While keeping the Service-Oriented Architecture in mind, could you please suggest, how we can do it with Spring-Boot 2.3.0-RELEASE?

Thank you for your patience reading the long message.

Comment From: dogilvie

Hello, this issue was also seen in the transition between boot 2.2.2 and 2.2.3

There is some conversation in this issue https://github.com/spring-projects/spring-boot/issues/19788

Comment From: dogilvie

While keeping the Service-Oriented Architecture in mind, could you please suggest, how we can do it with Spring-Boot 2.3.0-RELEASE?

Thank you for your patience reading the long message.

I will try to summarize in a few words the desired use case:

  1. Create a library which is configured by a property lib.foo which has different values for dev/test/prod environments
  2. Specify the different lib.foo values for dev/test/prod in the library to encapsulate them there.
  3. Use the library in an application - do not have to provide the lib.foo property in application.properties
  4. Run the application with spring.profiles.active=dev or test or prod
  5. The application uses the value of lib.foo appropriate to the active profile

Repeat the above for new lib.bar property, second library property lib2.foo, second application2 using first library etc.

Currently in spring boot 2.2 the following code works for this use case, where library.properties and library-dev/test/prod.properties are in main/resources for the library.

@Configuration
@PropertySources({
    @PropertySource(value= "classpath:/library.properties", ignoreResourceNotFound = true ),
    @PropertySource(value = "classpath:/library-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class LibraryConfiguration {}

Comment From: aratnam86

The following workaround would do the trick until this issue is resolved

public class JunitProfileResolver implements ActiveProfilesResolver{

  private static final String PROFILE_JUNIT = "junit";

  @Override
  public String[] resolve(Class<?> testClass) {
    System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, PROFILE_JUNIT);
    return new String[] {PROFILE_JUNIT};
  }
}
@SpringBootTest
@ActiveProfiles(resolver = JunitProfileResolver.class)
public class ApplicationTests {
  @Test
  public void contextLoads() {
  }
}

Comment From: mbhave

@aratnam86 With 2.4.x, we have added the concept of importing additional configuration files using the spring.config.import property. For your application, I can imagine an application.properties file that looks something like this:

#---
spring.config.activate.on-profile=dev
spring.config.import=pearl-services-dev.properties,ruby-services-dev.properties,diamond-service-dev.properties
#---
spring.config.activate.on-profile=prod
spring.config.import=pearl-services-prod.properties,ruby-services-prod.properties,diamond-service-prod.properties

For tests, you can add another application.properties file that imports the test specific configuration. The #--- syntax is also new in the 2.4.x line which is support for multi-document .properties files.

We've added support for importing additional files in the 2.4.x line so you can give it a try using the 2.4.0-SNAPSHOTs. For more details, please see the reference guide.

Could you let us know if this meets your needs and if not then please provide a minimal sample that we can run to understand the use case further.

That being said, if your code relies on reading the spring.profiles.active property, you should set it explicitly, using the properties attribute of @SpringBootTest, rather than relying on the implementation details of @ActiveProfiles.

Comment From: aratnam86

Thank you @mbhave . It works, if we move the configuration file out of the application to an external location and then using spring.config.import to include it.

Also, thank you for your tips on using #--- and spring.config.activate.on-profile. Very nice features.