Spring Boot property resolution fails tests at build time when adding a profile

151 Views Asked by At

I have a Spring Boot 2.7.14 application packaged as follows:

com.abc.globalpayments.feeds.downstream.dailycashreport com.abc.globalpayments.feeds.downstream.dailycashreport.acquire com.abc.globalpayments.feeds.downstream.dailycashreport.acquire.random com.abc.globalpayments.feeds.downstream.dailycashreport.distribute com.abc.globalpayments.feeds.downstream.dailycashreport.domain com.abc.globalpayments.feeds.downstream.dailycashreport.generate com.abc.globalpayments.feeds.downstream.dailycashreport.process

The root *.dailycashreport package contains the entry point:

@Slf4j
@SpringBootApplication
@PropertySource(value = "classpath:feeds-config.yml", factory = com.abc.globalpayments.feeds.downstream.YamlPropertySourceFactory.class)
public class DcrDataFactoryApplication implements CommandLineRunner {
    

    
    @Autowired
    private SyntheticRunner runner; data sources

    public static void main(String[] args) {//}

YamlPropertySourceFactory seen above is as follows:

public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(encodedResource.getResource());

        Properties properties = factory.getObject();

        return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
    }
}

There are the following 3 resources under src/main/resources:

application.yml:

spring:
  application:
    name: daily-cash-report-application

sftp:
  host: dev
  port: 22
  user: dev
  stagingDir: /opt/ccc/Data/Stage
  tmpDir: /opt/ccc/abcd/tmp
  #privateKey: 
  privateKey: file:///C:/Users/12345/AppData/Roaming/SSH/UserKeys/distributessh
  chmod: 664 
  
file:    
  stagingDir: /opt/ccc/Data/Stage
  tmpDir: C:\tmp 

feeds-config.yml

feeds:
  downstream:
    another-system:
      output-file-name: other.txt
    daily-cash-report:
      output-file-name: dcr.txt
      test:
        records-to-generate:
          pc: 10
          dp: 5
          z: 1

and logback.xml


In tests, I'm using the following setup:

@SpringBootTest
class DataMarshallerServiceTest {

    @Autowired
    private DataMarshallerService cut;
    
    @Autowired
//

The above setup builds and works fine, however as soon as I'm trying to introduce a dev profile by renaming application.yml into application-dev.yml, I start encountering the following exception as part of the build in tests execution:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sftp.host' in value "${sftp.host}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180)

Here's the launch profile in my STS IDE:

enter image description here

However, the error manifests during the build time, when executing

mvn clear package -P dcr

which is equivalent to the following STS Maven config also present in STS:

enter image description here

What is it I'm missing so that Spring profiles start functioning properly?

Could it be that since the *App has YamlPropertySourceFactory configured, the rest of the properties need to be specified explicitly?

Thank you in advance.

2

There are 2 best solutions below

0
Simeon Leyzerzon On BEST ANSWER

I've made the following assumption and an attempt to validate it, it seems to work, so I'll leave it here as a potential answer to be, potentially, challenged.

Apparently tests need to be pointed at the proper profile which is not part of the @SpringBootTest annotation itself.

Either one of the following 3 configurations seem to make the build work:

Option 1:

I've updated all the failing test cases with the following annotation after the @SpringBootTest:

@ActiveProfiles(profiles = "dev")

while adding the following stanza inside the application.yml:

spring:
  application:
    name: daily-cash-report-application
  profiles: dev

It also works without the above stanza if I rename application.yml into application-dev.yml.

Option 2:

Another, even simpler approach is as follows:

  1. no need to annotate all tests with additional @ActiveProfiles(profiles = "dev") as above while
  2. running the build with the following Maven command instead:
mvn clean package -P dcr -Dspring.profiles.active=dev

and

  1. consuming the application-dev.yml out of src/main/resources/

Option 3

No need to either:

  • adding an application-dev.yml file
  • or specifying an additional @ActiveProfiles(profiles = "dev") annotation
  • or needing to point to a specific profile as part of invocation of the build with -Dspring.profiles.active=dev,
  • or specifying -Dspring.profiles.active=dev as a parameter to Maven

Simply:

Drop a copy of application.yml into src/test/resources/ folder so as to be picked up and used for tests execution via mvn clean package -P dcr

I'll let someone more authoritative to confirm that these are the right approaches and to validate that this test behavior in Spring is by design.

Thank you for reading.

2
Slevin On

You are not providing the standard properties file but only the dev one. Keep the application.yml and add a application-dev.yml in addition. In the standard one you add the following property:

spring.profiles.active:
  - dev
  - another_profile
  - and_one_more_profile

Spring will now search for application-dev.yml in addition to the application.yaml. This way you can override the standards with properties from the -dev profile.

Or, as Gweltaz Niquel pointed out in his comment, you can also set profiles via system parameter.