Dépendances de test multi-projets avec gradle

154

J'ai une configuration multi-projets et je souhaite utiliser gradle.

Mes projets sont comme ça:

  • Projet A

    • -> src/main/java
    • -> src/test/java
  • Projet B

    • -> src/main/java(dépend src/main/javadu projet A )
    • -> src/test/java(dépend src/test/javadu projet A )

Mon fichier Project B build.gradle est comme ceci:

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

La tâche compileJavagrand travail , mais le compileTestJavane compile pas le fichier de test du projet A .

mathd
la source
2
duplication possible: stackoverflow.com/questions/5144325/gradle-test-dependency
Mike Rylander

Réponses:

122

Obsolète - Pour Gradle 5.6 et supérieur, utilisez cette réponse .

Dans le projet B , il vous suffit d'ajouter une testCompiledépendance:

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

Testé avec Gradle 1.7.

Fesler
la source
7
Il s'avère que la propriété classes est obsolète - utilisez plutôt la sortie.
Fesler
12
Cela ne fonctionne pas dans Gradle 1.3 car sourceSets n'est plus une propriété publique d'un projet.
David Pärsson
3
Gardez à l'esprit que la solution ci-dessus nécessite au moins un gradle testClassesavant que la structure de construction ne soit réellement valide. Par exemple, le plugin Eclipse ne vous permettra pas d'importer le projet avant cela. C'est vraiment dommage que testCompile project(':A')ça ne marche pas. @ DavidPärsson: "Gradle 1.3" contredit "plus" depuis que Fesler a testé avec Gradle 1.7.
Patrick Bergner
3
n'a pas fonctionné pour moi. Échec de la dépendance circulaire: compileTestJava \ ---: testClasses \ ---: compileTestJava (*)
rahulmohan
8
Ne faites pas cela, les projets ne sont pas censés atteindre d'autres projets. Utilisez plutôt la réponse de Nikita, modélisant correctement ceci comme une dépendance de projet.
Stefan Oehme
63

Un moyen simple consiste à ajouter une dépendance de tâche explicite dans ProjectB:

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

Une méthode difficile (mais plus claire) consiste à créer une configuration d'artefact supplémentaire pour ProjectA:

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

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

et ajoutez la testCompiledépendance pour ProjectB

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
  testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
Nikita Skvortsov
la source
3
J'ai essayé cela (de manière simple) et bien que cela garantisse la construction des testClasses, cela n'ajoute pas le chemin de test au CLASSPATH, de sorte que mes tests ProjectB qui dépendent des classes de test ProjectA échouent toujours à construire.
pjz
1
@dmoebius vous devez ajouter une testArtifactsconfiguration comme celle-ci: configurations { testArtifacts } pour plus de détails, consultez cette section de l'aide de Gradle: gradle.org/docs/current/dsl/…
Nikita Skvortsov
7
Dans Gradle 1.8, vous voudrez peut from sourceSets.test.output-être et éventuellement classifier = 'tests'à la place de // pack whatever you need...dans la réponse
Peter Lamberg
1
Confirmé qu'avec Gradle 1.12, l'utilisation de la solution complète, avec @PeterLamberg, les ajouts suggérés fonctionnent comme prévu. N'affecte pas l'importation du projet dans Eclipse.
sfitts le
3
Cela fonctionne pour moi dans Gradle 4.7. Ils ont maintenant quelques documents sur l'approche à docs.gradle.org/current/dsl/…
Nathan Williams
19

Ceci est désormais pris en charge en tant que fonctionnalité de première classe dans Gradle. Les modules avec javaou java-libraryplugins peuvent également inclure un java-test-fixturesplugin qui expose des classes d'assistance et des ressources à consommer avec testFixtureshelper. Les avantages de cette approche contre les artefacts et les classificateurs sont:

  • bonne gestion des dépendances (implémentation / api)
  • belle séparation du code de test (ensemble de sources séparé)
  • pas besoin de filtrer les classes de test pour exposer uniquement les utilitaires
  • maintenu par Gradle

Exemple

:modul:one

modul / one / build.gradle

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

modul / one / src / testFixtures / java / com / exemple / Helper.java

