Багатопроектні тестові залежності зі ступенем


153

У мене конфігурація мультипроекту, і я хочу використовувати gradle.

Мої проекти такі:

  • Проект А

    • -> src/main/java
    • -> src/test/java
  • Проект B

    • -> src/main/java(залежить src/main/javaвід проекту A )
    • -> src/test/java(залежить src/test/javaвід проекту A )

Мій файл проекту B build.gradle такий:

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

Завдання compileJavaроботи великий , але compileTestJavaНЕ компілювати тестовий файл з проекту A .


Відповіді:


122

Застаріле - для Gradle 5.6 і вище використовуйте цю відповідь .

У проекті B просто потрібно додати testCompileзалежність:

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

Тестували з Gradle 1.7.


7
Виявляється властивість класів застаріло - замість цього використовуйте вихід.
Феслер

12
Це не працює в Gradle 1.3, оскільки sourceSets більше не є публічною власністю проекту.
David Pärsson

3
Майте на увазі, що вищевказане рішення вимагає принаймні до gradle testClassesтого, як структура збірки буде дійсно дійсною. Наприклад, плагін Eclipse не дозволить вам імпортувати проект до цього. Це справді соромно testCompile project(':A')не працює. @ DavidPärsson: "Gradle 1.3" суперечить "більше", оскільки Феслер тестував Gradle 1.7.
Патрік Бергнер

3
не працювало для мене. Помилка з круговою залежністю: compileTestJava \ ---: testClasses \ ---: compileTestJava (*)
rahulmohan

8
Не робіть цього, проекти не повинні охоплювати інші проекти. Замість цього використовуйте відповідь Нікіти, правильно моделюючи це як залежність від проекту.
Стефан Оме

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
Я спробував це (простий спосіб), і хоча це гарантує, що він будує тестові класи, він не додає тестовий шлях до CLASSPATH, тому мої тести ProjectB, які залежать від тестових класів ProjectA, все ще не вдається скласти.
pjz

1
@dmoebius вам потрібно додати testArtifactsконфігурацію так: configurations { testArtifacts } детальніше дивіться цей розділ довідки Gradle: gradle.org/docs/current/dsl/…
Микита Скворцов

7
У Gradle 1.8 ви, from sourceSets.test.outputможливо, classifier = 'tests'замість // pack whatever you need...відповіді
Пітер Ламберг

1
Підтвердив, що з Gradle 1.12 з використанням повного рішення, @PeterLamberg запропонував доповнення працює, як очікувалося. Не впливає на імпорт проекту в Eclipse.
sfitts

3
Це працює для мене в Gradle 4.7. Зараз у них є деякі документи про підхід на docs.gradle.org/current/dsl/…
Натан Вільямс

19

Зараз це підтримується як функція першого класу в Gradle. Модулі з javaабо java-libraryплагінами можуть також включати java-test-fixturesплагін, який розкриває допоміжні класи та ресурси, які використовуються для testFixturesпомічника. Перевагою такого підходу щодо артефактів та класифікаторів є:

  • належне управління залежністю (впровадження / api)
  • приємне відрив від тестового коду (окремий набір джерел)
  • не потрібно фільтрувати тестові класи, щоб відкрити лише утиліти
  • підтримується Градле

Приклад

: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

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
Albert Vila Кальво

18

Я сам нещодавно зіткнувся з цією проблемою, і людині це важкі питання, на які можна знайти відповіді.

Помилка, яку ви робите, - це думка, що проект повинен експортувати свої тестові елементи так само, як експортувати свої основні артефакти та залежності.

Що я особисто мав набагато більше успіхів, то робив новий проект у Gradle. У вашому прикладі я би назвав це

Проект A_Test -> src / main / java

Я б поклав у src / main / java файли, які у вас зараз є у Project A / src / test / java. Створіть будь-які залежності testCompile свого проекту. Компілюйте залежності Project A_Test.

Тоді зробіть Project A_Test тестовою залежністю проекту B.

