Dependências de teste de vários projetos com gradle

153

Eu tenho uma configuração de vários projetos e quero usar o gradle.

Meus projetos são assim:

  • Projeto A

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

    • -> src/main/java(depende src/main/javado projeto A )
    • -> src/test/java(depende src/test/javado projeto A )

Meu arquivo do Projeto B build.gradle é assim:

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

A tarefa compileJavagrande trabalho, mas a compileTestJavanão compilar o arquivo de teste do Projeto A .

mathd
fonte
2
possível duplicado: stackoverflow.com/questions/5144325/gradle-test-dependency #
Mike Rylander 8/16

Respostas:

122

Descontinuado - Para o Gradle 5.6 e superior, use esta resposta .

No Projeto B , você só precisa adicionar uma testCompiledependência:

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

Testado com Gradle 1.7.

Fesler
fonte
7
Acontece que a propriedade de classes está obsoleta - use a saída.
Fesler
12
Isso não funciona no Gradle 1.3, pois o sourceSets não é mais uma propriedade pública de um projeto.
David Pärsson
3
Lembre-se de que a solução acima requer pelo menos um gradle testClassesantes que a estrutura de compilação seja realmente válida. Por exemplo, o plug-in do Eclipse não permitirá que você importe o projeto antes disso. É realmente uma pena testCompile project(':A')que não funciona. @ DavidPärsson: "Gradle 1.3" contradiz "não mais" desde que Fesler testou com Gradle 1.7.
Patrick Bergner
3
não funcionou para mim. Falha com dependência circular: compileTestJava \ ---: testClasses \ ---: compileTestJava (*)
rahulmohan
8
Não faça isso, os projetos não devem alcançar outros projetos. Em vez disso, use a resposta da Nikita, modelando-a corretamente como uma dependência do projeto.
Stefan Oehme 20/02
63

A maneira simples é adicionar dependência de tarefa explícita no ProjectB:

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

A maneira mais difícil (mas mais clara) é criar uma configuração de artefato adicional para o ProjectA:

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

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

e adicione a testCompiledependência para o ProjectB

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
  testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
Nikita Skvortsov
fonte
3
Eu tentei isso (da maneira simples) e, embora isso garanta que ele construa o testClasses, ele não adiciona o caminho de teste ao CLASSPATH; portanto, meus testes do ProjectB que dependem das classes de teste do ProjectA ainda não conseguem compilar.
pjz
1
@dmoebius você precisa adicionar uma testArtifactsconfiguração como esta: configurations { testArtifacts } para obter mais detalhes, consulte esta seção da ajuda do Gradle: gradle.org/docs/current/dsl/…
Nikita Skvortsov
7
No Gradle 1.8 você pode querer, from sourceSets.test.outpute possivelmente classifier = 'tests'no lugar da // pack whatever you need...resposta
Peter Lamberg
1
Confirmou que, com o Gradle 1.12, usando a solução completa, o @PeterLamberg sugere que as adições funcionem conforme o esperado. Não afeta a importação do projeto no Eclipse.
sfitts
3
Isso funciona para mim no Gradle 4.7. Agora eles têm alguns documentos sobre a abordagem em docs.gradle.org/current/dsl/…
Nathan Williams
19

Agora isso é suportado como um recurso de primeira classe no Gradle. Módulos com javaou java-libraryplug - ins também podem incluir um java-test-fixturesplug - in que expõe classes e recursos auxiliares a serem consumidos com o testFixturesauxiliar. Os benefícios dessa abordagem contra artefatos e classificadores são:

  • gerenciamento de dependência adequado (implementação / API)
  • boa separação do código de teste (conjunto de fontes separado)
  • não é necessário filtrar as classes de teste para expor apenas utilitários
  • mantido por Gradle

Exemplo

: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 / outro / build.gradle

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

módulo / outro / src / test / java / com / exemplo / outro / SomeTest.java

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

Leitura adicional

Para obter mais informações, consulte a documentação:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures

Foi adicionado na versão 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects

TWiStErRob
fonte
Eles estão trabalhando para dar suporte a isso no Android, consulte issuetracker.google.com/issues/139762443 e issuetracker.google.com/issues/139438142
Albert Vila Calvo em
18

Eu me deparei com esse problema recentemente, e o homem é uma questão difícil de encontrar respostas.

O erro que você está cometendo é pensar que um projeto deve exportar seus elementos de teste da mesma maneira que exporta seus artefatos e dependências principais.

O que eu tive muito mais sucesso pessoalmente foi fazer um novo projeto em Gradle. No seu exemplo, eu o nomearia

Projeto A_Test -> src / main / java

Eu colocaria no src / main / java os arquivos que você possui atualmente no Projeto A / src / test / java. Faça qualquer dependência testCompile do seu Projeto A, compile dependências do Projeto A_Test.

Em seguida, torne o Projeto A_Test uma dependência testCompile do Projeto B.

