我有一个裸露的 Spring Boot 应用程序
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
使用以下内容连接到 Spring Cloud 配置服务器
application.yml
spring:
application:
name: client
config:
import: configserver:http://localhost:8888
当配置服务器运行时,应用程序工作正常,当服务器未运行时,应用程序会按预期失败。
我现在想使用
@SpringBootTest
为不依赖于正在运行的配置服务器的应用程序编写一个集成测试,甚至不尝试连接到它。
配置服务器关闭后,进行裸测试
@SpringBootTest
class ClientApplicationTests {
@Test
void contextLoads() {
}
}
失败并显示
java.net.ConnectException: Connection refused
,这是预期的。
当我尝试禁用配置客户端时
@SpringBootTest(properties = "spring.cloud.config.enabled=false")
测试不会尝试连接到服务器,但失败并显示
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)
问题在于 Spring Boot 2.4 新引入的属性
spring.config.import
的工作方式与所有其他属性不同。在我看来,只能添加值而不能删除。
有没有办法覆盖
spring.config.import
中的SpringBootTest
?有没有办法只抑制与配置服务器的连接?
configserver.import = configserver:https://localhost:8888
spring.config.import = ${configserver.import}
这样您就可以在测试中将属性
configserver.import
设置为空字符串:
configserver.import =
现在空的
spring.config.import
属性将不再被考虑。这甚至可以与生产环境中的 -D
JVM 启动参数覆盖一起使用。
在测试场景中,这必须与
spring.cloud.config.enabled=false
配对,因为 Spring Cloud 配置会检查至少一个以 spring.config.import
前缀开头的 configserver:
值,如果找不到匹配的属性,则不会让您启动.
错误
Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader
是通过
spring.cloud.config.enabled=false
禁用 Spring Cloud 配置引起的,因为这也会删除其处理所有 configserver:
前缀 spring.config.import
的 PropertySourceLoader bean。
这是另一种变体,它允许在产品环境中切换配置服务器 url,因为多个
spring.config.import
定义不会互相覆盖:
configserver.import = configserver:${CONFIG_SERVER_URL:https://localhost:8888}
spring.config.import = ${configserver.import}
spring.cloud.config.import-check.enabled = false
为什么需要
spring.cloud.config.import-check.enabled
?因为 Spring Cloud 配置需要
configserver:
中至少有一个 spring.config.import
前缀值,但在执行此检查之前不会 not解析属性替换。因此,Spring Cloud 配置仅将
${configserver.import}
视为字符串,并会阻止您的应用程序启动。 spring.cloud.config.import-check.enabled = false
防止上述检查。此外,环境变量 CONFIG_SERVER_URL
可用于覆盖产品环境中的本地主机配置服务器,同时如果 spring.config.import
在测试中设置为空,则仍允许 configserver.import
为空字符串。/咆哮:真是一场噩梦
spring.config.import=configserver:http://localhost:8888
放入
/scr/main/resources/application.properties
而不是 .yml
文件放在项目的根目录中。然后您只需在测试中使用 spring.cloud.config.enabled=false
禁用它即可。另一个解决方案(如果您不想完全禁用它,那就是向该导入添加可选关键字(spring.config.import=optional:configserver:http://localhost:8888
(这样您可以将其保留在
.yml
中,但您将失去在测试中禁用它的能力,但是它会简单地忽略服务器已关闭的事实。当该类与前缀为
configserver:
的导入匹配时,将返回可选资源
package com.roblovelock.spring.cloud.config;
import org.springframework.boot.context.config.*;
import org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver;
import org.springframework.core.Ordered;
import java.io.IOException;
import java.util.List;
public class NoOpCloudConfig implements ConfigDataLocationResolver<ConfigDataResource>, Ordered {
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
return location.hasPrefix(ConfigServerConfigDataLocationResolver.PREFIX);
}
@Override
public List<ConfigDataResource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
return List.of(new NoOpResource());
}
@Override
public List<ConfigDataResource> resolveProfileSpecific(ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) throws ConfigDataLocationNotFoundException {
return List.of(new NoOpResource());
}
@Override
public int getOrder() {
return -1;
}
public static class NoOpResource extends ConfigDataResource{
private NoOpResource(){
super(true);
}
}
public static class NoOpResourceLoader implements ConfigDataLoader<NoOpResource> {
@Override
public ConfigData load(ConfigDataLoaderContext context, NoOpResource resource) {
return ConfigData.EMPTY;
}
}
}
为了工作,我们需要使用
spring.factories
...
注册课程在
src/test/resources
添加包含内容的
META-INF/spring.factories
文件org.springframework.boot.context.config.ConfigDataLocationResolver=com.roblovelock.spring.cloud.config.NoOpCloudConfig
org.springframework.boot.context.config.ConfigDataLoader=com.roblovelock.spring.cloud.config.NoOpCloudConfig.NoOpResourceLoader
还要确保您的测试配置包含
spring.cloud.config.enabled=false
。
spring:
application:
name: app-name
profiles:
active: dev
cloud:
config:
fail-fast: false
discovery:
enabled: true
service-id: app-config-server
uri: http://localhost:8888
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true
此配置使应用程序在配置服务器中查找属性,询问其在 Eureka 中的 URL。 在我找到一组有效的属性之前,此类应用程序中的测试被破坏了。
我找到了一个工作集:
spring.config.import
SPRING_CONFIG_IMPORT=optional:configserver:http://app-name-in-eureka
指定spring.config.import = ${SPRING_CONFIG_IMPORT:}
spring.config.import
spring.cloud.config.fail-fast.enabled=false