无法集成测试 gradle 多模块 Spring-Boot-Application

Posted

技术标签:

【中文标题】无法集成测试 gradle 多模块 Spring-Boot-Application【英文标题】:Unable to integration-testing gradle multi-module Spring-Boot-Application 【发布时间】:2020-10-02 01:38:17 【问题描述】:

在 Gradle 多模块项目中,在其自己的模块中进行引导,我无法使用 MockMvc,因为它需要引用引导模块。我不确定我是否配置错误。基本结构是:

module:一个包含一些 REST-Services 并且需要 testImplementation-Dependency on starter 的模块 starter:应用了 spring-boot-plugin 并依赖于模块的引导模块

我使用 Spring-Boot 2.3.1.RELEASE 和 Gradle 6.4 在 github 上设置了一个最小示例,配置如下:

./settings.gradle.kts

rootProject.name = "spring-multimodule-integrationtest"
include("starter", "module")

./build.gradle.kts

subprojects 
    repositories 
        jcenter()
    
    dependencies 
        apply(plugin = "java-library")
        "testImplementation"("junit:junit:4.12")
    

./starter/build.gradle.kts

plugins 
    id("org.springframework.boot") version "2.3.1.RELEASE"


dependencies 
    implementation(project(":module"))

./module/build.gradle.kts

dependencies 
    testImplementation(project(":starter"))

starter-module 只包含一个引用 module-module 的单一类“Starter”:

public class Starter 
    public String info()  return "starter"; 
    public static void main(String[] args) 
        System.out.println(new Starter().info() + " and " + new Module().info());
    

module-module(*叹气我应该为这个模块选择一个不同的名字)只包含这个实现类:

public class Module 
    public String info()  return "module"; 

此外,module-module 有以下测试类进行集成测试:

public class IntegrationTest

    @Test public void testSomeLibraryMethod() 
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        System.setOut(new PrintStream(out));
        Starter.main(new String[0]);
        assertEquals("starter and module\n", out.toString());
    

此代码运行良好,直到在“./starter/build.gradle.kts”中应用 spring-boot-plugin。当我在 shell 上发出“干净测试”任务时:

❯ ./gradlew clean test

> Task :module:test FAILED

de.kramhal.multi.IntegrationTest > testSomeLibraryMethod FAILED
    java.lang.NoClassDefFoundError at IntegrationTest.java:17
        Caused by: java.lang.ClassNotFoundException at IntegrationTest.java:17

1 test completed, 1 failed

在 IDE(确切地说是 IntelliJ)中执行测试时,不会出现此问题。

我已经尝试使用 answer(以及其他几个答案)中建议的 spring-dependency-management,但没有成功。

我做错了什么?

【问题讨论】:

【参考方案1】:

首先,我建议重组您的项目,这样您就没有循环依赖。就像现在一样,为了构建starter,您需要构建module。为了测试module,您需要构建starter。 Gradle 可以做到,但它通常是一种气味。

在故障排除方面:当您遇到这样的测试失败时,请查看测试报告,因为其中包含完整的堆栈跟踪。您应该看到它抱怨找不到Starter 类(Caused by: java.lang.ClassNotFoundException: de.kramhal.multi.Starter),这是starter 模块中的原因。

您提到了spring-dependency-management 插件,但这仅与管理 Maven 依赖项有关,与此类项目依赖项无关。所以这里用处不大。

我不完全确定这是否是特定于 Windows 的,因为我记得不久前有很多课程围绕性能进行了一些讨论。但我相信java-library插件会在其他项目中查找jar文件,而不是编译类的文件夹。这对您来说是个问题,因为spring-boot 插件默认禁用标准jar 任务,而是通过bootJar 任务创建“胖” jar 文件。因为您既需要用于打包应用程序以独立运行的胖 jar,也需要将其作为依赖项使用的普通 jar,因此您需要对 starter 项目(Kotlin DSL)进行一些调整:

tasks 
    jar 
        enabled = true
    
    bootJar 
        archiveClassifier.set("boot")
    

这将启用正常的jar文件,但由于名称会与bootJar任务生成的名称冲突,您需要重命名其中一个。我选择重命名bootJar 一个。

我不知道为什么该测试在 IntelliJ 中对您有效,因为默认情况下应该将所有内容委托给 Gradle。但也许你有一个旧版本,或者做了一些手动配置让 IntelliJ 编译和运行你的测试。

【讨论】:

先生!非常感谢您的解释。我没有从失败中得到这个想法,罐子丢失了,它本身就丢失了。我本可以想到这个想法,因为威尔金索纳已经解释了here。另外,我现在看到该链接在转到依赖管理而不是 packaging 时很烦人。 关于循环依赖:我知道这很不寻常,我个人更希望不要让测试依赖于不同的模块。使用 MockMvc 实现集成测试(需要知道“SpringBootApplication”注释类),有哪个选项?我只能考虑根据开胃菜和咖啡(收银员也一样)创建一个单独的模块咖啡集成测试。 为了更清楚地了解架构的意图: * 模块 starter:如上所述 = 仅引导应用程序,因此取决于其他所有内容 * 模块 咖啡:用于订购尴尬变体咖啡的休息服务(香草,生姜,......) *模块收银员:用于支付订单的休息服务模块咖啡和收银员不需要了解彼此,从架构的角度来看,将它们分开是一种很好的做法。 Multi-Module 允许将它们作为整体保持在一个项目中,使它们更易于处理(部署和开发)。 你不需要应用 spring-boot 插件来使用 MockMvc 进行测试。该插件仅用于最终打包为独立的 jar(或 war)。您也不需要带有 SpringBootApplication 注释的类来测试您的 Spring 控制器。我可能根本不依赖starter 模块,而只是单独测试模块的功能。然后对应用程序模块 (starter) 或单个单独模块中的所有模块进行全面的集成测试以进行测试。这并不是说你的方式是错误的,我只是以前从未见过这样的做法:)

以上是关于无法集成测试 gradle 多模块 Spring-Boot-Application的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring 在多模块 Maven 项目中进行集成测试

使用 spring boot gradle 插件无法使用依赖管理执行 junit5 测试

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

Gradle kotlin Springboot多模块导致无法引用kotlin的类文件(BootJar)

Gradle kotlin Springboot多模块导致无法引用kotlin的类文件(BootJar)

线程“main”中的异常 java.lang.ClassNotFoundException - Spring Boot,多模块 gradle 项目