Глава 47. Плагин Java.

Этот плагин добавляет в проект компиляцию исходных кодов Java вместе с тестированием и возможностями упаковки. Он служит основой для многих других плагинов Gradle.

47.1. Использование.

Чтобы использовать плагин Java, включите следующее в ваш сборочный файл:

Пример 47.1. Использование плагина Java

build.gradle

apply plugin: 'java'
	  

47.2. Набор исходников.

Плагин Java вводит идею набора исходников. Набор исходников - это просто группа исходных файлов, которые компилируются и исполняются вместе. Эти исходные файлы могут включать исходные файлы Java и файлы ресурсов. Другие плагины добавляют возможность включать в набор исходников исходные файлы Groovy и Scala. У набора исходников есть связанный с ним путь к классам компиляции и путь к классам выполнения.

Наборы исходников могут использоваться для объединения исходных файлов в логические группы, которые описывают их цель. Например, вы можете использовать набор исходников для задания набора интеграционных тестов или можете использовать отдельные наборы исходников для задания API и классов реализации вашего проекта.

Плагин Java определяет два стандратных набора исходников, названных main и test. Набор исходников main содержит исходные коды, которые компилируются и упаковываются в jar-файл. Набор исходников test содержит исходные коды тестов, которые компилируются и выполняются с использованием JUnit или TestNG. Это могут быть юнит-тесты, интеграционные-тесты, приёмочные тесты или любая комбинация, которая будет полезна для вас.

47.3. Задачи.

Как вы можете видеть ниже, плагин Java добавляет множество задач к вашему проекту.

Таблица 47.1. Плагин Java - задачи
Имя задачиЗависит отТипОписание
compileJavaВсех задач, которые формируют путь к классам компиляции. Сюда включаются задача jar для зависимостей проекта, вкюченных в конфигурацию compile.JavaCompileКомпилирует исходные файлы Java с использованием javac.
processResources-CopyКопирует ресурсы в папку ресурсов.
classesЗадач compileJava и processResources. Некоторые плагины добавляют дополнительные задачи компиляции.TaskОбъдиняет классы и папки ресурсов.
compileTestJavacompile, плюс все задачи, которые формируют путь к классам компиляции тестов.JavaCompileКомпилирует исходные файлы тестов Java с использованием javac.
processTestResources-CopyКопирует ресурсы тестов в папку ресурсов тестов.
testClassesЗадач compileTestJava и processTestResources. Некоторые плагины добавляют дополнительные задачи компиляции тестов.TaskОбъдиняет класс тестов и папки ресурсов тестов.
jarcompileJarСобирает jar-файл.
javadoccompileJavadocГенерирует документацию API для исходных кодов Java с использованием Javadoc.
testcompile, compileTest, плюс все задачи, которые формируют путь к класс выполнения тестов.TestЗапускает на выполнение юнит-тесты с использованием JUnit или TestNG.
uploadArchivesЗадач, которые выдают артефакты в конфигурации archives, включая задачу jar.UploadВыгружает артефакты из конфигурации archives, включая jar-файл.
clean-DeleteУдаляет папку сборки проекта
cleanTaskName-DeleteУдаляет файлы, созданные определенной задачей. cleanJar удалит jar-файл, созданный задачей jar, а cleanTest удалит результаты теста, созданные задачей test.

Для каждого набора исходников, который вы добавили в проект, плагин Java добавит следующие задачи компиляции:

Таблица 47.2. Плагин Java - задачи набора исходников
Имя задачиЗависит отТипОписание
compileНаборИсходниковJavaВсех задач, которые формируют путь к классам компиляции набора исходников.JavaCompileКомпилирует данный исходных файлов набора исходников Java с использованием javac.
processНаборИсходниковResources-CopyКомпирует ресурсы данного набора исходников в папку ресурсов.
наборИсходниковClassesЗадач compileНаборИсходниковJava и processНаборИсходниковResources. Некоторые плагины добавляют дополнительные задачи компиляции набора исходников.TaskОбъдиняет классы данного набора исходников и папки ресурсов.

Плагин Java также добавляет много задач, которые формируют жизненный цикл проекта:

Таблица 47.3. Плагин Java - задачи жизненного цикла
Имя задачиЗависит отТипОписание
assembleВсех архивных задач проекта, включая jar. Некоторые плагины добавляют дополнительные архивные задачи в проект.TaskСобирает все архивы проекта.
checkВсех задач проверки в проекте, включая test. Некоторые плагины добавляют дополнительные задачи проверки в проект.TaskВыполняет все задачи проверки в проекте.
buildcheck и assembleTaskВыполняет полную сборку проекта.
buildNeededЗадач build и buildNeeded во всех библиотечных зависимостях проекта конфигурации testRuntime.TaskВыполняет полную сборку проекта и всех проектов, от которых он зависит.
buildDependentsЗадач build и buildDependents во всех проектах, у которых есть библиотечная зависимость от этого проекта в конфигурации testRuntime.TaskВыполняет полную сборку проекта и всех проектов, которые зависят от него.
buildИмяКонфигурацииЗадач, которые создают артефакты в конфигурации ИмяКонфигурации.TaskСобирает все артефакты указанной конфигурации. Задач добавляется плагином Base, который неявно применяется плагином Java.
uploadИмяКонфигурацииЗадач, которые выгружают артефакты конфигурации ИмяКонфигурации.UploadСобирает и выгружает артефакты указанной конфигурации. Задача добавляет плагинов Base, который неявно применятся плагином Java.