Não é lógico quando você aborda a questão da perspectiva do autor dos dois projetos, mas acho que faz muito sentido pensar em projetos como junit e scalatest (e outros.) Embora essas estruturas sejam relacionadas a testes, elas não são considerados parte dos destinos de "teste" em suas próprias estruturas - eles produzem artefatos principais que outros projetos acabam usando na sua configuração de teste.Você só deseja seguir o mesmo padrão.

Tentar fazer as outras respostas listadas aqui não funcionou para mim pessoalmente (usando o Gradle 1.9), mas descobri que o padrão que descrevo aqui é uma solução mais limpa.

Martin Snyder
fonte
Sim, optou por esta abordagem no final do dia.
Koma
Essa é a melhor abordagem! Exceto que eu manteria o código de teste no projeto A e moveria apenas dependências para A src / test / java e B src / test / java para A_Test. Então faça Projeto A_Test uma dependência testImplementation de ambos A e B.
Erik Sillén
17

Sei que é uma pergunta antiga, mas só tive o mesmo problema e passei algum tempo tentando descobrir o que estava acontecendo. Estou usando o Gradle 1.9. Todas as alterações devem estar no ProjectBbuild.gradle

Para usar as classes de teste do ProjectA nos testes do ProjectB:

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

Para garantir que a sourceSetspropriedade esteja disponível para o ProjectA:

evaluationDependsOn(':ProjectA')

Para garantir que as classes de teste do ProjectA estejam realmente presentes, quando você compila o ProjectB:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Dominik Pawlak
fonte
1
Isso também funcionou para mim, exceto que eu tive que omitir o .classesDir.
11

Nova solução baseada em testJar (dependências trnsitive suportadas) disponível como plugin gradle:

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

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

Da documentação

No caso de você ter uma compilação de vários projetos, você pode ter dependências de teste entre subprojetos (o que provavelmente é uma dica de que seus projetos não estão bem estruturados).

Por exemplo, assuma um projeto em que o subprojeto Projeto B depende do Projeto A e B não apenas tenha uma dependência de compilação em A, mas também uma dependência de teste. Para compilar e executar os testes de B, precisamos de algumas classes auxiliares de teste de A.

Por padrão, o gradle não cria um artefato jar a partir da saída de construção de teste de um projeto.

Este plug-in adiciona uma configuração testArchives (baseada em testCompile) e uma tarefa jarTest para criar um jar a partir do conjunto de fontes de teste (com o teste do classificador adicionado ao nome do jar). Podemos então confiar em B na configuração testArchives de A (que também incluirá as dependências transitivas de A).

Em A, adicionaríamos o plugin ao build.gradle:

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

Em B, fazemos referência à configuração testArchives da seguinte maneira:

dependencies {
    ...
    testCompile project (path: ':ProjectA', configuration: 'testArchives') 
}
demon101
fonte
1
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Do comentário
Ian
poucas linhas de texto foram adicionadas #
demon101
De qualquer forma, foram fornecidas informações sobre o novo plugin do gradle.
demon101
4
@ demon101 não funciona no Gradle 4.6, recebendo erroCould not get unknown property 'testClasses' for project ':core' of type org.gradle.api.Project.
Vignesh Sundar
11

Por favor, leia a atualização abaixo.

Problemas semelhantes descritos por JustACluelessNewbie ocorrem no IntelliJ IDEA. O problema é que dependência testCompile project(':core').sourceSets.test.outputrealmente significa: "depende de classes geradas pela tarefa de construção de gradle". Portanto, se você abrir um projeto limpo, onde as classes ainda não são geradas, o IDEA não as reconhecerá e reportará erros.

Para corrigir esse problema, você deve adicionar uma dependência nos arquivos de origem de teste ao lado da dependência nas classes compiladas.

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

Você pode observar dependências reconhecidas pela IDEA em Configurações do módulo -> Dependências (escopo do teste) .

Btw. Como não é uma solução agradável, vale a pena considerar a refatoração. O próprio Gradle possui um subprojeto especial contendo apenas classes de suporte de teste. Consulte https://docs.gradle.org/current/userguide/test_kit.html

Atualizar 2016-06-05 Mais Estou pensando em uma solução proposta menos que eu goste. Existem alguns problemas com isso:

  1. Ele cria duas dependências no IDEA. Um aponta para testar fontes outro para classes compiladas. E é crucial em que ordem essas dependências são reconhecidas pela IDEA. Você pode brincar alterando a ordem das dependências em Configurações do módulo -> guia Dependências.
  2. Ao declarar essas dependências, você está poluindo desnecessariamente a estrutura de dependências.

Então, qual é a melhor solução? Na minha opinião, está criando um novo conjunto de fontes personalizadas e colocando classes compartilhadas nele. Na verdade, os autores do projeto Gradle fizeram isso criando o conjunto de fontes testFixtures.

Para fazer isso, basta:

  1. Crie um conjunto de fontes e adicione as configurações necessárias. Verifique esse plugin de script usado no projeto Gradle: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
  2. Declare a dependência apropriada no projeto dependente:

    dependencies {
        testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
    }
    
  3. Importe o projeto Gradle para o IDEA e use a opção "criar módulo separado por conjunto de fontes" durante a importação.
