Phụ thuộc kiểm tra đa dự án với gradle


153

Tôi có một cấu hình đa dự án và tôi muốn sử dụng gradle.

Các dự án của tôi là như thế này:

  • Dự án A

    • -> src/main/java
    • -> src/test/java
  • Dự án B

    • -> src/main/java(phụ thuộc src/main/javavào Dự án A )
    • -> src/test/java(phụ thuộc src/test/javavào Dự án A )

Tệp Project B của tôi build.gradlegiống như thế này:

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

Nhiệm vụ compileJavalớn công việc nhưng compileTestJavakhông biên dịch các tập tin thử nghiệm từ Dự án A .


Câu trả lời:


122

Không dùng nữa - Đối với Lớp 5.6 trở lên, hãy sử dụng câu trả lời này .

Trong dự án B , bạn chỉ cần thêm một testCompilephụ thuộc:

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

Đã thử nghiệm với Lớp 1.7.


7
Hóa ra thuộc tính lớp bị phản đối - thay vào đó hãy sử dụng đầu ra.
Fesler

12
Điều này không hoạt động trong Lớp 1.3 vì sourceSets không còn là tài sản công cộng của dự án.
David Pärsson

3
Hãy ghi nhớ giải pháp trên yêu cầu ít nhất một gradle testClassestrước khi cấu trúc xây dựng thực sự hợp lệ. Ví dụ, plugin Eclipse sẽ không cho phép bạn nhập dự án trước đó. Nó thực sự là một sự xấu hổ testCompile project(':A')không hoạt động. @ DavidPärsson: "Lớp 1.3" mâu thuẫn "không còn" kể từ khi Fesler thử nghiệm với Lớp 1.7.
Patrick Bergner

3
không làm việc cho tôi. Thất bại với sự phụ thuộc vòng tròn: compileTestJava \ ---: testClass \ ---: compileTestJava (*)
rahulmohan

8
Đừng làm điều này, các dự án không được phép tiếp cận với các dự án khác. Thay vào đó hãy sử dụng câu trả lời của Nikita, mô hình chính xác điều này như một sự phụ thuộc của dự án.
Stefan Oehme

63

Cách đơn giản là thêm phụ thuộc nhiệm vụ rõ ràng trong ProjectB:

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

Cách khó (nhưng rõ ràng hơn) là tạo cấu hình tạo tác bổ sung cho ProjectA:

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

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

và thêm testCompilephụ thuộc cho ProjectB

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

3
Tôi đã thử cách này (cách đơn giản) và trong khi điều đó chắc chắn rằng nó xây dựng testClass, nó không thêm đường dẫn thử nghiệm vào CLASSPATH để các thử nghiệm ProjectB của tôi phụ thuộc vào các lớp kiểm tra ProjectA vẫn không thể xây dựng.
pjz

1
@dmoebius bạn phải thêm testArtifactscấu hình như thế này: configurations { testArtifacts } để biết thêm chi tiết, hãy xem phần này của trợ giúp Gradle: gradle.org/docs/civerse/dsl/ Kẻ
Nikita Skvortsov

7
Trong Lớp 1.8, bạn có thể muốn from sourceSets.test.outputvà có thể classifier = 'tests'thay // pack whatever you need...thế câu trả lời
Peter Lamberg

1
Xác nhận rằng với Gradle 1.12 sử dụng giải pháp đầy đủ, với @PeterLamberg đề xuất bổ sung hoạt động như mong đợi. Không ảnh hưởng đến việc nhập dự án vào Eclipse.
phù hợp với

3
Điều này làm việc cho tôi trong Lớp 4.7. Bây giờ họ có một số tài liệu về cách tiếp cận tại docs.gradle.org/civerse/dsl/ Khăn
Nathan Williams

19

Điều này hiện được hỗ trợ như là một tính năng hạng nhất trong Gradle. Các mô-đun có javahoặc java-librarybổ trợ cũng có thể bao gồm một java-test-fixturesplugin hiển thị các lớp trình trợ giúp và tài nguyên được sử dụng với trình testFixturestrợ giúp. Lợi ích của phương pháp này đối với các hiện vật và phân loại là:

  • quản lý phụ thuộc thích hợp (thực hiện / api)
  • tách đẹp từ mã kiểm tra (bộ nguồn riêng biệt)
  • không cần phải lọc ra các lớp kiểm tra để chỉ hiển thị các tiện ích
  • duy trì bởi Gradle

Thí dụ

:modul:one

modul / một / build.gradle

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

modul / one / src / testFixenses / java / com / example / Helper.java

package com.example;
public class Helper {}

:modul:other

modul / khác / 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
  }
}

đọc thêm

Để biết thêm thông tin, hãy xem tài liệu:
https://docs.gradle.org/civerse/userguide/java_testing.html#sec:java_test_fixenses

