使用Jacoco

时间:2016-01-04 19:07:12

标签: unit-testing groovy sonarqube code-coverage jacoco-maven-plugin

我们正在使用带有Jacoco maven插件0.7.4的SonarQube 5.1,并且我们所有的slf4j日志记录语句(例如log.debug('Something happened'))都表明只覆盖了2个分支中的1个。我明白这是因为slf4j在内部做if debug,这很好,但我们不希望这会丢掉我们的数字。我们对测试slf4j不感兴趣,我们宁愿不为不同的日志记录级别多次运行每个测试。

那么,我们怎样才能告诉Sonar和/或Jacoco将这些线从覆盖范围中排除?它们都有可配置的文件排除,但我可以告诉它们只是从覆盖范围(使用目标目录)排除自己的类,而不是导入的库。我尝试将groovy.util.logging.*'添加到排除列表中,但它没有做任何事情。

logger.isDebugEnabled() is killing my code coverage. I'm planning to exclude it while running cobertura类似,并建议对于Cobertura,应使用'ignore'属性而不是'exclude'。在设置或文档中我没有看到Jacoco或Sonar的类似内容。

编辑: 运行Jacoco coverage后,Eclipse附带的示例图像(Sonar在GUI中显示相同的内容)。这是我们其中一个类的实际代码。 Jacoco branch coverage on slf4j logging

编辑2: 我们正在使用Slf4j注释。文件在这里: http://docs.groovy-lang.org/next/html/gapi/groovy/util/logging/Slf4j.html

  

此本地转换使用LogBack日志记录为您的程序添加日志记录功能。对名为log的未绑定变量的每个方法调用都将映射到对记录器的调用。为此,将在类中插入日志字段。如果该字段已存在,则此转换的使用将导致编译错误。方法名称将用于确定在记录器上调用的内容。

     

log.name(exp)

     

映射到

if (log.isNameLoggable() {
        log.name(exp)
     }
  

此处name是信息,调试,警告,错误等的占位符。如果表达式exp是常量或只是变量访问,则不会转换方法调用。但是这仍然会引起注入记录器的调用。

希望这可以澄清正在发生的事情。我们的日志语句成为2个分支ifs,以避免为未启用的日志级别构建昂贵的字符串构建(据我所知,这是一种常见的做法)。但这意味着为了保证覆盖所有这些分支,我们必须针对每个日志级别重复运行每个测试。

1 个答案:

答案 0 :(得分:0)

我没有找到排除它的通用解决方案,但如果您的代码库允许您这样做,您可以将日志语句包装在一个方法中,并在其名称中添加一个包含“Generated”的注释.

一个简单的例子:

package org.example.logging

import groovy.transform.Generated
import groovy.util.logging.Slf4j

@Slf4j
class Greeter {

    void greet(name) {
        logDebug("called greet for ${name}")
        println "Hello, ${name}!"
    }

    @Generated
    private logDebug(message) {
        log.debug message
    }
}

不幸的是 javax.annotation.Generated 不合适,因为它只保留了 SOURCE,因此我在这里(ab)使用了 groovy.transform.Generated,但可以轻松地为此目的创建自己的注释。

我在这里找到了解决方案:How would I add an annotation to exclude a method from a jacoco code coverage report?


更新:在 Groovy 中,您可以使用 trait 最优雅地解决它:

package org.example.logging

import groovy.transform.Generated
import groovy.util.logging.Slf4j

@Slf4j
trait LoggingTrait {

    @Generated
    void logDebug(String message) {
        log.debug message
    }
}

...然后...

package org.example.logging

import groovy.util.logging.Slf4j

@Slf4j
class Greeter implements LoggingTrait {

    void greet(name) {
        logDebug "called greet for ${name}"
        println "Hello, ${name}!"
    }

}

不幸的是,属性 log 被解释为 Greeter 的属性,而不是 LoggingTrait 的属性,因此您必须将 @Slf4j 附加到特征和实现特征的类。 尽管如此,这样做会为您提供预期的记录器 - 实现类之一:

14:25:09.932 [main] DEBUG org.example.logging.Greeter - called greet for world