package com.example;
public class Helper {}

:modul:other

modul / autre / build.gradle

plugins {
  id "java" // or "java-library"
}
dependencies {
  testImplementation(testFixtures(project(":modul:one")))
}

modul / autre / src / test / java / com / exemple / autre / SomeTest.java

package com.example.other;
import com.example.Helper;
public class SomeTest {
  @Test void f() {
    new Helper(); // used from :modul:one's testFixtures
  }
}

Lectures complémentaires

Pour plus d'informations, consultez la documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures

Il a été ajouté dans la version 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects

TWiStErRob
la source
Ils travaillent sur cette prise en charge sur Android, voir issuetracker.google.com/issues/139762443 et issuetracker.google.com/issues/139438142
Albert Vila Calvo
18

J'ai récemment rencontré ce problème moi-même, et l'homme est-ce un problème difficile auquel trouver des réponses.

L'erreur que vous faites est de penser qu'un projet doit exporter ses éléments de test de la même manière qu'il exporte ses principaux artefacts et dépendances.

Ce avec quoi j'ai eu beaucoup plus de succès personnellement, c'est de faire un nouveau projet à Gradle. Dans votre exemple, je le nommerais

Projet A_Test -> src / main / java

Je mettrais dans le src / main / java les fichiers que vous avez actuellement dans le projet A / src / test / java. Faites toutes les dépendances testCompile de votre projet A compiler les dépendances du projet A_Test.

Faites ensuite du projet A_Test une dépendance testCompile du projet B.

