如何在 Spring Boot 和 JUnit 测试中定义 spring.config.location?

Posted

技术标签:

【中文标题】如何在 Spring Boot 和 JUnit 测试中定义 spring.config.location?【英文标题】:How to define the spring.config.location on Spring Boot and JUnit tests? 【发布时间】:2019-02-08 01:16:17 【问题描述】:

在运行 JUnit 测试时,我们如何以编程方式配置 Spring Boot 以定义 spring.config.namespring.config.location 属性的新值?

例如,如果我们想在运行应用程序时定义这些属性,我们可以使用类似(在 Kotlin 中):

fun main(args: Array<String>) 
//  SpringApplication.run(Application::class.java, *args)

    val applicationContext = SpringApplicationBuilder(Application::class.java)
        .properties(
            """spring.config.name:
                $getSpringConfigNames()
            """,
            """spring.config.location:
                $getSpringConfigLocationPaths()
            """
        )
        .build()
        .run(*args)

//  val environment = applicationContext.getEnvironment()

但我无法找到一种方法来配置它以在 JUnit 测试中使用。

编辑

由于弹簧靴的限制,这里有一个复杂的问题。

我想使用整个文件夹及其子文件夹作为搜索配置文件的有效位置(例如,我们可以拥有用于特定环境、数据库、第三方等的文件夹)。

在运行应用程序时,可以创建一个方法,在本例中为 getSpringConfigLocationPaths()。并且此方法创建一个逗号分隔的列表,其中包含“主”文件夹内的所有文件夹。

例如,对于主文件夹src/main/resources/configuration,它将输出:

src/main/resources/configuration,
src/main/resources/configuration/environments,
src/main/resources/configuration/environments/development,
src/main/resources/configuration/environments/staging,
src/main/resources/configuration/environments/testing,
src/main/resources/configuration/environments/production,
src/main/resources/configuration/environments/common

在使用 JUnit 测试和 Spring Boot 时,我们如何解决这种情况?

不幸的是 Spring Boot 不允许 src/main/resources/configuration/**/* 之类的东西。

因为我们在不同的子文件夹中使用多个属性文件来组织系统,所以我们需要找到一种方法来动态地考虑它们。

【问题讨论】:

【参考方案1】:

我正在使用最新的 Spring Boot 2.2.0,根据我的经验,@TestPropertySource@SpringBootTest 注释都可以完成这项工作,因为它们具有 properties 属性。 所以,你可以这样做:

@TestPropertySource(properties = ["spring.config.location=classpath:dev/", "spring.config.name=custom-app-name"]
@TestConfiguration
class DevTestCfg  // this will make tests to look for configs in resources/dev/custom-app-name.properties

如果您希望从多个位置加载您的属性,请注意有一个 spring.config.additional-location 属性。

这里唯一的问题是properties 属性中的值必须是常量。 但是您可以为每个环境创建多个配置,并在每个配置类上放置对应的@Profile("envName")。然后用不同的-Dspring.profiles.active 运行你的测试,相应的测试配置应该会自动拾取。

【讨论】:

【参考方案2】:

应该仔细设计运行 spring boot 的测试, spring boot 测试有一个完整的测试框架,所以显然可以考虑使用这个框架。

在配置管理方面,我建议考虑以下几点:

基本上有两种类型的测试:

加载具体特定配置(bean 集)的测试,例如,如果您只想测试 DAO,则加载此 dao 的配置。 在这种情况下,配置应该根据特定测试的需要“量身定制”,而不需要“完整”配置。

例如,如果微服务包含数据库配置(用户、密码、模式等)以及消息管理,则在测试 DAO、消息传递时无需指定消息传递系统的配置无论如何都不会加载bean。

通常,这种“类型”的测试会是这样的:

@SpringBootTest(classes = RelationalDbDaoConfiguration.class) 
public class MyDaoTest 


如果您没有满足您需要的配置,您可以使用@MockBean 模拟不必要的bean,甚至在src/test/java 中创建自定义配置,以便它只在测试类路径中。使用@TestConfiguration 是有意义的,但它超出了问题的范围。

现在为了只为 db 加载配置,有很多选项,仅举几例:

@ActiveProfiles("dao") 在测试类上 + 将“application-dao.properties/yaml”放入src/test/resourcessrc/test/resources/config

在测试中使用@TestPropertySource(locations = "classpath:whatever.properties")

创建一个特殊的“DbProperties”bean 并在 Spring 中以编程方式对其进行初始化,当您了解有关仅在实际测试执行期间运行测试的上下文的一些详细信息时(例如,如果您开始测试前的数据库和端口是动态创建的,但它确实是一个相当高级的设置,超出了这个问题的范围)+数据源 bean 可以读取这些属性

使用@SpringBootTestproperties 属性提供“细粒度”属性定义

有点明显,但我还是要提一下:将application.properties 放入src/test/resources 它将覆盖常规配置

第二种类型的测试是当你加载“整个”微服务时,通常,这些是在@SpringBootTest注解中没有“classes”参数的测试

@SpringBootTest // note, no actual configurations specified
public class MyMicroserviceTest 
  ...

现在,这肯定需要指定一整套配置,尽管实际指定这些配置的技术仍然适用(只是配置文件的内容会有所不同)。

我不建议在测试过程中使用spring.config.location,因为这意味着测试依赖于一些外部资源,这使得整个设置更加复杂。

【讨论】:

嗨,马克,感谢您的解决方案。我应该多解释一下为什么我需要这个(主要问题与 spring.config.location 属性中要考虑的动态文件夹有关)。你知道我们能不能做点什么? 好吧,例如在@TestPropertySource 方法中有一个“位置”属性,因此您可以指定配置【参考方案3】:

如果是 XML 驱动的配置,

@ContextConfiguration(locations = "/app-context.xml")

如果是配置类驱动的注解,

@ContextConfiguration(classes = AppCOnfig::class, AnotherCOnfig::class

这些将在您运行的单元测试类的类级别上定义。

此外,如果您有可供 Junit 考虑的个人资料, @ActiveProfiles("myProfile") 将被添加到测试类中。

【讨论】:

您好 Karthik R,感谢您的解决方案。我应该多解释一下为什么我需要这个(主要问题与 spring.config.location 属性中要考虑的动态文件夹有关)。你知道我们能不能做点什么? 我看到了你更新的问题。您不需要为 Spring Boot 测试提供多个文件。我明白你想要做什么。您正在尝试测试所有环境配置。但是单元测试不应该这样做。您可以使用 spring.profiles.include 将一个配置文件包含在另一个配置文件中,但是如果您重复相同的配置,这会产生冲突。 如果您的目标是尝试测试所有环境配置,我不建议您尝试。您基于配置文件(dev、qa 等)的配置应该只因外部参数(如 DB url 等)而有所不同,但您的单元测试不应该担心它。要么模拟这些外部因素,要么只针对一种环境进行测试,比如本地。希望我没有误解你的问题。

以上是关于如何在 Spring Boot 和 JUnit 测试中定义 spring.config.location?的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个 JUnit 测试中运行两个 Spring Boot 微服务?

如何 JUnit 测试 Spring-Boot 的 Application.java

如何在spring-boot应用程序的junit中调用ApplicationContextInitializer

如何使用junit在spring-boot中测试常见错误ErrorController

如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法

如何使用 JUnit 测试 Spring Boot aspectj 方法?