Следующая диаграмма показыват взаимосвязь между задачами.

Рисунок 47.1. Плагин Java - задачи
Плагин Java - задачи

47.4. Разметка проекта.

Плагин Java предполагает, что у проекта разметка такая, как показано ниже. Ни одна из этих папок не обязана существовать или что-либо содержать. Плагин Java скомпилирует все, что найдет и обработает все пропущенное.

Таблица 47.4. Плагин Java - разметка проекта по умолчанию
ПапкаЗначение
src/main/javaИсходники Java
src/main/resourcesРесурсы
src/test/javaИсходники тестов
src/test/resourcesРесурсы тестов
src/наборИсходников/javaИсходники Java данного набора исходников
src/наборИсходников/resourcesРесурсы данного набора исходников

47.4.1. Изменение разметки проекта.

Вы настраиваете разметку проекта, изменением соответствующего набора исходников. Более детально это обсуждается в последующих секциях. Вот небольшой пример, в котором изменяются папки исходников Java и ресурсов.

Пример 47.2. Пользовательская разметка исходников Java

build.gradle

sourceSets {
    main {
        java {
            srcDirs = ['src/java']
        }
        resources {
            srcDirs = ['src/resources']
        }
    }
}
	  

47.5. Управление зависимостями.

Плагин Java добавляет приличное количество конфигураций зависимостей в ваш проект, как вы можете увидеть ниже. Он назначает эти конфигурации таким задачам, как compileJava и test.

Таблица 47.5. Плагин Java - конфигурации зависимостей
ИмяРасширяетИспользуется задачамиЗначение
compile--Зависимости времени компиляции.
compileOnly--Зависимости только времени компиляции, не используются во время выполнения.
compileClasspathcompile, compileOnlycompileJavaПуть к классам компиляции, используется при компиляции исходных кодов.
runtimecompile-Зависимости времени выполнения.
testCompilecompile-Дополнительные зависимости для компиляции тестов.
testCompileOnly--Дополнительные зависимости только для компиляции тестов, не используются во время выполнения.
testCompileClasspathtestCompile, testCompileOnlycompileTestJavaПуть к классам компиляции тестов, используется при компиляции исходных кодов тестов.
testRuntimeruntime, testCompiletestДополнительные зависимости только для запуска тестов.
archives-uploadArchivesАртефакты (например, jar-файлы) создаваемые этим проектов.
defaultruntime-Конфигурация по умолчанию, используемая зависимостью проекта на этот проект. Содержит артефакты и зависимости необходимые этому проекту во время выполнения.
Рисунок 47.2. Плагин Java - конфигурации зависимостей
Плагин Java - конфигурации зависимостей

Для каждого набора исходников, который вы добавляете в проект, плагин Java добавляет следующие конфигурации зависимостей:

Таблица 47.6. Плагин Java - конфигурации зависимостей наборов исходников
ИмяРасширяетИспользуется задачамиЗначение
наборИсходникоCompile--Зависимости времени компиляции данного набора исходников.
наборИсходниковCompileOnly--Зависимости только времени компиляции данного набора исходников, не используются во время выполнения.
наборИсходниковCompileClasspathнаборИсходниковCompile, наборИсходниковCompileOnlycompileНаборИсходниковJavaПуть к классам компиляции, используется при компиляции исходников.
наборИсходниковRuntimeнаборИсходниковCompile-Зависимости времени выполнения данного набора исходников.

47.6. Условные свойства.

Плагин Java добавляет много условных свойств к проекту и они показаны ниже. Вы можете использовать эти свойства в вашем сборочном скрипте, как если бы они были свойствами объекта проекта.

Таблица 47.7. Плагин Java - свойства папок
Имя свойстваТипЗначение по умолчаниюОписание
reportsDirNameStringreportsИмя папки, куда генерируются отчеты, относительно папки сборки.
reportsDirFile (только для чтения)buildDir/reportsDirNameПапка, в которую генерируются отчеты.
testResultsDirNameStringtest-resultsИмя папки, куда генерируются файлы тестов result.xml, относительно папки сборки.
testResultsDirFile (только для чтения)buildDir/testResultsDirNameПапка, куда генерируются файлы тестов result.xml.
testReportDirNameStringtestsИмя папки, куда генерируется отчет о тестах, относительно папки сборки.
testReportDirFile (только для чтения)reportsDir/testReportDirNameПапка, куда генерируется отчет о тестах.
libsDirNameStringlibsИмя папки, куда генерируются библиотеки, относительно папки сборки.
libsDirFile (только для чтения)buildDir/libsDirNameПапка, куда генерируются библиотеки.
distsDirNameStringdistributionsИмя папки, куда генерируются дистрибутивы, относительно папки сборки.
distsDirFile (только для чтения)buildDir/distsDirNameПапка, куда генерируются дистрибутивы.
docsDirNameStringdocsИмя папки, куда генерируется документация, относительно папки сборки.
docsDirFile (только для чтения)buildDir/docsDirNameПапка, куда генерируется документация.
dependencyCacheDirNameStringdependency-cacheИмя папки, которая используется для кэширования информации о зависимостях исходников, относительно папки сборки.
dependencyCacheDirFile (только для чтения)buildDir/dependencyCacheDirNameПапка, которая используется для кэширования информации о зависимостях исходников.
Таблица 47.8. Плагин Java - другие свойства
Имя свойстваТипЗначение по умолчаниюОписание
sourceSetsSourceSetContainer (только для чтения)Не nullСодержит наборы исходников проекта.
sourceCompatibilityJavaVersion. Можно установить String или Number, например, '1.5' или 1.5.версия используемой на данный момент JVMСовместимость с версией Java, используемая при компиляции исходников Java.
targetCompatibilityJavaVersion. Можно установить String или Number, например, '1.5' или 1.5.sourceCompatibilityВерсия Java для которой генерируются классы.
archivesBaseNameStringprojectNameБазовое имя, используемое для архивов, таких как jar или zip.
manifestManifestпустой манифестМанифест, который включается во все jar-файлы.

