用Jacoco从Sonar的条件覆盖范围中排除groovy slf4j日志记录

Ego*_*gor 7 groovy unit-testing code-coverage sonarqube jacoco-maven-plugin

我们将SonarQube 5.1与Jacoco maven插件0.7.4一起使用,并且所有slf4j日志记录语句(例如,log.debug('Something happened')表明仅覆盖2个分支中的1个)。我知道这是因为slf4j在内部执行if debug,这很好,但是我们不希望这会影响我们的数字。我们对测试slf4j并不感兴趣,我们宁愿不要针对不同的日志记录级别多次运行每个测试。

那么,我们如何告诉Sonar和/或Jacoco将这些线路排除在覆盖范围之外?它们都具有可配置的文件排除项,但是据我所知,这些排除项仅是用于从覆盖范围中排除自己的类(使用目标目录),而不是导入的库。groovy.util.logging.*'无论如何,我都尝试将其添加到排除列表中,但是它什么也没做。

logger.isDebugEnabled()杀死了我的代码覆盖范围。我打算在运行cobertura时将其排除在外,并且建议对于Cobertura,应使用'ignore'属性而不是'exclude'。我在设置或文档中没有看到类似Jacoco或Sonar的东西。

编辑:运行Jacoco覆盖后,附带的Eclipse示例图像(Sonar在其GUI中显示相同的内容)。这是我们其中一个类的实际代码。 Jacoco分支机构对slf4j日志记录的覆盖

编辑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)
     }
Run Code Online (Sandbox Code Playgroud)

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

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

nin*_*our 1

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

一个简单的例子:

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
    }
}

Run Code Online (Sandbox Code Playgroud)

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

我在这里找到了该解决方案:How will I add annotation to except a method from a jacoco codecoverage reports?


更新:在 Groovy 中,您可以通过以下特征最优雅地解决它:

package org.example.logging

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

@Slf4j
trait LoggingTrait {

    @Generated
    void logDebug(String message) {
        log.debug message
    }
}
Run Code Online (Sandbox Code Playgroud)

...进而...

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}!"
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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