禁用在SpringBootTest中使用spring.config.import激活的配置服务客户端

gc0ot86w  于 2022-12-29  发布在  Spring
关注(0)|答案(5)|浏览(118)

我有一个裸Sping Boot 应用程序

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

它通过以下application.yml连接到Spring云配置服务器

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)

问题是Sping Boot 2.4新引入的属性spring.config.import的工作方式与所有其他属性不同,在我看来,只能向其添加值,而不能删除。
有没有办法在SpringBootTest中覆盖spring.config.import?有没有办法只抑制到配置服务器的连接?

wgeznvg7

wgeznvg71#

在做了更多的研究之后,我发现了以下github问题:https://github.com/spring-cloud/spring-cloud-config/issues/1877
在我看来,如果需要在测试中覆盖spring.config.import,那么不令人满意的解决方案是从不spring.config.import放在application.yml中。
特别是对于配置服务器,它只应该放在一个特定于概要文件的配置文件中,或者放在一个多文档application.yml的特定于概要文件的文档中,然后您可以在生产中激活这个概要文件,而在测试和开发阶段保持它不活动。

ff29svar

ff29svar2#

仅用于测试的溶液

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 config会检查至少一个以configserver:前缀开头的spring.config.import值,如果找不到匹配的属性,则不会让您启动。
错误

Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader

原因是通过spring.cloud.config.enabled=false禁用Spring云配置,因为这也会删除其PropertySourceLoader bean,该bean处理所有以spring.config.import为前缀的configserver:

生产环境URL替换解决方案(还包括测试修复)

下面是另一个变体,它允许在生产环境中切换出配置服务器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 config要求在spring.config.import中至少有一个configserver:前缀值,但是在执行此检查之前解析属性替换。因此spring cloud config只将${configserver.import}视为字符串,并将阻止您的应用程序启动。spring.cloud.config.import-check.enabled = false阻止上述检查。此外,环境变量CONFIG_SERVER_URL可用于覆盖生产环境中的localhost configserver,同时如果configserver.import在测试中设置为空,则仍允许spring.config.import为空字符串。
/rant:真是一场噩梦

g9icjywg

g9icjywg3#

如果你除了测试之外到处都需要配置管理器,那么我认为解决你的问题的更好的方法是把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中,但你将失去在测试中禁用它的能力,但它将简单地忽略服务器停机的事实。

dzhpxtsq

dzhpxtsq4#

我的解决方案是让Spring认为该位置已经使用自定义资源加载器加载。
此类在匹配前缀为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

wrrgggsh

wrrgggsh5#

我的解决方案适用于从Eureka 发现ConfigService的情况。

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。在这样的应用程序中的测试被破坏,直到我找到一个工作的属性集。
我找到了一套工作:

  1. spring.config.import必须在环境SPRING_CONFIG_IMPORT=optional:configserver:http://app-name-in-eureka中准确设置
    1.指定spring.config.import = ${SPRING_CONFIG_IMPORT:}无关紧要
    1.在测试中指定spring.config.import并不重要
  2. spring.cloud.config.fail-fast.enabled=false-这就是诀窍

相关问题