Эти свойства предоставляются условными объектами типа JavaPluginConvention и BasePluginConvention.

47.7. Работа с наборами исходников.

Вы можете обратиться к наборам исходников проекта с помощью свойства sourceSets. Это контейнер наборов исходников проекта, типа SourceSetContainer. Также есть скриптовый блок sourceSets { }, в который вы можете передать замыкание для настройки контейнера наборов исходников. Этот контейнер работает почти так же, как и другие контейнеры, например, tasks.

Пример 47.3. Обращение к набору исходников

build.gradle

// Various ways to access the main source set
println sourceSets.main.output.classesDir
println sourceSets['main'].output.classesDir
sourceSets {
    println main.output.classesDir
}
sourceSets {
    main {
        println output.classesDir
    }
}

// Iterate over the source sets
sourceSets.all {
    println name
}
	  

Для настройки существующего набора исходников, вы просто используете один из методов доступа, показанных выше, чтобы установить его свойства. Свойства описаны ниже. Вот пример настройки основной папки Java и папки ресурсов:

Пример 47.4. Настройка папок с иходниками набора исходников

build.gradle

sourceSets {
    main {
        java {
            srcDirs = ['src/java']
        }
        resources {
            srcDirs = ['src/resources']
        }
    }
}
	  

47.7.1. Свойства набора исходников.

В таблице перечислены некоторые важные свойства набора исходников. Остальные вы сможете найти в документации API для SourceSet.

Таблица 47.9. Плагин Java - свойства набора исходников
Имя свойстваТипЗначение по умолчаниюОписание
nameString (только для чтения)Не nullИмя набора исходников, используемое для его идентификации.
outputSourceSetOutput (только для чтения)Не nullВыходные файлы набора исходников, содержащие его скомпилированные классы и ресурсы.
output.classesDirFilebuildDir/classes/nameПапка, куда генерируются классы набора исходников.
output.resourcesDirFilebuildDir/resources/nameПапка, куда генерируются ресурсы набора исходников.
compileClasspathFileCollectionконфигурация compileНаборИсходниковПуть к классам, используемый при компиляции исходных файлов набора исходников.
runtimeClasspathFileCollectionконфигурация output + runtimeНаборИсходниковПуть к классам, используемый при выполнении классов набора исходников.
javaSourceDirectorySet (только для чтения)Не nullИсходные файлы Java набора исходников. Содержит только файлы .java, найденные в папках исходников Java и исключает все остальные файлы.
java.srcDirsSet<File>. Может быть установлено с помощью всего, что описано в Секции 20.5 Указание набора входных файлов.[projectDir/src/name/java]Папки исходников, содержащие исходные файлы Java набора исходников.
resourcesSourceDirectorySet (только для чтения)Не nullРесурсы набора исходников. Содержит только ресурсы и исключает все файлы .java, найденные в папках ресурсов. Другие плагины, такие как плагин Groovy, исключают дополнительные типы файлов из этой коллекции.
resources.srcDirsSet<File>. Может быть установлено с помощью всего, что описано в Секции 20.5 Указание набора входных файлов.[projectDir/src/name/resources]Исходные папки, содержащие ресурсы набора исходников.
allJavaSourceDirectorySet (только для чтения)javaВсе файлы .java набора исходников. Некоторые плагины, такие как плагин Groovy, добавляют дополнительные исходные файлы Java в эту коллекцию.
allSourceSourceDirectorySet (только для чтения)resources + javaВсе исходные файлы набора исходников. Сюда включаются все файлы ресурсов и все исходные файлы Java. Некоторые плагины, такие как плагин Groovy, добавляют дополнительные исходные файлы в эту коллекцию.

47.7.2. Определение новых наборов исходников.

Для определения нового набора исходников, вы просто ссылаетесь на него в блоке sourceSets { }. Вот пример:

Пример 47.5. Определение набора исходников

build.gradle

sourceSets {
    intTest
}
	  

