无法集成测试 gradle 多模块 Spring-Boot-Application
Unable to integration-testing gradle multi-module Spring-Boot-Application
在一个 Gradle 多模块项目中,在其自己的模块中进行引导,我无法使用 MockMvc,因为它需要引用引导模块。我不确定我是否配置错误。基本结构为:
- 模块:一个包含一些 REST 服务的模块,需要一个 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-模块仅包含一个 class"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());
}
}
模块-模块(*唉我应该为这个模块选择一个不同的名字)只包含这个实现-class:
public class Module {
public String info() { return "module"; }
}
此外,模块-模块有以下测试-class进行集成测试:
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。当任务 "clean test" 在 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,但没有成功。
我做错了什么?
首先,我会建议重组您的项目,这样您就没有循环依赖。就像现在一样,为了构建 starter
,您需要构建 module
。为了测试 module
,您需要构建 starter
。 Gradle可以做到,但一般都是闻到味道。
在故障排除方面:当您遇到这样的测试失败时,请查看具有完整堆栈跟踪的测试报告。您应该看到它抱怨找不到 Starter
class (Caused by: java.lang.ClassNotFoundException: de.kramhal.multi.Starter
),这是 starter
模块中的原因。
您提到了 spring-dependency-management
插件,但这只与管理 Maven 依赖项相关,而不是像这样的项目依赖项。所以在这里没有帮助。
我不完全确定这是否是 Windows 具体的,因为我记得不久前有一些关于性能的讨论,当时有很多 classes。但我相信 java-library
插件会在其他项目中寻找 jar 文件,而不是编译后的文件夹 classes。这对您来说是个问题,因为 spring-boot
插件默认会禁用标准 jar
任务,而是通过 bootJar
任务创建 "fat" 一个 jar 文件。因为您既需要用于将应用程序打包到 运行 独立应用程序的 fat jar,也需要用于将其作为依赖项使用的普通 jar,因此您需要对 starter
项目 (Kotlin DSL) 进行一些调整:
tasks {
jar {
enabled = true
}
bootJar {
archiveClassifier.set("boot")
}
}
这将启用普通的jar 文件,但由于名称会与bootJar
任务生成的名称冲突,您需要重命名其中一个。我选择重命名 bootJar
一个。
我不知道为什么测试在 IntelliJ 中对你有效,因为默认情况下应该将所有内容委托给 Gradle。但是也许你有一个旧版本,或者做了一些手动配置让 IntelliJ 编译和 运行 你的测试。
在一个 Gradle 多模块项目中,在其自己的模块中进行引导,我无法使用 MockMvc,因为它需要引用引导模块。我不确定我是否配置错误。基本结构为:
- 模块:一个包含一些 REST 服务的模块,需要一个 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-模块仅包含一个 class"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());
}
}
模块-模块(*唉我应该为这个模块选择一个不同的名字)只包含这个实现-class:
public class Module {
public String info() { return "module"; }
}
此外,模块-模块有以下测试-class进行集成测试:
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。当任务 "clean test" 在 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,但没有成功。
我做错了什么?
首先,我会建议重组您的项目,这样您就没有循环依赖。就像现在一样,为了构建 starter
,您需要构建 module
。为了测试 module
,您需要构建 starter
。 Gradle可以做到,但一般都是闻到味道。
在故障排除方面:当您遇到这样的测试失败时,请查看具有完整堆栈跟踪的测试报告。您应该看到它抱怨找不到 Starter
class (Caused by: java.lang.ClassNotFoundException: de.kramhal.multi.Starter
),这是 starter
模块中的原因。
您提到了 spring-dependency-management
插件,但这只与管理 Maven 依赖项相关,而不是像这样的项目依赖项。所以在这里没有帮助。
我不完全确定这是否是 Windows 具体的,因为我记得不久前有一些关于性能的讨论,当时有很多 classes。但我相信 java-library
插件会在其他项目中寻找 jar 文件,而不是编译后的文件夹 classes。这对您来说是个问题,因为 spring-boot
插件默认会禁用标准 jar
任务,而是通过 bootJar
任务创建 "fat" 一个 jar 文件。因为您既需要用于将应用程序打包到 运行 独立应用程序的 fat jar,也需要用于将其作为依赖项使用的普通 jar,因此您需要对 starter
项目 (Kotlin DSL) 进行一些调整:
tasks {
jar {
enabled = true
}
bootJar {
archiveClassifier.set("boot")
}
}
这将启用普通的jar 文件,但由于名称会与bootJar
任务生成的名称冲突,您需要重命名其中一个。我选择重命名 bootJar
一个。
我不知道为什么测试在 IntelliJ 中对你有效,因为默认情况下应该将所有内容委托给 Gradle。但是也许你有一个旧版本,或者做了一些手动配置让 IntelliJ 编译和 运行 你的测试。