Как добавить новый источник в Gradle?
Я хочу добавить интеграционные тесты в мою сборку Gradle (версия 1.0). Они должны запускаться отдельно от моих обычных тестов, потому что они требуют развертывания веб-приложения на localhost (они тестируют это веб-приложение). Тесты должны быть в состоянии использовать классы, определенные в моем основном исходном наборе. Как мне это сделать?
11 ответов
Мне потребовалось некоторое время, чтобы понять, и онлайн-ресурсы не были хорошими. Поэтому я хотел документировать свое решение.
Это простой скрипт сборки Gradle, у которого есть набор исходного кода intTest в дополнение к основному и тестовому исходным наборам:
apply plugin: "java"
sourceSets {
// Note that just declaring this sourceset creates two configurations.
intTest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
}
configurations {
intTestCompile.extendsFrom testCompile
intTestRuntime.extendsFrom testRuntime
}
task intTest(type:Test){
description = "Run integration tests (located in src/intTest/...)."
testClassesDir = project.sourceSets.intTest.output.classesDir
classpath = project.sourceSets.intTest.runtimeClasspath
}
Вот как я добился этого без использования configurations{ }
,
apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_6
sourceSets {
integrationTest {
java {
srcDir 'src/integrationtest/java'
}
resources {
srcDir 'src/integrationtest/resources'
}
compileClasspath += sourceSets.main.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs Integration Tests"
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath += sourceSets.integrationTest.runtimeClasspath
}
Протестировано с использованием: Gradle 1.4 и Gradle 1.6
Чтобы суммировать оба старых ответа (получить лучший и минимальный жизнеспособность обоих миров):
Сначала несколько теплых слов:
Во-первых, нам нужно определить
sourceSet
:sourceSets { integrationTest }
Далее мы расширяем
sourceSet
отtest
для этого мы используемtest.runtimeClasspath
(который включает в себя все зависимости отtest
А ТАКЖЕtest
сам) как путь к классу для производногоsourceSet
:sourceSets { integrationTest { compileClasspath += sourceSets.test.runtimeClasspath runtimeClasspath += sourceSets.test.runtimeClasspath // ***) } }
- примечание) каким-то образом это объявление / продлить для
sourceSets.integrationTest.runtimeClasspath
необходимо, но не имеет значения, так какruntimeClasspath
всегда расширяетсяoutput + runtimeSourceSet
не понимаю
- примечание) каким-то образом это объявление / продлить для
мы определяем отдельную задачу для запуска интеграционных тестов:
task integrationTest(type: Test) { }
Настройте
integrationTest
тестовые классы и использование classpath. Значения по умолчанию отjava
плагин использоватьtest
sourceSet
task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath }
(необязательно) автоматический запуск после теста
интеграция.проверяет по тесту
(необязательно) добавить зависимость от
check
(так всегда работает, когдаbuild
или жеcheck
выполнены)tasks.check.dependsOn(tasks.integrationTest)
(необязательно) добавить Java, ресурсы к
sourceSet
поддерживать автоматическое обнаружение и создавать эти "частичные" в вашей IDE. т.е. IntelliJ IDEA автоматически создастsourceSet
каталоги java и ресурсы для каждого набора, если он не существует:sourceSets { integrationTest { java resources } }
ТЛ; др
apply plugin: 'java'
// apply the runtimeClasspath from "test" sourceSet to the new one
// to include any needed assets: test, main, test-dependencies and main-dependencies
sourceSets {
integrationTest {
// not necessary but nice for IDEa's
java
resources
compileClasspath += sourceSets.test.runtimeClasspath
// somehow this redeclaration is needed, but should be irrelevant
// since runtimeClasspath always expands compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
// define custom test task for running integration tests
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
tasks.integrationTest.dependsOn(tasks.test)
ссылаясь на:
- Gradle Java Глава 45.7.1. Свойства исходного набора
- Gradle Java Глава 45.7.3. Некоторые исходные примеры
К сожалению, пример кода на https://github.com/gradle/gradle/blob/master/subprojects/docs/src/samples/java/customizedLayout/build.gradle или …/gradle/…/withIntegrationTests/build.gradle, похоже, не справляется с этим или имеет другой / более сложный / для меня нет более ясного решения в любом случае!
Плагин Nebula-Facet устраняет шаблон:
apply plugin: 'nebula.facet'
facets {
integrationTest {
parentSourceSet = 'test'
}
}
Специально для интеграционных тестов, даже это сделано для вас, просто примените:
apply plugin: 'nebula.integtest'
Ссылки на портал Gradle для каждого из них:
Если вы используете
- Gradle 5.x, посмотрите раздел документации "Тестирование Java > Настройка интеграционных тестов, пример 14 и 15" для получения подробной информации (как для Groovy, так и для Kotlin DSL, какой вы предпочитаете)
- alt: "текущая" ссылка на Gradle doc на 2, но может отложить в будущем, вам следует посмотреть, если примеры изменятся)
- для Gradle 4 взгляните на древнюю версию 3, которая близка к тому, что @Spina опубликовал в 2012 году
Чтобы IntelliJ распознал пользовательский набор источников в качестве корневых источников тестов:
plugin {
idea
}
idea {
module {
testSourceDirs = testSourceDirs + sourceSets["intTest"].allJava.srcDirs
testResourceDirs = testResourceDirs + sourceSets["intTest"].resources.srcDirs
}
}
Вот что работает для меня с Gradle 4.0.
sourceSets {
integrationTest {
compileClasspath += sourceSets.test.compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs the integration tests."
group = 'verification'
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
Начиная с версии 4.0, Gradle теперь использует отдельные каталоги классов для каждого языка в исходном наборе. Так что если ваш скрипт сборки использует sourceSets.integrationTest.output.classesDir
вы увидите следующее предупреждение об устаревании.
Gradle теперь использует отдельные выходные каталоги для каждого языка JVM, но эта сборка предполагает наличие одного каталога для всех классов из исходного набора. Это поведение устарело и планируется удалить в Gradle 5.0
Чтобы избавиться от этого предупреждения, просто переключитесь на sourceSets.integrationTest.output.classesDirs
вместо. Для получения дополнительной информации см. Примечания к выпуску Gradle 4.0.
Я понимаю, что в 2012 году, когда был задан этот вопрос, документация была не очень хорошей, но для тех, кто читает это в 2020+: теперь в документации есть целый раздел о том, как добавить исходный набор для интеграционных тестов. Вы действительно должны прочитать это вместо того, чтобы копировать/вставлять фрагменты кода здесь и биться головой о стену, пытаясь понять, почему ответ от 2012-2016 не совсем работает.
Ответ, скорее всего, прост, но содержит больше нюансов, чем вы думаете, и точный код, который вам понадобится, скорее всего, будет отличаться от кода, который мне понадобится. Например, хотите ли вы, чтобы ваши интеграционные тесты использовали те же зависимости, что и ваши модульные тесты?
Вот решение для Kotlin (build.gradle.kts):
sourceSets {
create("uiTest") {
// Adds files from the main source set to the compilation and runtime classpaths of this new source set
// sourceSets.main.output is a collection of all the directories containing compiled main classes and resources
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
}
// Makes the uiTestImplementation configuration extend from testImplementaion,
// which means that all the declared dependencies of the test code (and transitively the main as well)
// also become dependencies of this new source set
val uiTestImplementation by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
val uiTestRuntimeOnly by configurations.getting {
extendsFrom(configurations.testRuntimeOnly.get())
}
Бонус:
val uiTest = task<Test>("uiTest") {
description = "Runs UI tests."
group = "verification"
testClassesDirs = sourceSets["uiTest"].output.classesDirs
classpath = sourceSets["uiTest"].runtimeClasspath
testLogging {
events(TestLogEvent.PASSED)
}
}
tasks.check { dependsOn(uiTest) }
Как я могу связать файл типа Biding.xml с исходными наборами, например
sourceSets {
zxc50{
xjcSchema {
srcDir 'src/main/schema/target'
includes = ['zxc50.xsd']
xjcTargetPackage = "zxc50"
}
}
Вы также можете настроить свои тесты для работы с jUnit:
task integrationTest(type: Test) {
...
useJunitPlatform()
}
Я новичок в Gradle, использую Gradle 6.0.1 JUnit 4.12. Вот что я придумал для решения этой проблемы.
apply plugin: 'java'
repositories { jcenter() }
dependencies {
testImplementation 'junit:junit:4.12'
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['tests']
}
}
}
Обратите внимание, что основной источник и тестовый источник упоминаются отдельно, один под main
и один под test
.
В testImplementation
пункт под dependencies
используется только для компиляции исходного кода в test
. Если ваш основной код действительно зависит от JUnit, вы также должны указатьimplementation
под dependencies
.
Я должен был указать repositories
раздел, чтобы заставить это работать, я сомневаюсь, что это лучший / единственный способ.