Когда вы определяете новый набора исходников, плагин Java добавляет несколько конфигураций зависимостей к нему, как показано в Таблице 47.6. Плагин Java - конфигурации зависимостей наборов исходников. Вы можете использовать эти конфигурации, чтобы задать зависимости компиляции и времени выполнения.

Пример 47.6. Задаем зависимости набора исходников

build.gradle

sourceSets {
    intTest
}

dependencies {
    intTestCompile 'junit:junit:4.12'
    intTestRuntime 'org.ow2.asm:asm-all:4.0'
}
	  

Также плагин Java добавляет задачи, которые собирают классы набора исходников, как показано в Таблице 47.2. Плагин Java - задачи набора исходников. Например, для набора исходников, названного intTest, компиляция классов производится запуском команды gradle intTestClasses.

Пример 47.7. Компиляция набора исходников

Вывод команды gradle intTestClasses

> gradle intTestClasses
:compileIntTestJava
:processIntTestResources
:intTestClasses

BUILD SUCCESSFUL

Total time: 1 secs
	  

47.7.3. Несколько примеров наборов исходников.

Добавление jar-файла, содержащего классы набора исходников:

Пример 47.8. Сборка jar-файла для набора исходников

build.gradle

task intTestJar(type: Jar) {
    from sourceSets.intTest.output
}
	  

Генерация Javadoc для набора исходников:

Пример 47.9. Генерация Javadoc для набора исходников

build.gradle

task intTestJavadoc(type: Javadoc) {
    source sourceSets.intTest.allJava
}
	  

Добавление набора тестов для тестирования набора исходников:

Пример 47.10. Запуск тестов для набора исходников

build.gradle

task intTest(type: Test) {
    testClassesDir = sourceSets.intTest.output.classesDir
    classpath = sourceSets.intTest.runtimeClasspath
}
	  

47.8. Javadoc.

Задача javadoc - это экземпляр Javadoc. Она поддерживает основные опции Javadoc и опции стандартного доклета, описанного в справочной документации Javadoc. Чтобы узнать полный список поддерживаемых опций Javadoc, обратитесь к документации API следующих классов: CoreJavadocOptions и StandardJavadocDocletOptions.

Таблица 47.10. Плагин Java - свойства Javadoc
Свойство задачиТипЗначение по умолчанию
classpathFileCollectionsourceSets.main.output + sourceSets.main.compileClasspath
sourceFileTree. Может быть установлено с помощью всего, что описано в Секции 20.5 Указание набора входных файловsourceSets.main.allJava
destinationDirFiledocsDir/javadoc
titleStringИмя и версия проекта

47.9. Clean.

Задача clean - это экземпляр Delete. Она просто удаляет папку, указанную в ее свойстве dir.

Таблица 47.11. Плагин Java - свойства Clean
Свойство задачиТипЗначение по умолчанию
dirFilebuildDir

47.10. Ресурсы.

Плагин Java использует задачу Copy для управления ресурсами. Он добавляет экземпляр этой задачи к каждому набору исходников проекта. Узнать больше о задаче copy можно в Секции 20.6 Копирование файлов.

Таблица 47.12. Плагин Java - свойства ProcessResources
Свойство задачиТипЗначение по умолчанию
srcDirsObject. Может быть установлено с помощью всего, что описано в Секции 20.5 Указание набора входных файловнаборИсходников.resources
destinationDirFile. Может быть установлено с помощью всего, что описано в Секции 20.1 Нахождение файловнаборИсходников.output.resourcesDir

47.11. Компиляция Java.

Плагин Java добавляет экземпляр JavaCompile к каждому набору исходников проекта. Несколько самых основных свойств перечислены в таблице ниже.

Таблица 47.13. Плагин Java - свойства компиляции
Свойство задачиТипЗначение по умолчанию
classpathFileCollectionнаборИсходников.compileClasspath
sourceFileTree. Может быть установлено с помощью всего, что описано в Секции 20.5 Указание набора входных файловнаборИсходников.java
destinationDirFile.наборИсходников.output.classesDir

По умолчанию, компилятор Java запускается в процессе Gradle. В результате установки options.fork в true компиляция будет происходить в отдельном процессе. В случае задачи Ant javac, это означает, что будет создан новый процесс для каждой задачи компиляции, что может замедлить всю компиляцию. С другой стороны, прямая интеграция Gradle с компилятором (смотрите выше), позволит использовать процесс компилятора настолько, насколько возможно. В обоих случаях все опции fork, указанные с параметрами options.forkOptions, будут выполнены.

47.12. Инкрементная компиляция Java.

Начиная с Gradle 2.1, существует возможность инкрементной компиляции Java. Смотрите задачу JavaCompile, чтобы узнать как включить ее.

Основные цели инкрементной компиляции:

  • Избежание траты времени на компиляцию классов, которые не надо компилировать. Это означает, что сборки будут быстрее, особенно, когда изменяется исходные класс или jar-файл, который не влечет за собой перекомпиляцию многих исходных классов, зависящих от него.
  • Изменение выходных классов как можно меньше. Классы, которые нет необходимости перекомпилировать, остаются неизмененными в выходной папке. Пример сценария, когда это может быть полезно - это использование JRebel, чем меньше выходных классов будет изменено, тем быстрее jvm сможет использовать обновленные классы.

