gradle的多项目测试依赖


152

我有一个多项目配置,我想使用gradle。

我的项目是这样的:

  • 项目A

    • -> src/main/java
    • -> src/test/java
  • 项目B

    • - > src/main/java(取决于src/main/java项目A
    • - > src/test/java(取决于src/test/java项目A

我的Project B build.gradle文件是这样的:

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
}

任务compileJava工作正常,但是compileTestJava不会编译Project A的测试文件。


Answers:


122

不推荐使用-对于Gradle 5.6及更高版本,请使用此答案

Project B中,您只需要添加一个testCompile依赖项:

dependencies {
  ...
  testCompile project(':A').sourceSets.test.output
}

经过Gradle 1.7测试。


7
原来不推荐使用classes属性-而是使用output。
菲斯勒

12
这在Gradle 1.3中不起作用,因为sourceSets不再是项目的公共属性。
DavidPärsson13年

3
请记住,以上解决方案至少需要一个gradle testClasses构建结构才有效的提示。例如,Eclipse插件不允许您在此之前导入项目。真是丢脸testCompile project(':A')不起作用。@DavidPärsson:自Fesler用Gradle 1.7进行测试以来,“ 1.3级”与“不再”相矛盾。
Patrick Bergner 2014年

3
没有为我工作。未能通过循环依赖:compileTestJava \ ---:testClasses \ ---:compileTestJava(*)
rahulmohan

8
不要这样做,项目不应该进入其他项目。而是使用Nikita的答案,将其正确建模为项目依赖项。
Stefan Oehme '02

63

简单的方法是在ProjectB中添加显式任务依赖项:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')

困难(但更清晰)的方法是为ProjectA创建其他工件配置:

task myTestsJar(type: Jar) { 
  // pack whatever you need...
}

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

并添加testCompile对ProjectB 的依赖

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
  testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}

3
我尝试过这种方法(简单的方法),虽然可以确保它构建了testClasses,但是它没有将测试路径添加到CLASSPATH中,因此依赖于ProjectA测试类的ProjectB测试仍然无法构建。
pjz 2011年

1
@dmoebius,您必须添加这样的testArtifacts配置:configurations { testArtifacts } 有关更多详细信息,请参见Gradle帮助的这一部分:gradle.org/docs/current/dsl/…–
Nikita Skvortsov

7
在Gradle 1.8中,您可能想要from sourceSets.test.output并可能classifier = 'tests'代替// pack whatever you need...答案
Peter Lamberg

1
确认在使用Gradle 1.12的完整解决方案的情况下,通过@PeterLamberg提出了建议,可以按预期进行添加。不会影响将项目导入Eclipse。
Sfitts 2014年

3
这在Gradle 4.7中对我有用。他们现在有关于方法的一些文档在docs.gradle.org/current/dsl/...
弥敦道威廉姆斯

19

现在,Gradle中已将其作为一流功能支持。带有javajava-library插件的模块还可以包括一个java-test-fixtures插件,该插件公开帮助程序类和将与该testFixtures帮助程序一起使用的资源。这种方法对工件和分类器的好处是:

  • 适当的依赖项管理(实现/ API)
  • 与测试代码的良好分离(单独的源集)
  • 无需过滤测试类以仅公开实用程序
  • 由Gradle维护

:modul:one

模数/一个/ build.gradle

plugins {
  id "java-library" // or "java"
  id "java-test-fixtures"
}

模数/一/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")))
}

模组/其他/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



18

我本人最近遇到了这个问题,这是一个很难解决的难题。

您所犯的错误是认为项目应该以导出其主要工件和依赖项的相同方式来导出其测试元素。

我个人获得的更多成功是在Gradle中进行了一个新项目。在您的示例中,我将其命名

项目A_Test-> src / main / java

我会将您当前在Project A / src / test / java中拥有的文件放入src / main / java中。使项目的所有testCompile依赖项成为A_Test项目的编译依赖项。