Nó đã được thêm vào trong 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixenses-for-java-projects


Họ đang làm việc vào việc hỗ trợ này trên Android, xem issuetracker.google.com/issues/139762443issuetracker.google.com/issues/139438142
Albert Vila Calvo

18

Gần đây tôi đã gặp vấn đề này và con người đây là một vấn đề khó khăn để tìm câu trả lời.

Sai lầm bạn đang mắc phải là nghĩ rằng một dự án nên xuất các phần tử thử nghiệm của nó giống như cách nó xuất các thành phần chính và phụ thuộc của nó.

Những gì tôi đã có nhiều thành công hơn với cá nhân là thực hiện một dự án mới ở Gradle. Trong ví dụ của bạn, tôi sẽ đặt tên cho nó

Dự án A_Test -> src / main / java

Tôi sẽ đưa vào src / main / java các tệp mà bạn hiện có trong Project A / src / test / java. Tạo bất kỳ phụ thuộc testCompile nào trong Project A biên dịch phụ thuộc của Project A_Test.

Sau đó, biến Project A_Test thành một phụ thuộc testCompile của Project B.

Điều đó không hợp lý khi bạn nhìn nhận nó từ quan điểm của tác giả của cả hai dự án, nhưng tôi nghĩ nó có ý nghĩa khi bạn nghĩ về các dự án như Junit và scalatest (và các dự án khác. Mặc dù các khung đó có liên quan đến thử nghiệm, nhưng chúng không được coi là một phần của các mục tiêu "thử nghiệm" trong các khung riêng của chúng - chúng tạo ra các tạo phẩm chính mà các dự án khác chỉ sử dụng trong cấu hình thử nghiệm của chúng. Bạn chỉ muốn theo mô hình tương tự.

Cố gắng thực hiện các câu trả lời khác được liệt kê ở đây không hiệu quả với cá nhân tôi (sử dụng Lớp 1.9), nhưng tôi đã thấy rằng mô hình mà tôi mô tả ở đây là một giải pháp sạch hơn.


Có, đã chọn cách tiếp cận này vào cuối ngày.
koma

Đây là cách tiếp cận tốt nhất! Ngoại trừ tôi sẽ giữ mã kiểm tra trong dự án A và chỉ di chuyển các phụ thuộc cho cả A src / test / java và B src / test / java sang A_Test. Sau đó, biến Project A_Test thành một phụ thuộc thử nghiệm của cả A và B.
Erik Sillén

17

Tôi biết đó là một câu hỏi cũ nhưng tôi cũng gặp vấn đề tương tự và dành thời gian để tìm hiểu chuyện gì đang xảy ra. Tôi đang sử dụng Lớp 1.9. Tất cả các thay đổi phải có trong ProjectB'sbuild.gradle

Để sử dụng các lớp kiểm tra từ ProjectA trong các thử nghiệm của ProjectB:

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

Để đảm bảo rằng sourceSetstài sản có sẵn cho ProjectA:

evaluationDependsOn(':ProjectA')

Để đảm bảo các lớp kiểm tra từ ProjectA thực sự ở đó, khi bạn biên dịch ProjectB:

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

1
Điều này cũng làm việc cho tôi ngoại trừ tôi phải bỏ qua .classesDir.

11

Giải pháp dựa trên testJar mới (hỗ trợ phụ thuộc ba lần) có sẵn dưới dạng plugin gradle:

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

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

Từ tài liệu

Trong trường hợp bạn có một bản dựng lớp đa dự án, bạn có thể có các phụ thuộc kiểm tra giữa các dự án con (có thể là một gợi ý rằng các dự án của bạn không có cấu trúc tốt).

Ví dụ, giả sử một dự án trong đó Dự án B phụ thuộc vào Dự án A và B không chỉ có một phụ thuộc biên dịch vào A mà còn phụ thuộc vào kiểm tra. Để biên dịch và chạy các bài kiểm tra của B, chúng ta cần một số lớp trợ giúp kiểm tra từ A.

Theo mặc định, lớp không tạo ra một tạo phẩm jar từ đầu ra xây dựng thử nghiệm của một dự án.

Plugin này thêm cấu hình testArchives (dựa trên testCompile) và tác vụ jarTest để tạo một jar từ bộ nguồn kiểm tra (với kiểm tra phân loại được thêm vào tên của jar). Sau đó chúng ta có thể phụ thuộc vào B vào cấu hình testArchives của A (cũng sẽ bao gồm các phụ thuộc bắc cầu của A).

Trong A, chúng tôi sẽ thêm plugin vào build.gradle:

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

Trong B, chúng tôi tham chiếu cấu hình testArchives như thế này:

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

1
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi. - Từ đánh giá
Ian

một vài dòng văn bản đã được thêm vào
dem101

Dù sao, thông tin về plugin gradle mới đã được cung cấp.
quỷ 101

4
@ devil101 Không hoạt động trong Lớp 4.6, bị lỗiCould not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
Vignesh Sundar

11

Xin vui lòng đọc bản cập nhật dưới đây.

Các vấn đề tương tự được mô tả bởi JustAClulessNewbie xảy ra trong IntelliJ IDEA. Vấn đề là testCompile project(':core').sourceSets.test.outputsự phụ thuộc thực sự có nghĩa là: "phụ thuộc vào các lớp được tạo bởi nhiệm vụ xây dựng lớp". Vì vậy, nếu bạn mở dự án sạch nơi các lớp chưa được tạo, IDEA sẽ không nhận ra chúng và báo cáo lỗi.

Để khắc phục vấn đề này, bạn phải thêm một phụ thuộc vào các tệp nguồn kiểm tra bên cạnh sự phụ thuộc vào các lớp được biên dịch.

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

Bạn có thể quan sát các phụ thuộc được IDEA nhận ra trong Cài đặt mô-đun -> Phụ thuộc (phạm vi kiểm tra) .

Btw. Đây không phải là giải pháp tốt vì vậy tái cấu trúc là đáng để xem xét. Bản thân Gradle chỉ có các tiểu dự án đặc biệt chỉ chứa các lớp hỗ trợ kiểm tra. Xem https://docs.gradle.org/civerse/userguide/test_kit.html

Cập nhật 2016-06-05 Tôi đang nghĩ về giải pháp được đề xuất ít hơn tôi thích nó. Có một vài vấn đề với nó:

  1. Nó tạo ra hai phụ thuộc trong IDEA. Một điểm để kiểm tra nguồn khác cho các lớp biên dịch. Và điều quan trọng là thứ tự các phụ thuộc này được IDEA công nhận. Bạn có thể chơi với nó bằng cách thay đổi thứ tự phụ thuộc trong cài đặt Mô-đun -> tab Phụ thuộc.
  2. Bằng cách khai báo các phụ thuộc này, bạn đang làm ô nhiễm cấu trúc phụ thuộc không cần thiết.

Vậy đâu là giải pháp tốt hơn? Theo tôi, nó tạo ra bộ nguồn tùy chỉnh mới và đặt các lớp dùng chung vào nó. Trên thực tế, các tác giả của dự án Gradle đã làm điều đó bằng cách tạo tập hợp nguồn testFixenses.

Để làm điều đó bạn chỉ cần:

  1. Tạo bộ nguồn và thêm các cấu hình cần thiết. Kiểm tra plugin tập lệnh này được sử dụng trong dự án Gradle: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixenses.gradle
  2. Khai báo sự phụ thuộc thích hợp trong dự án phụ thuộc:

    dependencies {
        testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
    }
    
  3. Nhập dự án Gradle vào IDEA và sử dụng tùy chọn "tạo mô-đun riêng cho mỗi bộ nguồn" trong khi nhập.


1
@jannis cố định. Btw. Gradle đã chuyển plugin thử nghiệm dựa trên Groovy của mình sang dựa trên Kotlin mới: github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/
Václav Kužel 19/12/18

@ VáclavKužel Tôi tìm ra giải pháp thú vị của bạn thông qua bài đăng trên blog của bạn và nó đã giải quyết vấn đề của tôi rất độc đáo. Cảm ơn;)
zaerymoghaddam

