Dependencias de prueba multiproyecto con gradle

153

Tengo una configuración multiproyecto y quiero usar gradle.

Mis proyectos son así:

  • Proyecto a

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

    • -> src/main/java(depende src/main/javadel Proyecto A )
    • -> src/test/java(depende src/test/javadel Proyecto A )

Mi archivo de Proyecto B build.gradle es así:

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

La tarea compileJavagran trabajo pero el compileTestJavano se compila el archivo de prueba del Proyecto A .

mathd
fuente

Respuestas:

122

En desuso: para Gradle 5.6 y superior, use esta respuesta .

En el Proyecto B , solo necesita agregar una testCompiledependencia:

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

Probado con Gradle 1.7.

Fesler
fuente
77
Resulta que la propiedad de clases está en desuso; use la salida en su lugar.
Fesler
12
Esto no funciona en Gradle 1.3 ya que sourceSets ya no es una propiedad pública de un proyecto.
David Pärsson
3
Tenga en cuenta que la solución anterior requiere al menos un tiempo gradle testClassesantes de que la estructura de compilación sea realmente válida. Por ejemplo, el complemento Eclipse no le permitirá importar el proyecto antes de eso. Realmente es una pena testCompile project(':A')que no funcione. @ DavidPärsson: "Gradle 1.3" contradice "ya no" desde que Fesler probó con Gradle 1.7.
Patrick Bergner
3
no funciono para mi Error con la dependencia circular: compileTestJava \ ---: testClasses \ ---: compileTestJava (*)
rahulmohan
8
No hagas esto, no se supone que los proyectos lleguen a otros proyectos. En su lugar, use la respuesta de Nikita, modelando correctamente esto como una dependencia del proyecto.
Stefan Oehme
63

La forma simple es agregar una dependencia de tarea explícita en ProjectB:

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

La forma difícil (pero más clara) es crear una configuración de artefactos adicional para el Proyecto A:

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

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

y agregue la testCompiledependencia para ProjectB

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
  testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
Nikita Skvortsov
fuente
3
Intenté esto (de manera simple) y aunque eso asegura que construya las clases de prueba, no agrega la ruta de prueba a CLASSPATH, por lo que mis pruebas de ProjectB que dependen de las clases de prueba de ProjectA aún no se pueden construir.
pjz 01 de
1
@dmoebius debe agregar una testArtifactsconfiguración como esta: configurations { testArtifacts } para obtener más detalles, consulte esta sección de la ayuda de Gradle: gradle.org/docs/current/dsl/…
Nikita Skvortsov
77
En Gradle 1.8 puede querer from sourceSets.test.outputy posiblemente classifier = 'tests'en lugar de // pack whatever you need...en la respuesta
Peter Lamberg
1
Confirmó que con Gradle 1.12 usando la solución completa, con @PeterLamberg las adiciones sugeridas funcionan como se esperaba. No afecta la importación del proyecto en Eclipse.
sfitts
3
Esto funciona para mí en Gradle 4.7. Ahora tienen algunos documentos sobre el enfoque en docs.gradle.org/current/dsl/…
Nathan Williams
19

Esto ahora es compatible como una característica de primera clase en Gradle. Los módulos con javao java-librarycomplementos también pueden incluir un java-test-fixturescomplemento que expone clases de ayuda y recursos para ser consumidos con testFixturesayuda. Los beneficios de este enfoque contra artefactos y clasificadores son:

  • gestión de dependencias adecuada (implementación / api)
  • Buena separación del código de prueba (conjunto fuente separado)
  • no es necesario filtrar las clases de prueba para exponer solo las utilidades
  • mantenido por Gradle

Ejemplo

: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
  }
}

Otras lecturas

Para obtener más información, consulte la documentación:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures

Fue agregado en 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects

TWiStErRob
fuente
Están trabajando para apoyar esto en Android, consulte issuetracker.google.com/issues/139762443 y issuetracker.google.com/issues/139438142
Albert Vila Calvo
18

Me he encontrado con este problema recientemente, y este es un tema difícil para el que encontrar respuestas.

El error que está cometiendo es pensar que un proyecto debería exportar sus elementos de prueba de la misma manera que exporta sus artefactos y dependencias principales.

Lo que tuve mucho más éxito personalmente fue hacer un nuevo proyecto en Gradle. En tu ejemplo, lo nombraría

Proyecto A_Test -> src / main / java

Pondría en src / main / java los archivos que tiene actualmente en Project A / src / test / java. Realice cualquier dependencia de TestCompile de su proyecto A compile las dependencias de Project A_Test.

Luego, haga que el Proyecto A_Test sea una dependencia de TestCompile del Proyecto B.