Инкрементная компиляция на высоком уровне:

  • Обнаружение корректного набора устаревших классов безотказно при некотором уменьшении скорости. Алгоритм использует анализ байт-кода и изящно работает с оптимизациями компилятора (встраивание открытых констант), переходными зависимостями классов и так далее. Пример: когда класс с открытой константой изменяется, мы компилируем классы, которые ее используют, чтобы избежать проблем с встроенными компилятором константами.
  • Чтобы сделать компляцию быстрой, мы кэшируем результат анализа классов и снимки jar-файлов. Первая инкрементая компиляция может быть медленнее из-за пустых кэшей.

Известные проблемы:

  • Если задача компиляции завершилась с ошибкой компиляции, в следующий раз будет выполнена полная компиляция.
  • Из-за стирания типа, инкрементная компиляция не способна распознать когда тип используется только в качестве параметра типа и в действительности никогда не используется в коде. Например, представим, что у нас есть следующий код: List<? extends A> list = Lists.newArrayList();, но A никогда не используется в коде, тогда изменение A не запустит повторную компиляцию класса. На практике, такое случается очень редко.

47.13. Избегание компиляции.

Если зависимый проект изменился способом совместимым с ABI (только его закрытое API изменилось), тогда задачи компиляции Java остаются актуальными. Это означает, что если проект A зависит от проекта B и класс в B изменился способом совместимым с ABI (обычно, изменяется только тело метода), тогда Gradle не будет перекомпилировать A.

Некоторые типы изменений, которые не затрагивают открытое API и игнорируются:

  • Изменение тела метода.
  • Изменение комментария.
  • Добавление, удаление или изменение закрытых методов, полей или внутренних классов.
  • Добавление, удаление или изменение ресурса.
  • Изменение jar-файлов или папок в пути к классам.
  • Переименование параметра.

Избегание компиляции отключается, елси в пути к классах находятся обработчики аннотация, потому что для них детали реализации имеют значение. Чтобы лучше разделить эти проблемы, рекомендуется объявлять обработчики аннотация отдельно: CompileOptions для типа задачи JavaCompile определяет свойство annotationProcessorPath, которое можно использовать для объявления обработчиков аннотаций. Для них рекомендуется использовать отдельную конфигурацию:

Пример 47.11. Объявление обработчиков аннотаций

build.gradle

configurations {
    apt
}
dependencies {
    // The dagger compiler and its transitive dependencies will only be found on annotation processing classpath
    apt 'com.google.dagger:dagger-compiler:2.8'

    // And we still need the Dagger annotations on the compile classpath itself
    implementation 'com.google.dagger:dagger:2.8'
}

compileJava {
    options.annotationProcessorPath = configurations.apt
}
	  

47.14. Задача test.

Задача test - экземпляр Test. Она автоматически обнаруживает и выполняет все юнит-тесты в наборе исходников test. После выполнения тестов, она генерирует отчет. Поддерживаются и JUnit, и TestNG. Чтобы узнать больше, взгляните на Test.

47.14.1. Выполнение тестов.

Тесты выполняются в отдельной JVM, изолированной от основного процесса сборки. API задачи Test дает вам небольшой контроль над тем как это происходит.

Есть много свойств, которые контролируют то, как процесс теста запускается. Они включают в себя системные свойства, аргументы JVM и исполнимый файл Java.

Вы можете указать, выполнять ли ваши тесты параллельно или нет. При параллельном выполнении тестов, выполняется несколько процессов тестов одновременно. Каждый тестовый процесс выполняет только один тест за раз, так что вам не надо делать ничего особенного с тестами, чтобы воспользоваться преимуществами параллельного выполнения. Свойство maxParallelForks указывает максимальное количество тестовых процессов запущенных одновременно. Значение по умолчанию 1, то есть, не выполнять тесты параллельно.

Процесс теста устанавливает системному свойству org.gradle.test.worker уникальный идентификатор тестового процесса, который вы можете использовать, например, в именах файлов или других идентификаторах ресурсов.

Вы можете указать, что тестовые процессы должны перезапускаться после выполнения определенного количества тестовых классов. Это может быть хорошей альтернативой выделения вашему тестовому процессу огромной кучи. Свойство forkEvery задает максимальное число тестовых классов выполняемы в тестовом процессе. По умолчанию выполняется неограниченное число тестов в каждом процессе.

У задачи есть свойство ignoreFailures для контроля поведения при завершении теста с ошибкой. Задача test всегда выполняет все тесты, которые обнаружит. Она останавливает дальнейшую сборку, если ignoreFailures установлено в false и есть тесты с ошибкой. Значение по умолчанию - false.

Свойство testLogging позволяет вам настроить какие события тестов будут логгироваться и на каком уровне. По умолчанию, краткое сообщение будет добавлено в лог для каждого проваленного теста. Чтобы узнать как настроить логгирование тестов под ваши нужды, смотрите TestLoggingContainer.

47.14.2. Отладка.

Задача test предоставляет свойство Test.getDebug(), которое можно установить для запуска, чтобы JVM ожидала подключение отладчика к порту 5005 до того, как продолжит выполнение тестов.