Václav Kužel
fonte
1
@jannis fixed. Btw. A Gradle mudou seu plug-in de dispositivos de teste com base no Groovy para o novo Kotlin: github.com/gradle/gradle/blob/v5.0.0/buildSrc/subprojects/…
Václav Kužel:
@ VáclavKužel Eu descobri sua solução interessante por meio de sua postagem no blog e resolveu meu problema muito bem. Graças;)
zaerymoghaddam
10

A solução do Fesler não funcionou para mim, quando tentei criar um projeto android (gradle 2.2.0). Então eu tive que fazer referência manualmente às classes necessárias:

android {
    sourceSets {
        androidTest {
            java.srcDir project(':A').file("src/androidTest/java")
        }
        test {
            java.srcDir project(':A').file("src/test/java")
        }
    }
}
Beloo
fonte
1
erro de digitação leve, faltando a cotação final após o projeto (': A'). Isso funcionou para mim, porém, graças M8
Ryan Newsom
1
Para Android, essa ideia funcionou muito bem para mim, sem o sentimento de
hacker
@arberg Sim, parece ser uma boa abordagem. A única limitação que vejo é com as @VisibleForTestingregras do fiapo. Você não poderá chamar esses métodos a partir do módulo regular na pasta não teste.
Beloo 11/03
5

Estou muito atrasado para a festa (agora é o Gradle v4.4), mas para qualquer pessoa que encontrar isso:

Assumindo:

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

Vá para o build.gradle do projeto B (aquele que precisa de algumas classes de teste de A) e adicione o seguinte:

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

ou (supondo que seu projeto seja nomeado "ProjetoB")

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

Voila!

truque
fonte
3
A pergunta não menciona o Android. Você pode tornar sua resposta independente de o desenvolvedor estar desenvolvendo para o Android ou não, ou é apenas para desenvolvedores do Android?
Robin Green
4

Se você tiver dependências simulados que você precisa para compartilhar entre os testes, você pode criar novo projeto projectA-mocke, em seguida, adicioná-lo como dependência de teste para ProjectAe ProjectB:

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

Este é solução clara para dependências share simulados, mas se você precisa para executar testes de ProjectAem ProjectBuso outra solução.

Sylwano
fonte
Ótima solução para o caso simulado compartilhado!
Erik Sillén
4

Se você deseja usar dependências de artefato para ter:

  • As classes de origem do ProjectB dependem das classes de origem do Projeto A
  • As classes de teste do ProjectB dependem das classes de teste do Projeto A

a seção de dependências do ProjectB em build.gradle deve ter a seguinte aparência:

dependencies {

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

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

}

Para que isso funcione, o ProjectA precisa criar um teste frasco de e incluí-lo nos artefatos que produz.

O build.gradle do ProjectA deve conter uma configuração como esta:

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

configurations {
    tests
}

artifacts {
    tests testsJar
    archives testsJar
}

jar.finalizedBy(testsJar)

Quando os artefatos do ProjectA forem publicados no seu artefato, eles incluirão um frasco de testes .

O testCompile na seção de dependências do ProjectB trará as classes no jar -tests .


Se você deseja incluir as classes de origem e teste do ProjectA do FLAT no ProjectB para fins de desenvolvimento, a seção de dependências no build.gradle do ProjectB ficaria assim:

dependencies {

  compile project(':projecta')

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

}
Joman68
fonte
1
Infelizmente (no Gradle 6) o flat include, que era exatamente o que eu queria, não funciona mais porque não há mais 'testes' de configuração. Usando println(configurations.joinToString("\n") { it.name + " - " + it.allDependencies.joinToString() })(em um buildscript da kotlin), determinei quais configurações ainda existem e têm dependências, mas para todas essas reclamações da 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

Algumas das outras respostas causaram erros de uma maneira ou de outra - Gradle não detectou classes de teste de outros projetos ou o projeto Eclipse tinha dependências inválidas quando importadas. Se alguém tiver o mesmo problema, sugiro ir com:

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

A primeira linha força o Eclipse a vincular o outro projeto como dependência, para que todas as fontes sejam incluídas e atualizadas. O segundo permite que Gradle veja realmente as fontes, sem causar erros de dependência inválidos, como testCompile project(':core').sourceSets.test.outputocorre.

Czyzby
fonte
2

Aqui, se você estiver usando o Kotlin DSL , crie sua tarefa assim, de acordo com a documentação da Gradle .

Como em algumas respostas anteriores, você precisa criar uma configuração especial dentro do projeto que compartilhará sua classe de testes, para não misturar as classes principal e de teste.

Passos simples

  1. No projeto A, você precisará adicionar seu 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. Então, no seu projeto B, nas dependências, você precisará adicionar build.gradle.kts:
dependencies {
    implementation(project(":ProjectA"))
    testImplementation(project(":ProjectA", "test"))
}
Sylhare
fonte
-1

no projeto B:

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

Parece funcionar em 1.7-rc-2

John Caron
fonte
2
Ele também cria complicações desnecessárias no manuseio do projeto pelo Eclipse. A solução sugerida por @NikitaSkvortsov é preferível.
sfitts