No es lógico cuando lo consideras desde la perspectiva del autor de ambos proyectos, pero creo que tiene mucho sentido cuando piensas en proyectos como junit y scalatest (y otros. Aunque esos marcos están relacionados con las pruebas, ellos no se consideran parte de los objetivos de "prueba" dentro de sus propios marcos: producen artefactos primarios que otros proyectos simplemente usan dentro de su configuración de prueba. Simplemente desea seguir ese mismo patrón.

Intentar hacer las otras respuestas enumeradas aquí no me funcionó personalmente (usando Gradle 1.9), pero descubrí que el patrón que describo aquí es una solución más limpia de todos modos.

Martin Snyder
fuente
Sí, opté por este enfoque al final del día.
koma
Este es el mejor enfoque! Excepto que mantendría el código de prueba en el proyecto A y movería solo las dependencias para A src / test / java y B src / test / java a A_Test. Luego, haga que el Proyecto A_Test sea una prueba de dependencia de implementación de A y B.
Erik Sillén
17

Sé que es una vieja pregunta, pero tuve el mismo problema y pasé un tiempo averiguando qué está pasando. Estoy usando Gradle 1.9. Todos los cambios deben estar en ProjectB'sbuild.gradle

Para usar las clases de prueba de ProjectA en pruebas de ProjectB:

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

Para asegurarse de que la sourceSetspropiedad esté disponible para ProjectA:

evaluationDependsOn(':ProjectA')

Para asegurarse de que las clases de prueba de ProjectA estén realmente allí, cuando compila ProjectB:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Dominik Pawlak
fuente
1
Esto también funcionó para mí, excepto que tuve que omitir el .classesDir.
11

Nueva solución basada en testJar (dependencias trnsitivas compatibles) disponible como complemento de gradle:

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

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

De la documentación

En caso de que tenga una compilación gradle multiproyecto, es posible que tenga dependencias de prueba entre subproyectos (lo que probablemente es una pista de que sus proyectos no están bien estructurados).

Por ejemplo, suponga un proyecto donde el subproyecto Proyecto B depende del Proyecto A y B no solo tiene una dependencia de compilación de A sino también una dependencia de prueba. Para compilar y ejecutar las pruebas de B necesitamos algunas clases auxiliares de prueba de A.

De forma predeterminada, gradle no crea un artefacto jar a partir de la salida de compilación de prueba de un proyecto.

Este complemento agrega una configuración testArchives (basada en testCompile) y una tarea jarTest para crear un jar desde el conjunto de origen de la prueba (con la prueba del clasificador agregada al nombre del jar). Entonces podemos depender en B de la configuración testArchives de A (que también incluirá las dependencias transitivas de A).

En A agregaríamos el complemento a build.gradle:

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

En B hacemos referencia a la configuración de testArchives de esta manera:

dependencies {
    ...
    testCompile project (path: ':ProjectA', configuration: 'testArchives') 
}
demon101
fuente
1
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia. - De la opinión
Ian
se han agregado algunas líneas de texto
demon101
De todos modos, se ha proporcionado información sobre el nuevo complemento de gradle.
demon101
44
@ demon101 No funciona en Gradle 4.6, obteniendo errorCould not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
Vignesh Sundar
11

Por favor, lea la actualización a continuación.

Problemas similares descritos por JustACluelessNewbie ocurren en IntelliJ IDEA. El problema es que la dependencia en testCompile project(':core').sourceSets.test.outputrealidad significa: "depende de las clases generadas por la tarea de construcción de gradle". Entonces, si abre un proyecto limpio donde las clases aún no se generan, IDEA no las reconocerá e informará de errores.

Para solucionar este problema, debe agregar una dependencia en los archivos de origen de prueba junto a la dependencia en las clases compiladas.

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

Puede observar las dependencias reconocidas por IDEA en Configuración del módulo -> Dependencias (alcance de la prueba) .

Por cierto. Esta no es una buena solución, por lo que vale la pena considerar la refactorización. Gradle tiene un subproyecto especial que contiene solo clases de soporte de prueba. Ver https://docs.gradle.org/current/userguide/test_kit.html

Actualización 2016-06-05 Más Estoy pensando en la solución propuesta menos Me gusta. Hay pocos problemas con esto:

  1. Crea dos dependencias en IDEA. Uno apunta a probar fuentes y otro a clases compiladas. Y es crucial en qué orden IDEA reconoce estas dependencias. Puedes jugar con él cambiando el orden de dependencia en Configuración del módulo -> pestaña Dependencias.
  2. Al declarar estas dependencias, está contaminando innecesariamente la estructura de dependencia.

Entonces, ¿cuál es la mejor solución? En mi opinión, está creando un nuevo conjunto de fuentes personalizado y colocando clases compartidas en él. En realidad, los autores del proyecto Gradle lo hicieron creando el conjunto de fuentes testFixtures.

Para hacerlo solo tienes que:

  1. Cree el conjunto de origen y agregue las configuraciones necesarias. Verifique este complemento de script utilizado en el proyecto Gradle: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
  2. Declarar dependencia adecuada en proyecto dependiente:

    dependencies {
        testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
    }
    
  3. Importe el proyecto Gradle a IDEA y use la opción "crear módulo separado por conjunto de origen" al importar.
Václav Kužel
fuente
1
@jannis arreglado. Por cierto. Gradle trasladó su complemento de accesorios de prueba basado en Groovy a uno nuevo basado en Kotlin: github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/…
Václav Kužel
@ VáclavKužel Descubrí su solución interesante a través de su publicación de blog y resolvió mi problema muy bien. Gracias;)
zaerymoghaddam
10

