java - 使用 Powermock 时 Gradle Jacoco 不跟踪 Spock 测试的覆盖率

标签 java gradle powermock jacoco

我正在使用 Gradle Jacoco 插件来记录我们软件的测试覆盖率。该软件包含一些大量使用静态方法的遗留代码。为了模拟它们,我们提出了 Powermock 并将其集成到我们的 Spock 测试中。

到目前为止一切正常。 唯一的事情是Jacoco不跟踪使用Powermock的测试的测试覆盖率。普通 Spock 测试(不使用 Powermock 的测试)的测试覆盖率按预期报告。

有人知道如何让 Jacoco 记录我的 Powermock 测试的覆盖范围吗?

请在下面找到 Spock 测试。

@PrepareForTest([CodeCacheManager])
class SampleSpec extends Specification {

 @Rule
 PowerMockRule powerMockRule = new PowerMockRule();

 @Unroll
 void "Convert CodeIdentifier #insertvalue toString #returnvalue"() {

    given:
    def converter = new CodeIdentifierCodeInternalNameCustomConverter()

    and:
    mockStatic(CodeCacheManager.class)
    Mockito.when(CodeCacheManager.getInternalNameForCode(insertvalue)).thenReturn(returnvalue)

    when:
    String value = converter.convertTo(insertvalue, null)

    then:
    value == returnvalue

    where:
    insertvalue                      | returnvalue
    PartnerCodes.Geschlecht.mannlich | "männlich"
    null                             | "NO INTERNAL NAME"
 }
}

测试方法的实现看起来如下

public String convertTo (CodeIdentifier source, String destination)
{
  if (source != null)
  {
     return CodeCacheManager.getInternalNameForCode (source);
  }
  return "NO INTERNAL NAME";
}

enter image description here

最佳答案

Jacoco Offline Instrumentation 就是这个问题的解决方案。

查看我的 gradle 构建文件

Build and run tests:
Linux:
\$ ./gradlew
Windows:
\$ gradlew
------------------------------------------
"""

apply plugin: 'java'
apply plugin: 'org.sonarqube'
apply plugin: 'jacoco'

// Project group and version
group 'com.abcd.jacocoTest'
version '1.0.0'

// JDK version source compatibility
sourceCompatibility = 1.8

// JDK version target compatibility
targetCompatibility = 1.8

configurations {
    jacocoAnt
    jacocoRuntime
}

task wrapper(type: Wrapper) {
    gradleVersion = "4.5.1"
}

defaultTasks 'clean', 'test'

buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
    }

    dependencies {
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2"
    }
}

repositories {
    mavenCentral()
}

dependencies {

    jacocoAnt group: 'org.jacoco', name: 'org.jacoco.ant', version: '0.8.1'
    jacocoAgent group: 'org.jacoco', name: 'org.jacoco.agent', version: '0.8.1'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.8.9'
    testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.7.4'
    testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '1.7.4'
    testCompile group: 'org.powermock', name: 'powermock-api-easymock', version: '1.7.4'

}

test {
    testLogging {
        afterSuite { desc, result ->
            if (!desc.parent) { // will match the outermost suite
                println "Unit Tests: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
            }
        }
    }

    jacoco {
        append = "false"
        destinationFile = file("$buildDir/reports/jacoco/jacoco-sonar/jacoco-coverage.exec")
    }
}

jacoco {
    toolVersion = "0.8.0"
}

jacocoTestReport {
    reports {
        html.destination file("${buildDir}/reports/jacoco/jacocoHtml")
    }
}

sonarqube {
    properties {
        property "sonar.projectName", 'JacocoTest'
        property "sonar.host.url", "http://localhost:9000"
        property "sonar.java.binaries", "${buildDir}/classes"
        property "sonar.java.libraries", "**/*.jar"
        property "sonar.dynamicAnalysis", "reuseReports"
        property "sonar.jacoco.reportPaths", "${buildDir}/reports/jacoco/jacoco-sonar/jacoco-coverage.exec"
    }
}

task instrument(dependsOn: ['classes']) {
    ext.outputDir = buildDir.path + '/reports/classes-instrumented'
    doLast {
        ant.taskdef(name: 'instrument',
                classname: 'org.jacoco.ant.InstrumentTask',
                classpath: configurations.jacocoAnt.asPath)
        ant.instrument(destdir: outputDir) {
            fileset(dir: sourceSets.main.output.classesDir)
        }
    }
}
gradle.taskGraph.whenReady { graph ->
    if (graph.hasTask(instrument)) {
        tasks.withType(Test) {
            doFirst {
                classpath = files(instrument.outputDir) + classpath + configurations.jacocoRuntime
            }
        }
    }
}
task report(dependsOn: ['instrument', 'test']) {
    doLast {
        ant.taskdef(name: 'report',
                classname: 'org.jacoco.ant.ReportTask',
                classpath: configurations.jacocoAnt.asPath)
        ant.report() {
            executiondata {
                ant.file(file: buildDir.path + '/reports/jacoco/jacoco-sonar/jacoco-coverage.exec')
            }
            structure(name: 'Example') {
                classfiles {
                    fileset(dir: sourceSets.main.output.classesDir)
                }
                sourcefiles {
                    fileset(dir: 'src/main/java')
                }
            }
            html(destdir: buildDir.path + '/reports/jacoco')
        }
    }
}

此处报告任务将创建项目的离线检测文件。

最后,执行sonarqube任务。然后你可以看到 powermocked 类的覆盖范围也包含在内。

Execution command ./gradlew report sonarqube

然后去你的sonarqube主机(localhost:9000)中查看。

关于java - 使用 Powermock 时 Gradle Jacoco 不跟踪 Spock 测试的覆盖率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30325715/

相关文章:

gradle - Gradle中有类似有效的pom(Maven)吗?

java - 将桌面应用程序与应用程序服务器和数据库连接

java - mySite 正在 :8080 not in www. mysite.com 上工作,在 Linux 中使用 Apache Tomcat

java - 将 Spring tx 与任务混合

android - 如何在Android Studio终端中运行gradle命令?

java - 绘制在 Java GUI 中创建为实例的圆圈

kotlin - 如何在Gradle中更改Kotlin项目的源文件夹

java - 如何为手动依赖注入(inject)创建mockito测试

java - PowerMockito 在父类(super class)静态方法上给出 InvalidUseOfMatchersException