然后使Project A_Test成为Project B的testCompile依赖项。

从两个项目的作者的角度来看这都是不合逻辑的,但是我认为当您考虑诸如junit和scalatest(以及其他项目)之类的项目时,这很有道理。即使这些框架与测试相关,在它们自己的框架中不被视为“测试”目标的一部分-它们产生主要项目,其他项目恰好在其测试配置中使用它们,您只想遵循相同的模式。

尝试执行此处列出的其他答案对我个人而言不起作用(使用Gradle 1.9),但是我发现我在此描述的模式仍然是一种更干净的解决方案。


是的,最终选择了这种方法。
科马2015年

这是最好的方法!除非我将测试代码保留在项目A中,并且仅将A src / test / java和B src / test / java的依赖项移至A_Test。然后,让项目A_Test既有的testImplementation依赖和B.
埃里克Sillén

17

我知道这是一个老问题,但是我也遇到了同样的问题,花了一些时间弄清楚发生了什么。我正在使用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')

1
这对我也有用,除了我不得不省略它.classesDir

11

基于gradle插件的新基于testJar的(支持传统依赖)解决方案:

https://github.com/hauner/gradle-plugins/tree/master/jartest

https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0

从文档

如果您具有多项目gradle构建,则子项目之间可能具有测试依赖性(这可能暗示您的项目结构不正确)。

例如,假设一个项目,其中子项目Project B依赖于Project A,而B不仅对A具有编译依赖关系,而且还具有对测试的依赖关系。要编译和运行B的测试,我们需要A的一些测试帮助程序类。

默认情况下,gradle不会从项目的测试构建输出中创建jar工件。

该插件添加了一个testArchives配置(基于testCompile)和一个jarTest任务,以根据测试源集创建一个jar(在分类器名称中添加了分类器test)。然后,我们可以在B中依赖A的testArchives配置(这还将包括A的传递依赖项)。

在A中,我们将插件添加到build.gradle中:

apply plugin: 'com.github.hauner.jarTest'

在B中,我们引用testArchives配置,如下所示:

dependencies {
    ...
    testCompile project (path: ':ProjectA', configuration: 'testArchives') 
}

1
尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。- 评分
伊恩·

添加了几行文字
demon101

无论如何,已经提供了有关新gradle插件的信息。
demon101

4
@ demon101在Gradle 4.6中不起作用,出现错误Could not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
Vignesh Sundar,

11

请阅读下面的更新。

JustACluelessNewbie描述的类似问题在IntelliJ IDEA中也会发生。问题在于,依赖关系testCompile project(':core').sourceSets.test.output实际上意味着:“依赖于gradle构建任务生成的类”。因此,如果您打开未生成类的干净项目,则IDEA将无法识别它们并报告错误。

若要解决此问题,您必须在对编译类的依赖旁边添加对测试源文件的依赖。

// First dependency is for IDEA
testCompileOnly files { project(':core').sourceSets.test.java.srcDirs }
// Second is for Gradle
testCompile project(':core').sourceSets.test.output

您可以在模块设置->依赖关系(测试范围)中观察IDEA识别的依赖关系

顺便说一句。这不是一个很好的解决方案,因此重构值得考虑。Gradle本身确实具有仅包含测试支持类的特殊子项目。参见https://docs.gradle.org/current/userguide/test_kit.html

2016年6月5日更新 更多我正在考虑提议的解决方案,我不太喜欢。它有几个问题:

  1. 它在IDEA中创建两个依赖项。一个要测试的源代码指向编译类。IDEA识别这些依赖关系的顺序至关重要。您可以通过在“模块设置”->“依赖关系”选项卡中更改依赖关系顺序来使用它。
  2. 通过声明这些依赖关系,您不必要地污染了依赖关系结构。