Ce n'est pas logique du point de vue de l'auteur des deux projets, mais je pense que cela a beaucoup de sens quand on pense à des projets comme junit et scalatest (et autres. Même si ces frameworks sont liés aux tests, ils ne sont pas considérés comme faisant partie des cibles "de test" dans leur propre cadre - ils produisent des artefacts primaires que d'autres projets utilisent simplement dans leur configuration de test. Vous voulez simplement suivre le même modèle.

Essayer de faire les autres réponses énumérées ici n'a pas fonctionné pour moi personnellement (en utilisant Gradle 1.9), mais j'ai trouvé que le modèle que je décris ici est de toute façon une solution plus propre.

Martin Snyder
la source
Oui, j'ai opté pour cette approche à la fin de la journée.
koma
C'est la meilleure approche! Sauf que je garderais le code de test dans le projet A et ne déplacerais que les dépendances pour A src / test / java et B src / test / java vers A_Test. Ensuite, faites du projet A_Test un testDépendance de mise en œuvre de A et B.
Erik Sillén
17

Je sais que c'est une vieille question, mais j'ai juste eu le même problème et j'ai passé du temps à comprendre ce qui se passe. J'utilise Gradle 1.9. Tous les changements doivent être dans ProjectBbuild.gradle

Pour utiliser les classes de test de ProjectA dans les tests de ProjectB:

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

Pour vous assurer que cette sourceSetspropriété est disponible pour ProjectA:

evaluationDependsOn(':ProjectA')

Pour vous assurer que les classes de test de ProjectA sont réellement présentes, lorsque vous compilez ProjectB:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Dominik Pawlak
la source
1
Cela a également fonctionné pour moi, sauf que j'ai dû omettre le .classesDir.
11

Nouvelle solution basée sur testJar (dépendances trnsitive prises en charge) disponible en tant que plugin gradle:

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

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

De la documentation

Dans le cas où vous avez une version multi-projets gradle, vous pouvez avoir des dépendances de test entre les sous-projets (ce qui est probablement un indice que vos projets ne sont pas bien structurés).

Par exemple, supposons un projet où le sous-projet Projet B dépend du projet A et B n'a pas seulement une dépendance de compilation sur A mais également une dépendance de test. Pour compiler et exécuter les tests de B, nous avons besoin de quelques classes d'assistance de test de A.

Par défaut, gradle ne crée pas d'artefact jar à partir de la sortie de génération de test d'un projet.

Ce plugin ajoute une configuration testArchives (basée sur testCompile) et une tâche jarTest pour créer un fichier jar à partir de l'ensemble de sources de test (avec le test du classificateur ajouté au nom du fichier jar). On peut alors dépendre en B de la configuration testArchives de A (qui inclura également les dépendances transitives de A).

Dans A, nous ajouterions le plugin à build.gradle:

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

En B, nous référençons la configuration testArchives comme ceci:

dependencies {
    ...
    testCompile project (path: ':ProjectA', configuration: 'testArchives') 
}
démon101
la source
1
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change. - De l'avis
Ian
quelques lignes de texte ont été ajoutées
demon101
Quoi qu'il en soit, des informations sur le nouveau plugin gradle ont été fournies.
demon101
4
@ demon101 Ne fonctionne pas dans Gradle 4.6, obtention d'une erreurCould not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
Vignesh Sundaramoorthy
11

Veuillez lire la mise à jour ci-dessous.

Des problèmes similaires décrits par JustACluelessNewbie se produisent dans IntelliJ IDEA. Le problème est que la dépendance testCompile project(':core').sourceSets.test.outputsignifie en fait: "dépend des classes générées par la tâche de construction gradle". Donc, si vous ouvrez un projet propre où les classes ne sont pas encore générées, IDEA ne les reconnaîtra pas et signale une erreur.

Pour résoudre ce problème, vous devez ajouter une dépendance sur les fichiers source de test à côté de la dépendance sur les classes compilées.

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

Vous pouvez observer les dépendances reconnues par IDEA dans Paramètres du module -> Dépendances (champ de test) .

Btw. ce n'est pas une bonne solution, donc la refactorisation vaut la peine d'être envisagée. Gradle lui-même a un sous-projet spécial contenant uniquement des classes de support de test. Voir https://docs.gradle.org/current/userguide/test_kit.html

Mise à jour 05/06/2016 Plus Je pense à la solution proposée moins je l'aime. Il y a peu de problèmes avec cela:

  1. Il crée deux dépendances dans IDEA. L'un pointe vers les sources de test, l'autre vers les classes compilées. Et il est crucial dans quel ordre ces dépendances sont reconnues par IDEA. Vous pouvez jouer avec en changeant l'ordre des dépendances dans les paramètres du module -> onglet Dépendances.
  2. En déclarant ces dépendances, vous polluez inutilement la structure de dépendances.

Alors, quelle est la meilleure solution? À mon avis, il s'agit de créer un nouvel ensemble de sources personnalisé et d'y mettre des classes partagées. En fait, les auteurs du projet Gradle l'ont fait en créant un ensemble de sources testFixtures.

Pour ce faire, il vous suffit de:

  1. Créez un ensemble de sources et ajoutez les configurations nécessaires. Vérifiez ce plugin de script utilisé dans le projet Gradle: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
  2. Déclarez la dépendance appropriée dans le projet dépendant:

    dependencies {
        testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
    }
    
  3. Importez le projet Gradle dans IDEA et utilisez l'option "Créer un module séparé par ensemble de sources" lors de l'importation.
Václav Kužel
la source
1
@jann est corrigé. Btw. Gradle a déplacé son plugin de test fixtures basé sur Groovy vers le nouveau Kotlin basé: github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects
Václav Kužel
@ VáclavKužel J'ai trouvé votre solution intéressante via votre article de blog et cela a très bien résolu mon problème. Merci;)
zaerymoghaddam
10

La solution de Fesler n'a pas fonctionné pour moi, quand je l'ai essayé pour construire un projet Android (gradle 2.2.0). J'ai donc dû référencer manuellement les classes requises:

android {
    sourceSets {
        androidTest {
            java.srcDir project(':A').file("src/androidTest/java")
        }
        test {
            java.srcDir project(':A').file("src/test/java")
        }
    }
}
Beloo
la source
1
légère faute de frappe, il manque le guillemet de fin après le projet (': A'). Cela a fonctionné pour moi cependant, merci m8
Ryan Newsom
1
Pour Android, cette idée a fonctionné à merveille pour moi, sans le sentiment de hacky stackoverflow.com/a/50869037/197141
arberg
@arberg Oui, cela semble être une bonne approche. La seule limitation que je vois concerne les @VisibleForTestingrègles de la charpie. Vous ne pourrez pas appeler de telles méthodes à partir du module normal sous le dossier not test.
Beloo
5

Je suis si en retard à la fête (c'est maintenant Gradle v4.4) mais pour tous ceux qui trouvent ceci:

En supposant:

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

Accédez au build.gradle du projet B (celui qui nécessite des classes de test de A) et ajoutez ce qui suit:

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

ou (en supposant que votre projet s'appelle "ProjectB")

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

Voila!

tromperie
la source
3
La question ne mentionne pas Android. Pouvez-vous faire en sorte que votre réponse soit indépendante de la question de savoir si le développeur développe pour Android ou non, ou est-ce uniquement pour les développeurs Android?
Robin Green
4

Si vous avez des dépendances fictives que vous devez partager entre les tests, vous pouvez créer un nouveau projet projectA-mock, puis l'ajouter en tant que dépendance de test à ProjectAet ProjectB:

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

Il s'agit d'une solution claire pour partager des dépendances fictives, mais si vous devez exécuter des tests à partir ProjectAd'une ProjectBautre solution en cours d' utilisation.

Sylwano
la source
Excellente solution pour le cas simulé partagé!
Erik Sillén
4

Si vous souhaitez utiliser des dépendances d' artefact pour avoir:

  • Les classes source de ProjectB dépendent des classes source de Project A
  • Les classes de test de ProjectB dépendent des classes de test du projet A

alors la section des dépendances de ProjectB dans build.gradle devrait ressembler à ceci:

dependencies {

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

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

}

Pour que cela fonctionne, ProjectA doit construire un -tests jar et l'inclure dans les artefacts qu'il produit.

Le build.gradle de ProjectA doit contenir une configuration comme celle-ci:

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

configurations {
    tests
}

artifacts {
    tests testsJar
    archives testsJar
}

jar.finalizedBy(testsJar)

Lorsque les artefacts de ProjectA sont publiés dans votre artefact, ils incluent un pot -tests .

Le testCompile dans la section des dépendances de ProjectB introduira les classes dans le jar -tests .


Si vous souhaitez inclure les classes source et de test de Flat ProjectA dans ProjectB à des fins de développement, la section des dépendances dans build.gradle de ProjectB ressemblerait à ceci:

dependencies {

  compile project(':projecta')

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

}
Joman68
la source
1
Malheureusement (dans Gradle 6) l'inclusion plate, qui était exactement ce que je voulais, ne fonctionne plus car il n'y a plus de «tests» de configuration. En utilisant println(configurations.joinToString("\n") { it.name + " - " + it.allDependencies.joinToString() })(dans un buildscript kotlin), j'ai déterminé quelles configurations existent toujours et ont des dépendances, mais pour tous ces, Gradle s'est plaint: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

Certaines des autres réponses ont provoqué des erreurs d'une manière ou d'une autre - Gradle n'a pas détecté les classes de test d'autres projets ou le projet Eclipse avait des dépendances non valides lors de l'importation. Si quelqu'un a le même problème, je suggère d'aller avec:

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

La première ligne force l'Eclipse à lier l'autre projet en tant que dépendance, de sorte que toutes les sources sont incluses et à jour. Le second permet à Gradle de voir réellement les sources, sans causer d'erreurs de dépendance invalides comme le testCompile project(':core').sourceSets.test.outputfait.

Czyzby
la source
2

Ici, si vous utilisez Kotlin DSL , vous devez créer votre tâche comme ça selon Gradle documentation .

Comme certaines réponses précédentes, vous devez créer une configuration spéciale à l'intérieur du projet qui partagera sa classe de tests, afin de ne pas mélanger les classes de test et principales.

Étapes simples

  1. Dans le projet A, vous devrez ajouter votre 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. Ensuite dans votre projet B dans les dépendances, vous devrez ajouter votre build.gradle.kts:
dependencies {
    implementation(project(":ProjectA"))
    testImplementation(project(":ProjectA", "test"))
}
Sylhare
la source
-1

dans le projet B:

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

Semble fonctionner dans 1.7-rc-2

John Caron
la source
2
Cela crée également des complications inutiles dans la gestion du projet par Eclipse. La solution proposée par @NikitaSkvortsov est préférable.
sfitts le