Межмодульное покрытие кода с помощью многомодульного проекта jacoco и gradle

Мы используем инструмент Gradle 3.3 и jacoco версии 0.7.6.201602180812. У нас есть такой многопроект Gradle:

  • parent
    • prod1
    • prod2
    • prod3
    • int-test

Мы используем модульные тесты, проверяющие исходники проекта и jacoco для всех дочерних проектов, создающих файлы test.exec. У нас есть дополнительные интеграционные тесты в проекте int-test, добавляющие результаты jacoco в test-exec в проекте int-test. Мы используем плагин sonarqube gradle (2.2.1) в родительском проекте, чтобы собрать все для сервера SonarQube v6.2.

Everything runs fine with tests that test sources in their own project: The code coverage is measured in the jacoco reports as well as on SonarQube.

Only the integration test (int-test project) coverage for the sources in the prod-projects (single process) is not measured neither in the coverage report in the project with the test nor in the project with the class.

Probably one needs to combine the coverage data on the top level project somehow - does anyone know how to do that? At best with SonarQube still showing the coverage on single module level as well.

РЕДАКТИРОВАТЬ Вот небольшой тестовый проект: https://github.com/MichaelZett/coveragetest Запуск
'build smokeTest sonarqube' приводит к:

  • Выполнение всех тестов
  • создание файлов jacoco / test.exec и test-results / test / ... во всех дочерних проектах
  • парсинг их в sonarqube
  • правильное измерение покрытия для тестов, которые тестируют исходники в своих проектах
  • отсутствует покрытие для тестов, которые тестируют исходники в другом проекте

person Michael Zöller    schedule 02.01.2017    source источник
comment
Структура вашего проекта (ов) неясна. Вы можете отредактировать свой вопрос, чтобы расширить его.   -  person G. Ann - SonarSource Team    schedule 03.01.2017
comment
Спасибо за вклад, я постарался сделать свой макет более понятным.   -  person Michael Zöller    schedule 04.01.2017
comment
Ваш примерный проект все еще доступен как-то? Если я понимаю, как JacocoSensor работает в Sonar, он требует, чтобы файлы классов были доступны в пути к классам вашего модуля int-test. Имеются в виду классы из prod1, prod2 и т. Д.   -  person dbalakirev    schedule 05.01.2018
comment
@dbalakirev Я удалил пример некоторое время назад, так как моя проблема решена.   -  person Michael Zöller    schedule 05.01.2018


Ответы (3)


Говоря о SonarQube: вы можете получить агрегированный отчет, используя одно и то же местоположение для jacoco.exec во всех модулях. Перед сборкой убедитесь, что файл удален и добавлен во все модули.

Говоря исключительно о Gradle: взгляните на

person Godin    schedule 03.01.2017
comment
Спасибо за вклад, я попробую задачу jacocoMerge. Для SonarQube: он отлично собирает отдельные данные, но не данные между проектами. - person Michael Zöller; 04.01.2017
comment
@ MichaelZöller еще раз о SonarQube: собирайте данные в один файл jacoco.exec, например. в ../jacoco.exec, относящемся к каждому модулю или любому другому файлу, который является одинаковым для всех модулей, указать SonarQube использовать это местоположение во время анализа, установив sonar.jacoco.reportPath в ../jacoco.exec. Если это не помогает, постарайтесь более точно описать вашу проблему и привести пример (см. stackoverflow.com/help/mcve). - person Godin; 04.01.2017
comment
Наконец я нашел время, чтобы использовать единый подход jacoco.exec. Это работает. Кроме того, нужно скопировать результаты junit-теста в одно место, чтобы получить правильное количество тестов в SonarQube. Я адаптировал проект на github под нужные задачи. Новое свойство SQ 6.2 для коллекции путей jacoco, похоже, не работает с плагином sonar-gradle-plugin версии 2.2.1. - person Michael Zöller; 08.03.2017

Как отмечено в комментариях, вы должны сначала объединить данные выполнения Jacoco, а затем указать sonarqube использовать это вместо отдельных exec файлов, генерируемых каждым подмодулем.

Я добавляю здесь пример, поскольку ссылки, приведенные в принятом ответе, немного вводят в заблуждение. Большинство из них предоставляют вам различные обходные пути для объединения отчетов Jacoco, а не для объединения данных выполнения, как вы хотите.

Вот как бы это выглядело:

def allTestCoverageFile = "$buildDir/jacoco/allTestCoverage.exec"

sonarqube {
    properties {
        property "sonar.projectKey", "your.org:YourProject"
        property "sonar.projectName", "YourProject"
        property "sonar.jacoco.reportPaths", allTestCoverageFile
    }
}

task jacocoMergeTest(type: JacocoMerge) {
    destinationFile = file(allTestCoverageFile)
    executionData = project.fileTree(dir: '.', include:'**/build/jacoco/test.exec')
}

