如何在多 Gradle 项目的 JUnit Spring 上下文中加载资源属性?

Posted

技术标签:

【中文标题】如何在多 Gradle 项目的 JUnit Spring 上下文中加载资源属性?【英文标题】:How to load resource properties in a JUnit Spring context in a multi Gradle project? 【发布时间】:2018-04-19 03:57:22 【问题描述】:

我有以下项目树:

├── app
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── java
│       │   │   └── child
│       │   │       └── app
│       │   │           └── Application.java
│       │   └── resources
│       │       └── application-default.yaml
│       └── test
│           └── java
│               └── child
│                   └── app
│                       └── ApplicationTest.java
├── build.gradle
├── childA
│   ├── build.gradle
│   └── src
│       └── main
│           └── java
│               └── child
│                   └── a
│                       ├── BaseGreeterImpl.java
│                       ├── ChildAConfig.java
│                       ├── Greeter.java
│                       └── MySpringProperties.java
├── childB
│   ├── build.gradle
│   └── src
│       └── main
│           └── resources
│               ├── application-test.yaml
│               └── childB.properties
├── childC
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── java
│       │   │   └── child
│       │   │       └── c
│       │   │           ├── ChildCConfig.java
│       │   │           └── PropertyGreeterImpl.java
│       │   └── resources
│       │       └── childc.properties
│       └── test
│           └── java
│               └── child
│                   └── c
│                       ├── TestYamlImport.java
│                       └── TestGreeter.java
└── settings.gradle

我有以下测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =  ChildCConfig.class , loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
@SpringBootTest
public class TestYamlImport 

    @Autowired
    private MySpringProperties properties;

    @Test
    public void readChildAYaml() 
        assertThat(properties.getName()).isEqualTo("it-is-another-thing");
    


预期

我希望properties.getName() 能够从childB/src/main/resources/application-test.yaml 中的资源childB 中读取值。

结果

我收到null

复制

GitHub:https://github.com/kopax/adk/tree/adk-spring

一个班轮:

git clone git@github.com:kopax/adk.git && cd adk && git checkout adk-spring && ./gradlew build --info

问题

在复制项目中有一个名为 childC/src/test/java/childC/TestGreeter.java 的测试,通过 childB.properties 导入证明 这不是类路径问题

所以这是我的问题:

在使用@ConfigurationProperties 时,spring 是否会以某种方式限制类路径分辨率?

childA 中从childB测试范围 初始化的配置@Bean 中,我还没有找到读取我的application-test.yml 的方法,这怎么可能?

【问题讨论】:

【参考方案1】:

您使用AnnotationConfigContextLoader 而不是(默认)SpringBootContextLoader 有什么特别的原因吗?您面临的问题不是由类路径中缺少文件引起的(您可以将application-test.yaml 复制到任何src/main/resourcessrc/test/resources 具有相同的结果)但AnnotationConfigContextLoader 不使用ConfigFileApplicationListener 的事实是负责通过从众所周知的文件位置加载属性来配置上下文(如您的情况下为application-profile.yaml)。

您可以轻松比较使用每个加载器时加载的属性。首先,您可以检查 AnnotationConfigContextLoader 的功能 - 只需在 AbstractGenericContextLoader.java 文件的第 128 行放置一个断点,然后在您最喜欢的 IDE 中运行调试器:

接下来你可以调查变量context -> environment -> propertySources -> propertySourceList。您将找到 5 个属性来源:

它们都不从application.ymlapplication.properties 等配置文件中加载属性。

现在让我们做同样的事情,但使用SpringBootContextLoader 类。首先删除

loader = AnnotationConfigContextLoader.class

MyEntityTest 中并在 SpringApplication.java 文件的第 303 行放置一个断点:

在刷新应用程序上下文之前,我们就到了这里。现在让我们研究变量context -> environment -> propertySources -> propertySourceList:

我们可以看到的第一个区别是,现在我们有 7 个属性源,而不是之前示例中的 5 个。最重要的是——ConfigFileApplicationListener.ConfigurationPropertySources 就在这里。此类使应用程序上下文知道application-profile.yaml 属性文件的存在。

如您所见,这只是使用正确的上下文加载器的问题。替换

@ContextConfiguration(classes =  ChildCConfig.class , loader = AnnotationConfigContextLoader.class)

@ContextConfiguration(classes =  ChildCConfig.class , loader = SpringBootContextLoader.class)

@ContextConfiguration(classes =  ChildCConfig.class )

因为这个加载器是使用@SpringBootTest 注释时的默认加载器,你会让你的测试像魅力一样通过。希望对你有帮助。

【讨论】:

没有特别的原因......非常感谢它真的有帮助,也感谢您提供调试信息!

以上是关于如何在多 Gradle 项目的 JUnit Spring 上下文中加载资源属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Gradle中为单个测试类并行执行JUnit测试

如何在多模块 Gradle 项目中构建分发包?

使用 gradle 进行 android 项目的 junit 测试

如何使用 Gradle 中的本地 JUnit Jar 而不是 Maven Central?

Gradle / javacard插件是否支持在多项目gradle构建中构建两个applet依赖项?

如何将 JUnit 5 与 Gradle 一起使用?