10

Giải pháp của Fesler không hiệu quả với tôi, khi tôi thử nó để xây dựng một dự án Android (lớp 2.2.0). Vì vậy, tôi đã phải tham khảo các lớp yêu cầu bằng tay:

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

1
lỗi đánh máy nhẹ, thiếu dấu ngoặc kép sau dự án (': A'). Điều này làm việc cho tôi mặc dù, cảm ơn m8
Ryan Newsom

1
Đối với Android, ý tưởng này hoạt động rất tốt đối với tôi, không có cảm giác khó chịu stackoverflow.com/a/50869037/197141
arberg

@arberg Vâng, có vẻ như là một cách tiếp cận tốt. Giới hạn duy nhất tôi thấy là với các @VisibleForTestingquy tắc lint. Bạn sẽ không thể gọi các phương thức như vậy từ mô-đun thông thường trong thư mục không kiểm tra.
Beloo

5

Tôi đến bữa tiệc quá muộn (bây giờ là Gradle v4.4) nhưng đối với bất kỳ ai khác tìm thấy điều này:

Giả định:

~/allProjects
|
|-/ProjectA/module-a/src/test/java
|
|-/ProjectB/module-b/src/test/java

Chuyển đến build.gradle của dự án B (lớp cần một số lớp kiểm tra từ A) và thêm vào như sau:

sourceSets {
    String sharedTestDir = "${projectDir}"+'/module-b/src/test/java'
    test {
        java.srcDir sharedTestDir
    }
}