task jacocoMerge(dependsOn: ['jacocoMergeTest']) {
    // used to run the other merge tasks
}

subprojects {
    sonarqube {
        properties {
            property "sonar.jacoco.reportPaths", allTestCoverageFile
        }
    }
}

В двух словах:

  • Во-первых, мы определяем выходной файл глобального покрытия для наших отчетов об испытаниях (allTestCoverageFile).
  • Затем нам нужно указать Sonarqube использовать этот файл (используя sonar.jacoco.reportPaths). Но обратите внимание, что мы также должны сделать это при закрытии подпроектов. Это очень важно. Не пропустите.
  • Наконец, мы создаем настраиваемую задачу, которая расширяется от JacocoMerge (инкубирующий класс из подключаемого модуля Jacoco), которая объединяет все отчеты о тестовом покрытии из всех проектов (executionData) в наш allTestCoverageFile.

Если вы используете версию SonarQube до 6.2, используйте свойство sonar.jacoco.reportPath

person Cristian    schedule 08.03.2018
comment
Привет, Кристиан, в чем разница между двумя свойствами sonar.jacoco.reportPaths и sonar.jacoco.reportPath (без завершающих 's')? Вы пишете, что важно установить это свойство глобально и для каждого подпроекта ... но это два разных свойства! В чем дело...?! - person dokaspar; 29.03.2018
comment
docs.sonarqube.org/display/ PLUG / если вы используете версию SonarQube до 6.2, используйте свойство sonar.jacoco.reportPath - person Cristian; 30.03.2018
comment
Я понимаю что ты имеешь ввиду. У меня опечатка. Я починю это. - person Cristian; 30.03.2018
comment
@Cristian Могу я получить вашу электронную почту? - person Pie; 08.12.2018
comment
Итак, sonarqube.com в настоящее время не работает, но я ясно помню, что читал, что последний плагин Sonarqube org.sonarqube: 2.7 не поддерживает двоичные файлы .exec. Я также помню, что читал, что плагин sonarqube должен запускаться только из корневого проекта (поэтому выделенная инструкция запуска его из закрытия подпроектов кажется устаревшей). Это означает, что вышеуказанное решение больше не применимо. Плагин org.sonarqube: 2.7, похоже, действительно поддерживает список XML-отчетов, разделенных запятыми, для свойства sonar.coverage.jacoco.xmlReportPaths, поэтому я ищу какой-нибудь скрипт Groovy для его создания. - person Frans; 04.02.2020
comment
docs.sonarqube.org/latest/analysis/scan/sonarscanner-for -gradle говорит: Чтобы проанализировать иерархию проекта, примените плагин SonarQube к корневому проекту иерархии. - person Frans; 04.02.2020
comment
У вас есть полный пример? Я предполагаю, что часть jacocoTestReport или codeCoverageReport отсутствует, не так ли? Вдобавок я спрашиваю себя, как запустить это через gitlab-ci. - person Bobbelinio; 19.08.2020

subprojects {
    apply(plugin: 'org.jetbrains.kotlin.jvm')

    repositories {
        jcenter()
        mavenCentral()
   }
}

task codeCoverageReport(type: JacocoReport) {

    // Gather execution data from all subprojects
    executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")

    // Add all relevant sourcesets from the subprojects
    subprojects.each {
        sourceSets it.sourceSets.main
    }

    reports {
        xml.enabled true
        html.enabled true
        csv.enabled false
    }
}

// always run the tests before generating the report
codeCoverageReport.dependsOn {
    subprojects*.test
}

sonarqube {
    properties {
        property "sonar.projectKey", "your_project_key"
        property "sonar.verbose", true
        property "sonar.projectName", "Your project name"
        property "sonar.coverage.jacoco.xmlReportPaths", "${rootDir}/build/reports/jacoco/codeCoverageReport/codeCoverageReport.xml"
    }
}

Команда для запуска теста с покрытием:

./gradlew codeCoverageReport
./gradlew sonarqube -x test (test is excluded since already run and sonarqube by default executes test)

Следует отметить две вещи, которые заставили его работать:

  1. Чтобы сделать доступными исходные наборы всех модулей, работал цикл над подпроектами и накопление исходных наборов. subprojects.sourceSets.main.allSource.srcDirs не работал.
  2. sonar.jacoco.reportPaths устарел. Нам нужно использовать sonar.coverage.jacoco.xmlReportPaths. Ознакомьтесь с документацией здесь

person Ashwini Mutalik Desai    schedule 11.03.2020
comment
Я получил Не удалось получить неизвестное свойство test для проекта: app типа org.gradle.api.Project. Я извлек эту логику в файл jacoco.gradle, но для меня это не имеет значения. - person Bobbelinio; 19.08.2020