Це не логічно, коли ви підходите до цього з точки зору автора обох проектів, але я думаю, що це має багато сенсу, коли ви думаєте про проекти, такі як junit та scalatest (та ін. Хоча ці рамки пов'язані з тестуванням, вони не вважаються частиною "тестових" цілей у своїх власних рамках - вони створюють первинні артефакти, які інші проекти просто випадково використовуються в їх тестовій конфігурації.

Спроба зробити інші відповіді, перелічені тут, не працювала особисто для мене (використовуючи Gradle 1.9), але я виявив, що модель, яку я тут описую, все одно є чистішим рішенням.


Так, вирішили вирішити цей підхід наприкінці дня.
кома

Це найкращий підхід! За винятком того, що я б зберігав тестовий код у проекті A і переміщував би лише залежності як A src / test / java, так і B src / test / java до A_Test. Тоді зробіть Project A_Test тестовою залежністю виконання і A, і B.
Ерік Сіллен

17

Я знаю, що це старе питання, але у мене просто була та сама проблема і витратив деякий час на з'ясування того, що відбувається. Я використовую Gradle 1.9. Усі зміни повинні бути в проектіbuild.gradle

Для використання тестових класів ProjectA в тестах ProjectB:

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

Щоб переконатися, що sourceSetsвластивість доступна для ProjectA:

evaluationDependsOn(':ProjectA')

Щоб переконатися, що тестові класи від ProjectA насправді є, під час компіляції ProjectB:

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

1
Це також працювало для мене, за винятком того, що мені довелося опустити .classesDir.

11

Нове рішення, що базується на testJar (підтримується тринізована залежність) у вигляді плагіна:

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

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

З документації

Якщо у вас є багатопроектна збірка gradle, ви можете мати тестові залежності між підпроектами (що, мабуть, є натяком на те, що ваші проекти недостатньо структуровані).

Наприклад, припустимо проект, де підпроект Проект B залежить від проекту A і B має не лише залежність від компіляції від A, але і залежність від тесту. Для складання та запуску тестів B нам потрібні кілька тестових класів від A.

За замовчуванням gradle не створює артефакт jar із результатів тестової збірки проекту.

Цей плагін додає конфігурацію testArchives (на основі testCompile) та завдання jarTest створити банку з набору тестових джерел (при цьому тест класифікатора додається до назви jar). Тоді ми можемо залежати у B від конфігурації A testhiarchives A (яка також буде включати перехідні залежності 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.
Вігнеш Сундара

11

Прочитайте нижче оновлення.

Подібні проблеми, описані JustACluelessNewbie, виникають в IntelliJ IDEA. Проблема полягає в тому, що залежність testCompile project(':core').sourceSets.test.outputнасправді означає: "залежать від класів, породжених завданням gradile 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 в Налаштуваннях модуля -> Залежності (тестова область) .

Btw. це не приємне рішення, тому рефакторинг варто задуматися. У самому Gradle є спеціальний підпроект, що містить лише тестові класи підтримки. Дивіться https://docs.gradle.org/current/userguide/test_kit.html

Оновити 2016-06-05 Більше Я думаю про запропоноване рішення, менше мені це подобається. Проблем із цим мало:

  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 виправлено. Btw. Gradle перемістив свій плагін для тестових світильників на базі Groovy на новий Котлін: github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/…
Вацлав Кужель

@ VáclavKužel Я дізнаюся ваше цікаве рішення через ваш пост у блозі, і це дуже добре вирішило мою проблему. Спасибі;)
заерімогаддам

10

Рішення Феслера не спрацювало для мене, коли я спробував його створити проект для Android (gradle 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 ця ідея працювала прекрасно для мене, без Hacky почуття stackoverflow.com/a/50869037/197141
Аарберг

@arberg Так, це здається хорошим підходом. Єдине обмеження, яке я бачу, - це @VisibleForTestingправила ворсу. Ви не зможете викликати такі методи зі звичайного модуля в папці, що не тестується.
Белу

5

Я так спізнюсь на вечірку (зараз Gradle v4.4), але для всіх, хто знайде це:

Припустимо:

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

Перейдіть до build.gradle проекту B (той, якому потрібні деякі тестові класи від 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а потім додати його як тестову залежність до ProjectAта ProjectB:

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

Це зрозуміло рішення залежностей частка помилкових, але якщо вам потрібно запустити тести з ProjectAв ProjectBвикористанні іншого рішення.


Прекрасне рішення для спільного макету!
Ерік Сіллен

4

Якщо ви хочете використовувати артефактні залежності, щоб мати:

  • Джерела класів ProjectB залежать від початкових класів Project A
  • Тестові класи ProjectB залежать від тестових класів Project A

тоді розділ залежності ProjectB в build.gradle повинен виглядати приблизно так:

dependencies {

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

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

}

Для цього до роботи ProjectA необхідно побудувати - тести банку і включити його в артефактах , які вона виробляє.

Project.'s 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 будуть опубліковані у вашій артефактиці, вони включатимуть банку -тестів .

testCompile в залежності розділі ProjectB принесе в класах в - тестах баночки.


Якщо ви хочете включити джерело та тестові класиFlat ProjectA в ProjectB для цілей розвитку, тоді розділ залежності у build.gradle ProjectB виглядатиме так:

dependencies {

  compile project(':projecta')

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

}

1
На жаль (у Gradle 6) квартира включає, що було саме те, що я хотів, більше не працює, тому що "тестів" на конфігурацію вже немає. Використовуючи println(configurations.joinToString("\n") { it.name + " - " + it.allDependencies.joinToString() })(у kotlin buildscript), я визначив, які конфігурації все ще існують та мають залежності, але для всіх цих 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.