La solución de Fesler no me funcionó cuando lo intenté para construir un proyecto de Android (gradle 2.2.0). Así que tuve que hacer referencia a las clases requeridas manualmente:

android {
    sourceSets {
        androidTest {
            java.srcDir project(':A').file("src/androidTest/java")
        }
        test {
            java.srcDir project(':A').file("src/test/java")
        }
    }
}
Beloo
fuente
1
error tipográfico leve, falta la cita final después del proyecto (': A'). Sin embargo, esto funcionó para mí, gracias m8
Ryan Newsom
1
Para Android, esta idea funcionó muy bien para mí, sin el sentimiento de
hackear
@arberg Sí, parece un buen enfoque. La única limitación que veo es con las @VisibleForTestingreglas de pelusa. No podrá llamar a tales métodos desde el módulo normal en la carpeta no prueba.
Beloo
5

Llego tan tarde a la fiesta (ahora es Gradle v4.4) pero para cualquiera que encuentre esto:

Asumiendo:

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

Vaya a build.gradle del proyecto B (el que necesita algunas clases de prueba de A) y agregue lo siguiente:

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

o (suponiendo que su proyecto se llame "Proyecto B")

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

Voila!

truco
fuente
3
La pregunta no menciona Android. ¿Puede hacer que su respuesta sea independiente de si el desarrollador está desarrollando para Android o no, o es solo para desarrolladores de Android?
Robin Green
4

Si tiene dependencias simuladas que necesita compartir entre pruebas, puede crear un nuevo proyecto projectA-mocky luego agregarlo como dependencia de prueba ProjectAy ProjectB:

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

Esta es una solución clara para compartir dependencias simuladas, pero si necesita ejecutar pruebas ProjectAen ProjectBuso, use otra solución.

sylwano
fuente
¡Gran solución para el caso simulado compartido!
Erik Sillén
4

Si desea utilizar dependencias de artefactos para tener:

  • Las clases de origen de ProjectB dependen de las clases de origen de Project A
  • Las clases de prueba de ProjectB dependen de las clases de prueba de Project A

entonces la sección de dependencias de ProjectB en build.gradle debería verse así:

dependencies {

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

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

}

Para que esto funcione, ProjectA necesita construir un tarro de pruebas e incluirlo en los artefactos que produce.

El build.gradle de ProjectA debe contener una configuración como esta:

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

configurations {
    tests
}

artifacts {
    tests testsJar
    archives testsJar
}

jar.finalizedBy(testsJar)

Cuando los artefactos de ProjectA se publiquen en su artefacto, incluirán un jar de prueba .

El testCompile en la sección de dependencias ProjectB traerá en las clases en el -pruebas frasco.


Si desea incluir las clases de fuente y prueba de Flat ProjectA en ProjectB para fines de desarrollo, la sección de dependencias en build.gradle de ProjectB se vería así:

dependencies {

  compile project(':projecta')

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

}
Joman68
fuente
1
Desafortunadamente (en Gradle 6) la inclusión plana, que era exactamente lo que quería, ya no funciona porque ya no hay 'pruebas' de configuración. Usando println(configurations.joinToString("\n") { it.name + " - " + it.allDependencies.joinToString() })(en un Kotlin Buildscript), determiné qué configuraciones aún existen y tienen dependencias, pero para todos estos Gradle se quejó: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

Algunas de las otras respuestas causaron errores de una forma u otra: Gradle no detectó clases de prueba de otros proyectos o el proyecto Eclipse tenía dependencias no válidas cuando se importó. Si alguien tiene el mismo problema, sugiero ir con:

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

La primera línea obliga al Eclipse a vincular el otro proyecto como dependencia, por lo que todas las fuentes están incluidas y actualizadas. El segundo permite que Gradle vea realmente las fuentes, sin causar ningún error de dependencia no válido como lo testCompile project(':core').sourceSets.test.outputhace.

Czyzby
fuente
2

Aquí, si está utilizando Kotlin DSL , debe crear su tarea de esa manera según la documentación de Gradle .

Como en algunas respuestas anteriores, debe crear una configuración especial dentro del proyecto que comparta su clase de pruebas, para que no mezcle las clases principales y de prueba.

Pasos simples

  1. En el proyecto A necesitaría agregar su 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. Luego, en su proyecto B en las dependencias, deberá agregar su build.gradle.kts:
dependencies {
    implementation(project(":ProjectA"))
    testImplementation(project(":ProjectA", "test"))
}
Sylhare
fuente
-1

en el proyecto B:

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

Parece funcionar en 1.7-rc-2

John Caron
fuente
2
También crea complicaciones innecesarias en el manejo del proyecto por parte de Eclipse. La solución sugerida por @NikitaSkvortsov es preferible.
sfitts