【问题标题】:Jacoco Unit and Integration Tests coverage - individual and overall [closed]Jacoco 单元和集成测试覆盖范围 - 个人和整体 [关闭]
【发布时间】:2013-09-18 18:05:45
【问题描述】:

我有一个项目 (ProjectA),其中包含一些单元测试和集成测试。

以下是结构。

项目A - src/java (java 源代码)
- test/java(Junit 单元测试)
- 测试/资源(Junit 单元测试所需的资源)
- src/java-test(集成测试)
- conf(包含构建/测试/编译所需的 .xml 文件)

我运行以下命令——它们都有效,但我对 build.gradle / GRADLE_HOME/init.d/*.gradle 文件中的配置如何影响我得到的结果有疑问。

似乎我错过了什么,却没有得到我想要的东西。

命令
- gradle clean build -- 它工作正常
- gradle clean build jacocoTestReport -- 它工作正常。
- gradle clean build integrationTest jacocoTestReport - 它工作正常(如果我有一个 tomcat 实例启动并在同一个 ProjectA 的另一个腻子窗口中运行)。

第三个项目符号操作完成后,我在我的 Jenkins 作业工作区中看到了额外的文件夹“build”及其子文件夹(从源代码/版本控制中签出的除外)。

即下 -- JenkinsWorkspace
/构建
- classes(包含以下的 .class 文件,这些文件被称为 sourceSets 部分之一)
---- 集成测试
---- 主要
---- 测试

- resources(这包含源代码管理中“conf”文件夹下的所有文件 .properties/.xml 文件。

- 报告(包含 PMD/CheckStyle/Findbugs 的 .xml/.html 文件以及单元测试或 IT 测试的测试结果,但不能同时包含两者)。 ---- 检查样式
---- 发现错误
---- pmd
---- 雅可可
---- 测试(注意:这是复数,即它不是在 sourceSets 中定义为一个条目的“测试”)

- jacoco(这包含 2 个 .exec 文件,即 test.exec 和 integrationTest.exec 都有不同的文件大小)
---- test.exec
---- integrationTest.exec

- jacocoHtml(这个文件夹包含很多文件夹(包含.html文件),主要是它下面的“index.html”。
---- 一些xxx文件夹
---- ---- somexxfolder's.html 文件
---- index.html
---- 其他 etc 文件/文件夹

- test-results(其中包含一些 .xml 文件,但仅适用于单元测试或集成测试 - 但不是适用于给定时间的两种测试类型)。
即如果我运行“gradle clean build”,那么您将看到与单元测试相关的 .xml 文件和 如果我运行“gradle clean build integrationTest”,那么单元测试 .xml 文件将被覆盖 而我看到的 .xml 文件仅与 integrationTest 任务相关/生成。


以下是常用的gradle之一(GRADLE_HOME/init.d/some.common.gradle文件)

//
//Extra file can hold global Gradle settings so that these dont have to be inserted in project
//specific build.gradle file.
//Filename: extraN.common<anyname>.gradle
allprojects {
   apply plugin: 'java'
   apply plugin: 'pmd'
   apply plugin: 'findbugs'
   apply plugin: 'checkstyle'
   apply plugin: 'jacoco'
   apply plugin: 'sonar-runner'
   tasks.withType(Compile) {
     options.debug = true
     options.compilerArgs = ["-g"]
   }
   sourceSets {
      main {
         java {
            // MOTE: If your project's build.gradle specify the sourceSet section, the following
            // values will be overwritten by what project's build.gradle will set.
            //
            // If you project's sourceSet structure if different in each project, then in this
            // global common .gradle file, you can define srcDir for main or other sections like
            // test, integrationTest etc as shown below -- commented out. If that's the case, 
            // then uncomment the below line and comment out using // -- srcDir 'src/java' line
            // for sourceSets.main.java section. This rule applies to other sections as well.
            // srcDir 'no_src_dir_set_yet'

            srcDir 'src/java'
         }
         resources {
            srcDir 'conf'
         }
      }
      test {
         java {
            srcDir 'test/java'
         }
         resources {
            srcDir 'test/resources'
            srcDir 'conf'
         }
      }
      integrationTest {
         java {
            srcDir 'src/java-test'
         }
         resources {
            srcDir 'conf'
         }
      }
   }
   def sonarServerUrl = "dev.sandbox.server.com"
   sonarRunner {
      sonarProperties {
         property "sonar.host.url", "http://$sonarServerUrl:9000"
         property "sonar.jdbc.url", "jdbc:h2:tcp://$sonarServerUrl:9092/sonar"
         property "sonar.jdbc.driverClassName", "org.h2.Driver"
         property "sonar.jdbc.username", "sonar"
         property "sonar.jdbc.password", "sonar"
         properties ["sonar.sources"] += sourceSets.main.allSource.srcDirs
         //properties ["sonar.tests"] += sourceSets.test.java.srcDirs
         properties ["sonar.tests"] += sourceSets.integrationTest.allSource.srcDirs
      }
   }
   checkstyle {
        configFile = new File(rootDir, "config/checkstyle.xml")
        ignoreFailures = true
        //sourceSets = [sourceSets.main, sourceSets.test, sourceSets.integrationTest]
        sourceSets = [sourceSets.main]
   }
   findbugs {
        ignoreFailures = true
        sourceSets = [sourceSets.main]
   }
   pmd {
        ruleSets = ["basic", "braces", "design"]
        ignoreFailures = true
   }
   jacoco {
      toolVersion = "0.6.2.201302030002"
      reportsDir = file("$buildDir/customJacocoReportDir")
   }
   task testReport(type: TestReport) {
      destinationDir = file("$buildDir/reports/allTests")
   }
   test {
        jacoco {
            //destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
            destinationFile = file("$buildDir/jacoco/test.exec")
            //classDumpFile = file("$buildDir/jacoco/classpathdumps")
            classDumpFile = file("$buildDir/build/classes/test")
        }
   }
   jacocoTestReport {
         group = "Reporting"
         description = "Generate Jacoco coverage reports after running tests."
         reports {
                xml{
                    enabled true
                    destination "${buildDir}/reports/jacoco/jacoco.xml"
                }
                csv.enabled false
                html{
                    enabled true
                    destination "${buildDir}/jacocoHtml"
                }
        }
        additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
        //additionalSourceDirs = files([sourceSets.main.allJava.srcDirs, xxxx, 'xxxxyyyy' ])
   }
}

build.gradle 文件如下所示:

import com.tr.ids.gradle.CustomFileUtil
apply plugin: 'CustomSetup'
apply plugin: 'java'
apply plugin: 'customJarService'
apply plugin: 'customWarService'
sourceSets {
   main {
      java {
         srcDir 'src/java'
      }
   }
   test {
      java {
         srcDir 'test/java'
      }
      resources {
         srcDir 'test/resources'
         srcDir 'conf'
      }
   }
   integrationTest {
      java {
         srcDir 'src/java-test'
      }
   }
}
// Read dependency lists from external files. Our custom plugin reads a dep-xxx.txt file for compile/test/war related .jar file entries
// where each entry is like: groupid:artifactid:x.x.x
// and these artifact jars are available in Artifactory
List depListCompile = customFileUtil.readIntoList( "$projectDir/dep-compile.txt" )
List depListTest = customFileUtil.readIntoList( "$projectDir/dep-testArtifacts.txt" )
List depListWar = customFileUtil.readIntoList( "$projectDir/dep-war.txt" )
// Define dependencies
dependencies {
   // Compilation
   compile  depListCompile
   // Unit Tests
   testCompile depListTest
   // Integration tests
   // Everything from compile and testCompile targets
   integrationTestCompile configurations.compile
   integrationTestCompile configurations.testCompile
   // Output of compiling "main" files
   integrationTestCompile sourceSets.main.output
   // Additional dependencies from war and others
   integrationTestCompile depListTest, depListWar
   // All configuration files
   integrationTestRuntime files( 'conf' )
}
task deployTomcat( type: Copy, dependsOn: [ jar, compileIntegrationTestJava, warService ] ) {
   from "$buildDir/customWar/${project.name}.war"
   into "$projectDir/tomcat/webapps"
}
build {
  dependsOn deployTomcat
}
task integrationTest( type: Test, dependsOn: cleanTest ) {
   jacoco {
      //destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
      destinationFile = file("$buildDir/jacoco/integrationTest.exec")
      //classDumpFile = file("$buildDir/jacoco/classpathdumps")
      classDumpFile = file("$buildDir/classes/integrationTest")
   }
   testClassesDir = sourceSets.integrationTest.output.classesDir
   classpath = sourceSets.integrationTest.runtimeClasspath
}
apply plugin: 'eclipse'
eclipse.classpath {
   // Define output directory so Eclipse does not accidentally clobber /bin
   defaultOutputDir = file( 'out/classes' )
   // Add integration test
   plusConfigurations += configurations.integrationTestCompile
   // Remove unnecessary files
   file.whenMerged { classpath ->
      classpath.entries.removeAll { entry -> ( entry.path.indexOf( '/build/classes/main' ) > 0 ) }
      classpath.entries.removeAll { entry -> ( entry.path.indexOf( '/build/resources/main' ) > 0 ) }
   }
}

我的问题

1) 为什么“gradle clean build integrationTest”——它工作成功,会覆盖 build/reports/tests 和 build/test-results 文件夹中的测试结果。

2) 无论我在通用 gradle 文件下为 .exec 文件命名,还是在 build.gradle 中为 jacoco 的 integrationTest 任务命名,它总是创建 test.exec 和 integrationTest.exec 文件,但生成的构建/jacocoHtml 文件夹 index.html 文件不显示与单元/集成测试相关的覆盖率/文件。为了证明这一点,如果我运行“gradle clean build integrationTest jacocoTestReport sonarRunner”,我会看到作业的工作区,现在包含“.sonar”文件夹和 build/reports/sonar 文件夹,其中包含另一个名为“overall-xxx.exec”的文件" 一些文件,但该文件大小并不接近 Unit test.exec 和 IT integrationTest.exec 文件大小的“总和”。虽然它比 test.exec 文件大几个字节。

3) 我可以设置什么配置来全面覆盖单元和 IT 测试,即整体... exec 文件大小合适(运行 sonarRunner 任务后)。在 sonarRunner 任务期间,我确实看到 SonarRunner 任务的“jacocoSensor 步骤”确实会自动看到 UT 和 IT .exec 文件以及整个 .exec 文件(Sonar 的一个很好的功能)。

【问题讨论】:

    标签: unit-testing gradle integration-testing code-coverage jacoco


    【解决方案1】:

    找到我的第二个问题的答案。高级信息:

    1. Gradle 1.6 jacocoTestReport 使用不同的变量,Gradle >=1.7 使用不同。

      例如:我们可以通过使用正确的变量更改“test”或“integrationTest”任务来调整单元测试和集成测试.exec文件的创建 - 否则它不会工作n生成“test.exec”和“integrationTest.exec” " 默认文件名。请参见下面的示例。

    task integrationTest(type: Test) OR test { ... } 部分可以包含我们正在使用的给定 Gradle 版本的正确变量。

    task integrationTest (type: Test) { 
    testClassesDir = sourceSets.integrationTest.output.classesDir
    
    classpath = sourceSets.integrationTest.runtimeClasspath
    
       testReportDir = file("$buildDir/reports/tests/IT")
       testResultsDir = file("$buildDir/test-results/IT")
    
    
    
      ignoreFailures = true
       jacoco {
    
     //This works with 1.6
     destPath = file("$buildDir/jacoco/IT/jacocoIT.exec")
     classDumpPath = file("$buildDir/jacoco/IT/classpathdumps")
    
     /*
     Following works only with versions >= 1.7 version of Gradle
     destinationFile = file("$buildDir/jacoco/IT/jacocoIT.exec")
     classDumpFile = file("$buildDir/jacoco/IT/classpathdumps")
     */
       }
    }
    

    同样,对于 test { .... } 任务,您可以将其定义为 ../../UT/jacocoUT.exec 和 ../../UT/classpathdumps...

    1. 如果我在项目的工作区中运行“sonar-runner”Linux/Unix sonar-runner 可执行文件,则会创建.sonar 文件夹,但如果我运行调用 Gradle“clean build integrationTest jacocoTestReport sonarRunner”的 Jenkins 作业,然后构建/sonar 文件夹已创建并成为 SONAR 的 WORKING DIR(这会在输出过程中显示)。

    2. 在 Jenkins > 在 Post build 部分下,我提到了以下内容,现在,Jenkins Dashboard 上针对 jacoco 文件(.html 和 .xml)的 jacoco 代码覆盖率报告 - 包括单元和集成测试代码覆盖率数据。

    Jenkins 中的记录 Jacoco 覆盖率报告部分具有以下框:

    我提到过:

    执行文件的路径:/build/jacoco/UT/jacocoUT.exec, */build/jacoco/IT/jacocoIT.exec 类目录的路径:*/build/jacoco//classpathdumps/com/thc (这是 Jacoco 检测类所在的位置).. build/jacoco/UT/classpathdumps/com/thc 和 build/jacoco/IT/classpathdumps/com/thc 都将被选择,因为 ** 将被替换为下的任何文件夹建造/雅可可。该值也可以设置为“build/classes”文件夹。

    源文件路径:** (如果我使用 src/java,源文件的链接很少,即文件内容不显示).. 使用 ** .. 它现在可以工作了。

    休息盒 - 留空。

    1. 仍然想知道 - 为什么整体 jacoco.exec 没有文件大小 = jacocoUT.exec 和 jacocoIT.exec 的总和

    此时,我可以看到单元测试和集成测试的 Jacoco 代码覆盖率,即通过访问作业仪表板上的 Jacoco 代码覆盖率图像和访问源代码链接,如果你去浏览“build/reports/ jacoco/html/index.html”或“build/jacocoHtml/index.html”文件。

    仍在尝试找出 - SONAR 需要做什么才能选择这 2 个 .exec 文件(我为 sonar.xxx 变量设置了有效值,用于 UT / 源、测试、二进制文件、...reportsPath 等IT 执行文件...在 SonarQube 仪表板上,单元测试覆盖率显示良好,但集成测试覆盖率仍为 0.0%。

    我将很快粘贴我的 common.gradle 和项目的 build.gradle 文件的副本 .... 以便更好地查看。

    【讨论】:

      【解决方案2】:

      好的,在这篇文章中找到了 UT/IT 文件夹问题和我的问题 (1) 的解决方案。它实际上是“cleanTest”任务,它是 Gradle 中的默认规则之一。

      运行“gradle tasks -all”会给出 Gradle 支持的大量任务输出,最后的输出会告诉我们这样的规则,如果我们调用 clean,那么它将擦除这些文件夹。正如您在上面的代码中看到的,integrationTest 依赖于 cleanTest,因此当我调用“gradle clean build integrationTest”时,它首先运行单元测试(通过构建任务,因为单元测试在 Gradle 中的构建步骤默认运行),然后是集成测试因此,通过“integrationTest”任务,在运行集成测试时,它调用了 cleanTest 任务,它清除了我在通用 gradle 脚本(/init.d/commmon-some-name.gradle 文件)中提到的“UT”文件夹,例如我提到了报告/结果目录的 IT 文件夹。

      从 integrationTest 任务中删除 cleanTest 作为dependsOn,解决了清除问题。

      task integrationTest( type: Test, dependsOn: cleanTest ) {
      //task integrationTest( type: Test ) {
      



      以下命令的输出:仅显示最后几行...

      gradle 任务 -all

      integrationTest
          classes - Assembles binary 'main'.
          cleanTest
          compileIntegrationTestJava - Compiles source set 'integrationTest:java'.
          compileJava - Compiles source set 'main:java'.
          integrationTestClasses - Assembles binary 'integrationTest'.
          processIntegrationTestResources - Processes source set 'integrationTest:resources'.
          processResources - Processes source set 'main:resources'.
      jarService
      sonarRunner [test]
      
      Rules
      -----
      Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
      Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
      Pattern: clean<TaskName>: Cleans the output files of a task.
      
      BUILD SUCCESSFUL
      

      【讨论】:

      • 注意,即使我没有为单元测试和集成测试 (UT/IT) 文件夹设置单独的 reportsDir resultsDir 文件夹,并从我的 build.gradle 中的任务 integrationTest 中删除了 dependsON (cleanTest),当我运行“gradle clean build integrationTest jacocoTestReport”,我看到报告的默认文件夹是 build/reports,该文件夹内的 index.html 没有引用 .html 进行单元测试(即它仅引用最后一个的 .html命令行中调用的测试任务类型,它是 intergrationTest 任务)- 似乎是 Gradle 中的错误。
      • 发现:不要在 integrationTest 任务中使用“dependsOn cleanTest”。在 integrationTest 任务中使用 output.xxx {faslse} 为:outputs.upToDateWhen { false } --- 这将导致 integrationTest任务始终运行,而且由于我们没有调用dependsOn cleanTest,它不会在IT文件夹创建/集成测试任务运行期间删除单元测试的UT文件夹。如果您想在 IT 每次运行时为其创建新文件夹,请创建一个任务:task integTest { ...define those 2 testxxxDir values within integTest ... } 现在您可以将“dependsOn cleanIntegTest”用于 integrationTest 任务。
      猜你喜欢
      • 1970-01-01
      • 2017-10-01
      • 2016-04-07
      • 2014-01-21
      • 2012-04-14
      • 2017-03-06
      • 1970-01-01
      • 2018-10-18
      相关资源
      最近更新 更多