hoặc (giả sử dự án của bạn được đặt tên là "ProjectB")

sourceSets {
    String sharedTestDir = project(':ProjectB').file("module-b/src/test/java")
    test {
        java.srcDir sharedTestDir
    }
}

Voila!


3
Câu hỏi không đề cập đến Android. Bạn có thể đưa ra câu trả lời của mình không biết liệu nhà phát triển có đang phát triển cho Android hay không, hay nó chỉ dành cho các nhà phát triển Android?
Robin Green

4

Nếu bạn có các phụ thuộc giả mà bạn cần chia sẻ giữa các thử nghiệm, bạn có thể tạo dự án mới projectA-mockvà sau đó thêm nó dưới dạng phụ thuộc thử nghiệm vào ProjectAProjectB:

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

Đây là giải pháp rõ ràng để chia sẻ các phụ thuộc giả, nhưng nếu bạn cần chạy thử nghiệm từ ProjectAviệc ProjectBsử dụng giải pháp khác.


Giải pháp tuyệt vời cho trường hợp giả được chia sẻ!
Erik Sillén

4

Nếu bạn muốn sử dụng các phụ thuộc giả để có:

  • Các lớp nguồn của ProjectB phụ thuộc vào các lớp nguồn của Project A
  • Các lớp kiểm tra của ProjectB phụ thuộc vào các lớp kiểm tra của Project A

sau đó phần phụ thuộc của ProjectB trong build.gradle sẽ trông giống như thế này:

dependencies {

  compile("com.example:projecta:1.0.0")

  testCompile("com.example:projecta:1.0.0:tests")

}

Để làm việc này, ProjectA cần xây dựng một -tests jar và bao gồm nó trong hiện vật nó tạo ra.

Build.gradle của ProjectA nên chứa cấu hình như thế này:

task testsJar(type: Jar, dependsOn: testClasses) {
    classifier = 'tests'
    from sourceSets.test.output
}

configurations {
    tests
}

artifacts {
    tests testsJar
    archives testsJar
}

jar.finalizedBy(testsJar)

Khi các tạo phẩm của ProjectA được xuất bản tới vật phẩm của bạn, chúng sẽ bao gồm một -tests lọ .

Các testCompile trong phần phụ thuộc ProjectB sẽ mang lại các lớp trong -tests jar.


Nếu bạn muốn bao gồm các lớp nguồn và kiểm tra của ProjectA trong ProjectB cho mục đích phát triển thì phần phụ thuộc trong build.gradle của ProjectB sẽ giống như sau:

dependencies {

  compile project(':projecta')

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

}

1
Thật không may (ở Lớp 6) căn hộ bao gồm, chính xác là những gì tôi muốn, không hoạt động nữa vì không còn cấu hình 'kiểm tra' nữa. Sử dụng println(configurations.joinToString("\n") { it.name + " - " + it.allDependencies.joinToString() })(trong bản dựng kotlin), tôi đã xác định cấu hình nào vẫn tồn tại và có phụ thuộc, nhưng đối với tất cả các Gradle này đã phàn nàn: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

Một số câu trả lời khác gây ra lỗi theo cách này hay cách khác - Gradle không phát hiện các lớp kiểm tra từ các dự án khác hoặc dự án Eclipse có các phụ thuộc không hợp lệ khi được nhập. Nếu bất cứ ai có cùng một vấn đề, tôi khuyên bạn nên đi với:

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

Dòng đầu tiên buộc Eclipse liên kết dự án khác với tư cách phụ thuộc, vì vậy tất cả các nguồn được bao gồm và cập nhật. Thứ hai cho phép Gradle thực sự nhìn thấy các nguồn, trong khi không gây ra bất kỳ lỗi phụ thuộc không hợp lệ nào như testCompile project(':core').sourceSets.test.output.


2

Ở đây nếu bạn đang sử dụng DSL DSL , bạn nên tạo nhiệm vụ của mình như thế theo Gradle tài liệu .

Giống như một số câu trả lời trước đó, bạn cần tạo một cấu hình đặc biệt bên trong dự án sẽ chia sẻ lớp thử nghiệm của nó, để bạn không trộn lẫn các lớp thử nghiệm và lớp chính.

Các bước đơn giản

  1. Trong dự án A, bạn sẽ cần thêm vào 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. Sau đó, trong dự án B của bạn trong phần phụ thuộc, bạn sẽ cần thêm vào build.gradle.kts:
dependencies {
    implementation(project(":ProjectA"))
    testImplementation(project(":ProjectA", "test"))
}

-1

trong dự án B:

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

Có vẻ hoạt động trong 1.7-rc-2


2
Nó cũng tạo ra các phức tạp không cần thiết trong việc xử lý dự án của Eclipse. Giải pháp được đề xuất bởi @NikitaSkvortsov là tốt hơn.
phù hợp với
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.