Также такое поведение можно включить в время выполнения посредством опции задачи --debug-jvm (начиная с Gradle 1.12).

47.14.3. Фильтрация тестов.

Начиная с Gradle 1.10, есть возможность включать только определенные тесты, основываясь на шаблоне имени. Фильтрация - это механизм отличный от включения/выключения, которое будет описано в следующих нескольких параграфах (-Dtest.single, test.include и прочие). Последний основан на файлах, например, физическом расположение класса реализации теста. Выбор теста на уровне файла не поддерживает множество интересных сценариев, которые возможны с фильтрацией на уровне теста. Некоторые из них уже обрабатываются Gradle, а некоторые будут в будущих релизах:

  • Фильтрация на уровне определенных методов теста; выполнение одного метода теста.
  • Фильтрация, основанная на пользовательских аннотациях (в будущем).
  • Фильтрация, основанная на иерархии тестов; выполнение всех тестов, которые расширяют определенный базовый класс (в будущем).
  • Фильтрация на основе какого-либо пользовательского правила времени выполнения, например, особое значение системного свойства или какое-либо статическое состояние (в будущем).

У функции фильтрации тестов следующая характеристика:

  • Поддерживаются полностью определенное имя класса или полностью определенное имя метода, например, 'org.gradle.SomeTest', 'org.gradle.SomeTest.someMethod'.
  • Поддерживается метасимвол '*', который соответствует любому символу.
  • Опция командной строки '--tests' предоставлена для удобной установки фильтра тестов. Она особенно полезна в случае классического 'выполнения одиночного метода теста'. Когда используется опция командной строки, фильтр включения, объявленный в сборочном скрипте, игнорируется. Есть возможность предоставить несколько опций '--tests' и тесты, подходящие под эти шаблоны, будут включены.
  • Gradle пытается фильтровать тесты с учетом ограничений API фреймворка тестов. Некоторые современные, синтетические тесты могут быть не полностью совместимы с фильтрацией. Однако, подавляющее большинство тестов и вариантов их использования должны обрабатываться хорошо.
  • Фильтрация тестов перезаписывает выбор, основанный на файлах. Последний может быть полностью заменен в будущем. Мы будем нарашивать API фильтрации тестов и добавлять больше типов фильтров.
Пример 47.12. Фильтрация тестов в сборочном скрипте

build.gradle

test {
    filter {
        //include specific method in any of the tests
        includeTestsMatching "*UiCheck"

        //include all tests from package
        includeTestsMatching "org.gradle.internal.*"

        //include all integration tests
        includeTestsMatching "*IntegTest"
    }
}
	  

За примерами и деталями обращайтесь к справке по TestFilter.

Несколько примеров использования опции командной строки:

  • gradle test --tests org.gradle.SomeTest.someSpecificFeature
  • gradle test --tests *SomeTest.someSpecificFeature
  • gradle test --tests *SomeSpecificTest
  • gradle test --tests *SomeSpecificTestSuite
  • gradle test --tests all.in.specific.package*
  • gradle test --tests *IntegTest
  • gradle test --tests *IntegTest*ui*
  • gradle test --tests "com.example.MyTestSuite"
  • gradle test --tests "com.example.ParameterizedTest"
  • gradle test --tests "*ParameterizedTest.foo*"
  • gradle test --tests "*ParameterizedTest.*[2]"
  • gradle someTestTask --tests *UiTest someOtherTestTask --tests *WebTest*ui

47.14.4. Одиночное выполнение теста посредством системных свойств.

Этот механизм был заменен 'Фильтрацией тестов', описанной выше.

После установки системного свойства taskName.single = шаблонИмениТеста, будут выполнены только те тесты, которые соответствуют указанному шаблону. ИмяТеста может быть как полным путем мультипроекта, наподобие ':sub1:sub2:test' или просто именем задачи. ШаблонИмениТеста будут использован для формирования шаблона вида '**/шаблонИмениТеста*.class'. Если не будет найдено ни одного теста под этот шаблон, будет выброшено исключение. Это ваш щит от ложной безопасности. Если выполняются тесты более чем одного подпроекта, шаблон применяется к каждому подпроекту. Исключение будет выброшено, если ни одного теста не найдено для определенного подпроекта. В таком случае, вы можете использовать нотацию пути шаблона, чтобы применить его только к задаче test определенного подпроекта. В качестве альтернативы, вы можете указать полностью определенное имя задачи для выполнения. Также вы можете указать несколько шаблонов. Примеры:

  • gradle -Dtest.single=ThisUniquelyNamedTest test
  • gradle -Dtest.single=a/b/ test
  • gradle -DintegTest.single=*IntegrationTest integTest
  • gradle -Dtest.single=:proj1:test:Customer build
  • gradle -DintegTest.single=c/d/ :proj1:integTest

47.14.5. Обнаружение тестов.

Задача test обнаруживает классы тестов инспектируя скомпилированные классы. По умолчанию, она сканирует все файлы .class. Вы можете установить свои собственные включения/выключения, только эти классы будут просканированы. В зависимости от используемого фреймворка тестов (JUnit/TestNG), для обнаружения классов тестов используются различные критерии.

