I have a bare Spring Boot application
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
which connects to a Spring Cloud Config Server with the following application.yml
spring:
application:
name: client
config:
import: configserver:http://localhost:8888
The application works fine when the config server is running and fails as expected when the server is not running.
I now want to write an integration test with @SpringBootTest for the application that does not depend on a running config server, and does not even try to connect to it.
With the config server down, the bare test
@SpringBootTest
class ClientApplicationTests {
@Test
void contextLoads() {
}
}
fails with java.net.ConnectException: Connection refused, which is expected.
When I try to disable the config client with
@SpringBootTest(properties = "spring.cloud.config.enabled=false")
the test does not try to connect to the server but fails with
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
...
Caused by: java.lang.IllegalStateException: Unable to load config data from 'configserver:http://localhost:8888'
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:141)
...
Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/' or File.separator
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferencesForFile(StandardConfigDataLocationResolver.java:229)
The problem is that the property spring.config.import that has been newly introduced with Spring Boot 2.4 works differently than all other properties. It seems to me that only values can be added to it but never removed.
Is there a way to override spring.config.import in a SpringBootTest? Is there way to suppress only connections to config servers?
configserver.import = configserver:https://localhost:8888
spring.config.import = ${configserver.import}
This way you can set the property configserver.import to empty string in your tests:
configserver.import =
The now empty spring.config.import property will not be considered anymore. This would even work with -D JVM launch parameter overrides in prod environments.
This has to be paired with a spring.cloud.config.enabled=false in test scenarios as spring cloud config checks for at least one spring.config.import value to start with the configserver: prefix and won't let you start if it does not find a matching property.
The error
Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader
is caused by by disabling spring cloud config via spring.cloud.config.enabled=false as that also removes its PropertySourceLoader bean that is handling all configserver: prefixed spring.config.import.
Here is another variant which allows for switching out the config server url in prod environments as multiple spring.config.import definitions do not override each other:
configserver.import = configserver:${CONFIG_SERVER_URL:https://localhost:8888}
spring.config.import = ${configserver.import}
spring.cloud.config.import-check.enabled = false
Why is spring.cloud.config.import-check.enabled necessary? Because spring cloud config requires at least one configserver: prefixed value in spring.config.import but does not resolve property replacements before doing this check. Therefor spring cloud config only sees ${configserver.import} as a string and will prevent your application from starting. spring.cloud.config.import-check.enabled = false prevents the aforementioned check. Furthermore the environment variable CONFIG_SERVER_URL can be used to override the localhost configserver in prod environments while still allowing spring.config.import to be an empty string if configserver.import is set to empty in tests.
/rant: what a nightmare
After doing some more research, I found the following github issue: https://github.com/spring-cloud/spring-cloud-config/issues/1877
It seems to me that the unsatisfying solution is to never put a spring.config.import in the application.yml if you need to override it in a test.
For the config server in particular, it should only be placed in a profile specific configuration file or a profile specific document of a multi-document application.yml. Then you can activate that profile in production and keep it inactive in tests and development phases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With