那么什么是更好的解决方案?在我看来,它正在创建新的自定义源集并将共享类放入其中。实际上,Gradle项目的作者是通过创建testFixtures源集来实现的。

为此,您只需要:

  1. 创建源集并添加必要的配置。检查Gradle项目中使用的此脚本插件:https : //github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
  2. 在依赖项目中声明适当的依赖项:

    dependencies {
        testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
    }
    
  3. 将Gradle项目导入IDEA,并在导入时使用“为每个源集创建单独的模块”选项。


1
@jannis已修复。顺便说一句。摇篮移动了它的基于Groovy测试夹具插件来新科特林基于:github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/...
瓦茨拉夫Kužel

@VáclavKužel我通过您的博客文章找到了您感兴趣的解决方案,它很好地解决了我的问题。谢谢;)
zaerymoghaddam

10

当我尝试使用Fesler的解决方案构建一个Android项目(2.2.0版)时,该解决方案对我没有用。所以我不得不手动引用所需的类:

android {
    sourceSets {
        androidTest {
            java.srcDir project(':A').file("src/androidTest/java")
        }
        test {
            java.srcDir project(':A').file("src/test/java")
        }
    }
}

1
略有错字,在项目(':A')之后缺少结尾引号。不过,这对我有用,谢谢m8
Ryan Newsom

1
对于Android来说,这个想法对我来说效果非常好,没有
让人讨厌的

@arberg是的,这似乎是个好方法。我看到的唯一限制是@VisibleForTesting皮棉规则。您将无法从not test文件夹下的常规模块调用此类方法。
Beloo

5

我参加聚会太晚了(现在是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
    }
}

瞧!


3
这个问题没有提到Android。您是否可以对开发人员是否为Android开发,还是仅针对Android开发人员不作回答?
罗宾·格林

4

如果您需要在测试之间共享模拟依赖项,则可以创建新项目projectA-mock,然后将其作为测试依赖项添加到ProjectAProjectB

dependencies {
  testCompile project(':projectA-mock')
}

这是共享模拟依赖项的明确解决方案,但是如果您需要ProjectAProjectB使用其他解决方案的情况下运行测试。


共享模拟案例的绝佳解决方案!
ErikSillén19年

4

如果要使用工件依赖项,请执行以下操作:

  • 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的“ dependencies”部分中的testCompile将在-tests jar中引入这些类。


如果你想includeFlat项目A的来源和测试类的项目B为发展目的则依赖于项目B的部分的build.gradle是这样的:

dependencies {

  compile project(':projecta')

  testCompile project(path: ':projecta', configuration: 'tests')

}

1
不幸的是(在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.
Xerus

2

其他一些答案会以一种或另一种方式导致错误-Gradle无法检测到其他项目中的测试类,或者Eclipse项目在导入时具有无效的依赖关系。如果有人遇到同样的问题,我建议继续:

testCompile project(':core')
testCompile files(project(':core').sourceSets.test.output.classesDir)

第一行强制Eclipse将其他项目作为依赖项进行链接,因此包括所有源代码并保持最新状态。第二个允许Gradle实际看到源,而不会像这样testCompile project(':core').sourceSets.test.output做那样引起任何无效的依赖项错误。


2

如果您使用的是Kotlin DSL,则应根据Gradle文档创建任务。

像以前的答案一样,您需要在项目内部创建一个特殊的配置,以共享其测试类,以免混淆测试类和主类。

简单步骤

  1. 项目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"])
}
  1. 然后在您的项目B中的依赖项中,您将需要添加build.gradle.kts
dependencies {
    implementation(project(":ProjectA"))
    testImplementation(project(":ProjectA", "test"))
}

-1

在项目B中:

dependencies {
  testCompile project(':projectA').sourceSets.test.output
}

似乎可以在1.7-rc-2中使用


2
这也会在Eclipse处理项目时造成不必要的麻烦。@NikitaSkvortsov建议的解决方案是更可取的。
Sfitts
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.