При использовании JUnit, мы сканируем классы тестов JUnit 3 и 4. Если любой из перечисленных критериев подходит, класс считается классом теста JUnit:

  • Класс или его родительский класс наледуются от TestCase или GroovyTestCase.
  • Класс или его родительский класс аннотирован @RunWith.
  • Класс или его родительский класс содержат метод, аннотированный @Test.

При использовании TestNG, мы сканируем на наличие методов аннотированных @Test.

Обратите внимание на то, что абстрактные классы не выполняются. Также Gradle сканирует дерево наследования в jar-файлах в пути к классам тестов.

Если вы не хотите использовать обнаружение классов тестов, его можно отключить установив настройку scanForTestClasses в false. После установки этой настройки, задача test будет использовать только включение/выключение для поиска классов. Если настройка scanForTestClasses установлена в false и шаблонов включения/выключения не указано, по умолчанию они '*/*Tests.class', '**/*Test.class' and '**/Abstract*.class' для включения и выключения, соответственно.

47.14.6. Группировка тестов.

В JUnit и TestNG есть возможность использовать сложную группировку методов тестов.

Для группировки классов тестов и методов JUnit, JUnit 4.8 представляет концепцию категорий. Задача test позволяет указывать категории JUnit, которые вы хотите включить или исключить.

Пример 47.13. Категории JUnit

build.gradle

test {
    useJUnit {
        includeCategories 'org.gradle.junit.CategoryA'
        excludeCategories 'org.gradle.junit.CategoryB'
    }
}
	  

В фреймворке TestNG есть похожая концпеция. В нем вы можете указывать различные группы тестов. Группы тестов, которые должны быть включены или исключены из выполнения, могут быть настроены в задаче test.

Пример 47.14. Группировка тестов TestNG

build.gradle

test {
    useTestNG {
        excludeGroups 'integrationTests'
        includeGroups 'unitTests'
    }
}
	  

47.14.7. Порядок выполнения тестов в TestNG.

TestNG позволяет явно контролировать порядок выполнения тестов.

Свойство preserveOrder контролирует, будут ли тесты выполняться в детерминистическом порядке или нет. Сохранение порядка гарантирует, что полный тест (включая @BeforeXXX и @AfterXXX) запускается в потоке теста до запуска следующего. Хотя сохранение порядка тестов - поведение по умолчанию, когда идет работа напрямую с файлами testng.xml, TestNG API, которое используется для запуска тестов программно, так же как и интеграция Gradle с TestNG выполняют тесты по умолчанию в непредсказуемом порядке. Сохранение порядка тестов было представлено вместе с версией TestNG 5.14.5. Установка свойства preserveOrder в true для более старых версий послужит причиной падения сборки.

Пример 47.15. Сохранение порядка тестов TestNG

build.gradle

test {
    useTestNG {
        preserveOrder true
    }
}
	  

Свойство groupByInstance контролирует, должны ли тесты быть сгруппированы по экземплярам. В результате группировки по экземплярам зависимости метода теста будут разрешаться для каждого экзмепляра вместо запуска сначала всех зависимых экземпляров, а потом всех зависящих. По умолчанию тесты не группируются. Группировка тестов по экземплярам была представлена в версии TestNG 6.1. Установка свойства groupByInstances в true дял более старых версии приведет к падению сборки.

Пример 47.16. Группировка тестов TestNG по экземплярам

build.gradle

test {
    useTestNG {
        groupByInstances true
    }
}
	  

47.14.8. Отчеты о тестах.

По умолчанию, задача test генерируется следующие результаты.

  • HTML-отчет о тестировании.
  • Результаты в XML-формате, которые совместимы с задачей отчетов Ant JUnit. Этот формат поддерживается множеством других инструментов, таких как сервера непрерывной интеграции.
  • Результаты в эффективном двоичном формате. Задача генерирует другие результаты из этих.

Также есть автономный тип задачи TestReport, который генерирует HTML-отчет о тестах из двоичных результатов, сгенерированных одним или более экземплярами задачи test. Чтобы использовать этот тип задачи, вам надо задать destinationDir и результаты тестирования, которые включаются в отчет. Ниже пример, в коором генерируется объединенный отчет по юнит-тестам подпроектов:

Пример 47.17. Создание отчета по юнит-тестам подпроектов

build.gradle

subprojects {
    apply plugin: 'java'

    // Disable the test report for the individual test task
    test {
        reports.html.enabled = false
    }
}

task testReport(type: TestReport) {
    destinationDir = file("$buildDir/reports/allTests")
    // Include the results from the `test` task in all subprojects
    reportOn subprojects*.test
}
	  

Вам стоит обратить внимание на то, что тип TestReport объединяет результаты несколько задач test и ему требуется собрать вместе результаты индивидуальных классов тестов. Это означает, что если данных класс теста выполняется несколькими задачами test, тогда в отчет будут включены выполнения этого класса, но может быть трудно различить индивидуальные выполнения и их вывод.

Параметризованные методы TestNG и отчеты.

TestNG поддерживает параметризованные методы тестов, позволяю определенному методу теста выполняться несколько раз с разными входными значениями. Gradle включает значения параметров в отчет о выполнении метода теста.

