使用 gradle 的多项目测试依赖项
Posted
技术标签:
【中文标题】使用 gradle 的多项目测试依赖项【英文标题】:Multi-project test dependencies with gradle 【发布时间】:2011-08-04 09:07:44 【问题描述】:我有一个多项目配置,我想使用 gradle。
我的项目是这样的:
项目A
->src/main/java
->src/test/java
项目B
->src/main/java
(取决于项目A上的src/main/java
)
-> src/test/java
(取决于 Project A 上的 src/test/java
)
我的Project Bbuild.gradle
文件是这样的:
apply plugin: 'java'
dependencies
compile project(':ProjectA')
任务compileJava
运行良好,但compileTestJava
无法从项目A 编译测试文件。
【问题讨论】:
可能重复:***.com/questions/5144325/gradle-test-dependency 【参考方案1】:已弃用 - 对于 Gradle 5.6 及更高版本,请使用 this answer。
在Project B中,只需要添加一个testCompile
依赖即可:
dependencies
...
testCompile project(':A').sourceSets.test.output
使用 Gradle 1.7 测试。
【讨论】:
原来 classes 属性已被弃用——改用输出。 这在 Gradle 1.3 中不起作用,因为 sourceSets 不再是项目的公共属性。 请记住,上述解决方案至少需要一个gradle testClasses
才能使构建结构真正有效。例如。 Eclipse 插件不允许您在此之前导入项目。真的很遗憾testCompile project(':A')
不起作用。 @DavidPärsson:“Gradle 1.3”与“不再”相矛盾,因为 Fesler 使用 Gradle 1.7 进行了测试。
对我不起作用。循环依赖失败:compileTestJava \--- :testClasses \--- :compileTestJava (*)
不要这样做,项目不应该涉及到其他项目。而是使用 Nikita 的答案,正确地将其建模为项目依赖项。【参考方案2】:
现在作为 Gradle 中的一流功能支持此功能。 带有 java
或 java-library
插件的模块还可以包含一个 java-test-fixtures
插件,该插件公开了要使用的辅助类和资源testFixtures
帮手。这种方法对工件和分类器的好处是:
示例
:modul:one
modul/one/build.gradle
plugins
id "java-library" // or "java"
id "java-test-fixtures"
modul/one/src/testFixtures/java/com/example/Helper.java
package com.example;
public class Helper
:modul:other
模块/其他/build.gradle
plugins
id "java" // or "java-library"
dependencies
testImplementation(testFixtures(project(":modul:one")))
modul/other/src/test/java/com/example/other/SomeTest.java
package com.example.other;
import com.example.Helper;
public class SomeTest
@Test void f()
new Helper(); // used from :modul:one's testFixtures
进一步阅读
有关详细信息,请参阅文档:https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
在 5.6 中添加:https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects
【讨论】:
他们正在努力在 android 上支持此功能,请参阅 issuetracker.google.com/issues/139762443 和 issuetracker.google.com/issues/139438142 目前不适用于 android gradle 插件和 kotlin 是的,@ArpitA,请参阅您之前的评论。本问答适用于标准 Java Gradle 项目。【参考方案3】:简单的方法是在ProjectB中添加明确的任务依赖:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
困难(但更清晰)的方法是为 ProjectA 创建额外的工件配置:
task myTestsJar(type: Jar)
// pack whatever you need...
configurations
testArtifacts
artifacts
testArtifacts myTestsJar
并为 ProjectB 添加 testCompile
依赖项
apply plugin: 'java'
dependencies
compile project(':ProjectA')
testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
【讨论】:
我试过这个(简单的方法),虽然它确保它构建了 testClasses,但它不会将测试路径添加到 CLASSPATH 所以我依赖于 ProjectA 测试类的 ProjectB 测试仍然失败构建。 @dmoebius 你必须像这样添加testArtifacts
配置:configurations testArtifacts
有关更多详细信息,请参阅 Gradle 帮助的这一部分:gradle.org/docs/current/dsl/…
在 Gradle 1.8 中,您可能希望 from sourceSets.test.output
并且可能需要 classifier = 'tests'
来代替答案中的 // pack whatever you need...
确认 Gradle 1.12 使用完整解决方案,@PeterLamberg 建议添加按预期工作。不影响将项目导入 Eclipse。
这适用于我在 Gradle 4.7 中。他们现在在docs.gradle.org/current/dsl/… 有一些关于该方法的文档【参考方案4】:
我知道这是一个老问题,但我只是遇到了同样的问题,并花了一些时间弄清楚发生了什么。我正在使用 Gradle 1.9。所有更改都应该在 ProjectB 的build.gradle
在 ProjectB 的测试中使用来自 ProjectA 的测试类:
testCompile files(project(':ProjectA').sourceSets.test.output.classesDir)
要确保 sourceSets
属性可用于 ProjectA:
evaluationDependsOn(':ProjectA')
当你编译 ProjectB 时,要确保 ProjectA 中的测试类确实存在:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
【讨论】:
这也对我有用,只是我不得不省略.classesDir
。【参考方案5】:
我自己最近也遇到过这个问题,这是一个很难找到答案的问题。
您所犯的错误是认为项目应该以导出其主要工件和依赖项的相同方式导出其测试元素。
我个人取得更大成功的是在 Gradle 中创建了一个新项目。在你的例子中,我会命名它
项目 A_Test -> src/main/java
我会将您当前在项目 A/src/test/java 中的文件放入 src/main/java。使您的项目 A 的任何 testCompile 依赖项编译项目 A_Test 的依赖项。
然后让Project A_Test成为Project B的testCompile依赖。
从两个项目的作者的角度来看,这不合逻辑,但我认为当你考虑像 junit 和 scalatest(以及其他)这样的项目时,这很有意义。即使这些框架正在测试 -相关,它们不被视为自己框架中“测试”目标的一部分 - 它们产生其他项目恰好在其测试配置中使用的主要工件。您只想遵循相同的模式。
尝试执行此处列出的其他答案对我个人而言不起作用(使用 Gradle 1.9),但我发现我在此处描述的模式无论如何都是更清洁的解决方案。
【讨论】:
是的,最终选择了这种方法。 这是最好的方法!除了我会将测试代码保留在项目 A 中,并且仅将 A src/test/java 和 B src/test/java 的依赖项移动到 A_Test。然后让 Project A_Test 成为 A 和 B 的 testImplementation 依赖项。【参考方案6】:请阅读下面的更新。
JustACluelessNewbie 描述的类似问题出现在 IntelliJ IDEA 中。问题是依赖 testCompile project(':core').sourceSets.test.output
实际上意味着:“依赖于 gradle build task 生成的类”。因此,如果您打开尚未生成类的干净项目,IDEA 将无法识别它们并报告错误。
要解决此问题,您必须在对已编译类的依赖项旁边添加对测试源文件的依赖项。
// First dependency is for IDEA
testCompileOnly files project(':core').sourceSets.test.java.srcDirs
// Second is for Gradle
testCompile project(':core').sourceSets.test.output
您可以在Module Settings -> Dependencies(测试范围)中观察IDEA识别的依赖关系。
顺便说一句。这不是很好的解决方案,因此值得考虑重构。 Gradle 本身确实有仅包含测试支持类的特殊子项目。见https://docs.gradle.org/current/userguide/test_kit.html
2016-06-05 更新 更多我正在考虑提出的解决方案,我不喜欢它。它的问题很少:
-
它在 IDEA 中创建了两个依赖项。一个指向测试源另一个指向已编译的类。 IDEA 以何种顺序识别这些依赖关系至关重要。您可以通过在 Module settings -> Dependencies 选项卡中更改依赖顺序来使用它。
通过声明这些依赖关系,您会不必要地污染依赖结构。
那么更好的解决方案是什么?在我看来,它正在创建新的自定义源集并将共享类放入其中。实际上 Gradle 项目的作者是通过创建 testFixtures 源集来实现的。
要做到这一点,您只需:
-
创建源集并添加必要的配置。检查 Gradle 项目中使用的这个脚本插件:https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
在依赖项目中声明正确的依赖关系:
dependencies
testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
将 Gradle 项目导入 IDEA,并在导入时使用“为每个源集创建单独的模块”选项。
【讨论】:
@jannis 已修复。顺便提一句。 Gradle 将其基于 Groovy 的测试装置插件移至新的基于 Kotlin 的:github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/…【参考方案7】:新的基于 testJar(支持 trnsitive 依赖)的解决方案可作为 gradle 插件使用:
https://github.com/hauner/gradle-plugins/tree/master/jartest
https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
来自文档
如果您有一个多项目 gradle 构建,您可能需要进行测试 子项目之间的依赖关系(这可能暗示您的 项目结构不完善)。
例如假设一个项目,其中子项目Project B依赖 在项目 A 和 B 上不仅对 A 有编译依赖,而且 也是一个测试依赖。要编译和运行 B 的测试,我们需要一些 来自 A 的测试助手类。
默认情况下,gradle 不会从测试构建中创建 jar 工件 项目的输出。
这个插件添加了一个 testArchives 配置(基于 testCompile) 和一个 jarTest 任务,用于从测试源集创建一个 jar(使用 分类器测试添加到 jar 的名称)。然后我们可以依赖于 B A 的 testArchives 配置(其中还将包括 A) 的传递依赖。
在 A 中,我们将插件添加到 build.gradle:
apply plugin: 'com.github.hauner.jarTest'
在 B 中,我们引用 testArchives 配置如下:
dependencies
...
testCompile project (path: ':ProjectA', configuration: 'testArchives')
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 添加了几行文字 总之,关于新的gradle插件的信息已经提供了。 @demon101 在 Gradle 4.6 中不工作,出现错误Could not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
【参考方案8】:
当我尝试构建一个 android 项目(gradle 2.2.0)时,Fesler 的解决方案对我不起作用。 所以我不得不手动引用所需的类:
android
sourceSets
androidTest
java.srcDir project(':A').file("src/androidTest/java")
test
java.srcDir project(':A').file("src/test/java")
【讨论】:
轻微错字,在项目(':A')之后缺少结束引号。不过这对我有用,谢谢 m8 对于 Android,这个想法对我来说效果很好,没有 ***.com/a/50869037/197141 的 hacky 感觉 @arberg 是的,似乎是一个好方法。我看到的唯一限制是@VisibleForTesting
lint 规则。您将无法从非测试文件夹下的常规模块调用此类方法。【参考方案9】:
我参加聚会太晚了(现在是 Gradle v4.4),但对于发现这个的其他人来说:
假设:
~/allProjects
|
|-/ProjectA/module-a/src/test/java
|
|-/ProjectB/module-b/src/test/java
转到项目 B 的 build.gradle
(需要 A 中的一些测试类的那个)并添加以下内容:
sourceSets
String sharedTestDir = "$projectDir"+'/module-b/src/test/java'
test
java.srcDir sharedTestDir
或(假设您的项目名为ProjectB
)
sourceSets
String sharedTestDir = project(':ProjectB').file("module-b/src/test/java")
test
java.srcDir sharedTestDir
瞧!
【讨论】:
这个问题没有提到Android。您能否就开发者是否为 Android 开发,还是仅针对 Android 开发者做出不可知论的回答? 我在一个有周期的项目上工作,所以这个答案是唯一一个或多或少干净的答案。作为参考,我把来自projectExposingTests
sn-p(kotlin 版本)的需要测试的项目放在:sourceSets test java.srcDir(project(":projectExposingTests").file("src/test/java"))
【参考方案10】:
如果您使用的是 Kotlin DSL,则应根据 Gradle 文档创建类似的任务。
与之前的一些答案一样,您需要在项目中创建一个特殊的配置来共享其测试类,这样您就不会混合测试类和主类。
简单的步骤
-
在项目A中,您需要添加
build.gradle.kts
:
configurations
create("test")
tasks.register<Jar>("testArchive")
archiveBaseName.set("ProjectA-test")
from(project.the<SourceSetContainer>()["test"].output)
artifacts
add("test", tasks["testArchive"])
-
然后在你的项目B的依赖项中,你需要在你的
build.gradle.kts
中添加:
dependencies
implementation(project(":ProjectA"))
testImplementation(project(":ProjectA", "test"))
【讨论】:
如上所述,另一个答案如下:只是认为是的,配置应该真正命名为 test,仅此而已(否则 IDE 可能难以获取对测试源)。【参考方案11】:为 Gradle 6.6.x 创建测试罐
我知道有很多消息来源告诉你,那不行,fe:
https://github.com/gradle/gradle/issues/11280 https://gradle.org/whats-new/gradle-6/#better-builds但这太他妈简单了,我只是不喜欢在testFixtures
文件夹中单独拥有通用测试类的想法。
所以在模块 A 中:
task jarTests(type: Jar, dependsOn: testClasses)
classifier = 'tests'
from sourceSets.test.output
configurations
tests
extendsFrom testRuntime
artifacts
tests jarTests
在模块 B 中:
testImplementation project(':moduleA')
testImplementation project(path: ':moduleA', configuration: 'tests')
而且效果很好!
【讨论】:
IntelliJ 用户注意事项:使用此方法时,请确保将配置命名为test
。 gradle 命令行使用任何名称都可以正常工作,但至少 IntelliJ 可以更好地使用“test”(因为它会理解依赖实际上是对测试源的依赖,所以您不希望 IDE“真正”依赖于输出 jar!)
【参考方案12】:
如果你想使用 artifact 依赖有:
ProjectB 的源类依赖于 Project A 的源类 ProjectB 的测试类依赖于 Project A 的测试类那么 build.gradle 中 ProjectB 的依赖项部分应如下所示:
dependencies
compile("com.example:projecta:1.0.0")
testCompile("com.example:projecta:1.0.0:tests")
为此,ProjectA 需要构建一个 -tests jar 并将其包含在它生成的工件中。
ProjectA 的 build.gradle 应该包含如下配置:
task testsJar(type: Jar, dependsOn: testClasses)
classifier = 'tests'
from sourceSets.test.output
configurations
tests
artifacts
tests testsJar
archives testsJar
jar.finalizedBy(testsJar)
当 ProjectA 的工件发布到您的工件时,它们将包含一个 -tests jar。
ProjectB 的依赖项部分中的 testCompile 将引入 -tests jar 中的类。
如果您想在 ProjectB 中includeFlat ProjectA 的源代码和测试类以用于开发目的,那么 ProjectB 的 build.gradle 中的依赖项部分将如下所示:
dependencies
compile project(':projecta')
testCompile project(path: ':projecta', configuration: 'tests')
【讨论】:
不幸的是(在 Gradle 6 中)平面包含,这正是我想要的,不再工作,因为不再有配置“测试”。使用println(configurations.joinToString("\n") it.name + " - " + it.allDependencies.joinToString() )
(在 kotlin 构建脚本中),我确定了哪些配置仍然存在并且具有依赖关系,但是对于所有这些 Gradle 抱怨:Selected configuration 'testCompileClasspath' on 'project :sdk' but it can't be used as a project dependency because it isn't intended for consumption by other components.
此解决方案适用于 Gradle 7.0【参考方案13】:
如果您有需要在测试之间共享的模拟依赖项,您可以创建新项目projectA-mock
,然后将其作为测试依赖项添加到ProjectA
和ProjectB
:
dependencies
testCompile project(':projectA-mock')
这是共享模拟依赖项的明确解决方案,但如果您需要在 ProjectB
中从 ProjectA
运行测试,请使用其他解决方案。
【讨论】:
共享模拟案例的绝佳解决方案!【参考方案14】:Nikita 提到的 Android + Kotlin 的解决方案如下所示:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest")
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
configurations
unitTestArtifact
artifacts
unitTestArtifact jarTests
将使用依赖项的项目的 Gradle:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
【讨论】:
为我工作。谢谢【参考方案15】:其他一些答案以某种方式导致错误 - Gradle 未检测到来自其他项目的测试类或 Eclipse 项目在导入时具有无效的依赖项。如果有人遇到同样的问题,我建议使用:
testCompile project(':core')
testCompile files(project(':core').sourceSets.test.output.classesDir)
第一行强制 Eclipse 将其他项目链接为依赖项,因此所有源都包含在内并且是最新的。第二个允许 Gradle 实际查看源代码,同时不会像 testCompile project(':core').sourceSets.test.output
那样导致任何无效的依赖错误。
【讨论】:
【参考方案16】:如果您正在努力使解决方案适应 Gradle Kotlin DSL,这是等效的:
configurations
register("testClasses")
extendsFrom(testImplementation.get())
val testJar = tasks.register<Jar>("testJar")
archiveClassifier.set("test")
from(sourceSets.test)
artifacts.add("testClasses", testJar)
【讨论】:
【参考方案17】:在项目 B 中:
dependencies
testCompile project(':projectA').sourceSets.test.output
似乎在 1.7-rc-2 中工作
【讨论】:
它还会在 Eclipse 处理项目时产生不必要的复杂性。 @NikitaSkvortsov 建议的解决方案更可取。以上是关于使用 gradle 的多项目测试依赖项的主要内容,如果未能解决你的问题,请参考以下文章
多模块 Spring Boot 项目中的 Gradle 依赖插件
Spring Boot Gradle 多项目构建在测试期间看不到内部依赖项
Gradle:如何在java中以编程方式声明对项目特定配置的依赖关系