Если у нас есть метод теста с именем aTestMethod, который принимает два параметра, в отчете он появится с именем: aTestMethod(toStringValueOfParam1, toStringValueOfParam2). Это делает легкой идентфикацию значений параметов для определенной итерации.

47.14.9. Условные значения.

Таблица 47.14. Плагин Java - свойства задачи testg
Свойство задачиТипЗначение по умолчанию
testClassesDirFilesourceSets.test.output.classesDir
classpathFileCollectionsourceSets.test.runtimeClasspath
testResultsDirFiletestResultsDir
testReportDirFiletestReportDir

47.15. Задача jar.

Задача jar создает jar-файл, содержащий файлы классов и ресурсы проекта. Jar-файл объявляется в качестве артефакта конфигурации зависимостей archives. Это означает, что он доступен в пути к классам зависимого проекта. Если вы выгружаете ваш проект в хранилище, этот jar-файл объявляется в качестве части описания зависимости. Вы можете узнать больше о том, как работать с архивами в Секции 20.8 Создание архивов, а о конфигурациях артефактов в Главе 32 Публикация артефактов.

47.15.1. Манифест.

У каждого объекта jar или war есть свойство manifest с отдельным экземпляром Manifest. Когда генерируется архив, соответствующий файл MANIFEST.MF записывается в архив.

Пример 47.18. Настройка файла MANIFEST.MF

build.gradle

jar {
    manifest {
        attributes("Implementation-Title": "Gradle",
                   "Implementation-Version": version)
    }
}
	  

Вы можете создать автономные экземпляры Manifest. Это может использоваться, например, для разделения информации манифеста между jar-файлами.

Пример 47.19. Создание объекта манифеста

build.gradle

ext.sharedManifest = manifest {
    attributes("Implementation-Title": "Gradle",
               "Implementation-Version": version)
}
task fooJar(type: Jar) {
    manifest = project.manifest {
        from sharedManifest
    }
}
	  

Вы можете слить другие в манифесты в любой объект Manifest. Они могут быть описаны либо путем к файлу, либо, как в примере выше, ссылкой на другой объект Manifest.

Манифесты сливаются в том порядке, в котором они объявлены в инструкции from. Если оба манифеста, базовый и сливаемый, задают значения для одного и того же ключа, по умолчанию, побеждает сливаемый. Вы можете полностью настроить поведение сливания, добавив действия eachEntry, в которых у вас есть доступ к экземпляру ManifestMergeDetails для каждого элемента в результирующем манифесте. Процесс сливания запускается не сразу же инструкцией from. Он стартует 'лениво', либо при генерации jar-файла, либо вызовом writeTo или effectiveManifest.

Вы легко можете записать манифест на диск.

Пример 47.20. Отдельный MANIFEST.MF для определенного архива

build.gradle

task barJar(type: Jar) {
    manifest {
        attributes key1: 'value1'
        from sharedManifest, 'src/config/basemanifest.txt'
        from('src/config/javabasemanifest.txt',
             'src/config/libbasemanifest.txt') {
            eachEntry { details ->
                if (details.baseValue != details.mergeValue) {
                    details.value = baseValue
                }
                if (details.key == 'foo') {
                    details.exclude()
                }
            }
        }
    }
}
	  

build.gradle

jar.manifest.writeTo("$buildDir/mymanifest.mf")
	  

47.16. Выгрузка.

Как выгружать архивы описано в Главе 32 Публикация артефактов.

47.17. Компиляция и тестирование на Java 6.

Gradle может запускаться на версии Java 7 или выше. Gradle 3.x все еще поддерживает компиляцию, тестирование, генерацию Javadoc и выполнение приложений на Java 6. Java 5 не поддерживается.

Чтобы использовать Java 6, необходимо настроить следующие задачи:

  • Задачу JavaCompile для создания новых процессов и использования корректной домашней папки Java.
  • Задачу Javadoc для использования корректного исполнимого файла javadoc.
  • Задачи Test и JavaExec для использования корректного исполнимого файла java.

Следующие примеры показывают как необходимо изменить build.gradle. Для того, чтобы сделать сборку независмой от машины, расположение домашней папки Java 6 должно быть настроено в GRADLE_USER_HOME/gradle.properties в домашней папке пользователя на каждой машине разработки, как показно в примере.

Пример 47.21. Настройка сборки Java 6

gradle.properties

# in $HOME/.gradle/gradle.properties
java6Home=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
	  

build.gradle

sourceCompatibility = 1.6

assert hasProperty('java6Home') : "Set the property 'java6Home' in your your gradle.properties pointing to a Java 6 installation"
def javaExecutablesPath = new File(java6Home, 'bin')
def javaExecutables = [:].withDefault { execName ->
    def executable = new File(javaExecutablesPath, execName)
    assert executable.exists() : "There is no ${execName} executable in ${javaExecutablesPath}"
    executable
}
tasks.withType(AbstractCompile) {
    options.with {
        fork = true
        forkOptions.javaHome = file(java6Home)
    }
}
tasks.withType(Javadoc) {
    executable = javaExecutables.javadoc
}
tasks.withType(Test) {
    executable = javaExecutables.java
}
tasks.withType(JavaExec) {
    executable = javaExecutables.java
}