当前位置: 首页 > 知识库问答 >
问题:

bundle中的Jacoco类与事务性方法不匹配

薛弘厚
2023-03-14

我正在用JUnit5测试一个应用程序,并使用Jacoco进行报道。测试执行正常,并有测试报告。

但是,如果服务包含方法,则Jacoco report会有以下日志,并用@Transactional注释

[ant:jacocoReport] Classes in bundle 'my-service' do no match with execution data. For report generation the same class files must be used as at runtime.
[ant:jacocoReport] Execution data for class mypackage/SampleService does not match. 

所有@Service class方法都会发生此错误,并用@Transactional注释,普通类coverage is calculated ok。

下面是一个样本测试:

@SpringBootTest
@ExtendWith(SpringExtension.class)
public class MyServiceTest {

    @Autowired
    private SampleService sampleService;

    @Test
    public void doWork(){
        sampleService.doWork();
    }
}

很好。覆盖率为非零:

public class SampleService {

    public void doWork(){
        System.out.println("HEY");
    }
}

0%覆盖率:

public class SampleService {

    @Transactional
    public void doWork(){
        System.out.println("HEY");
    }
}

Transactional围绕actuall类创建一个代理。但是,难道雅科科没有现成的方法来处理这种常见的情况吗?

我已经尝试了@EnableAsheJAutoProxy与不同的标志变化,检查了最新的木星引擎和Jacoco插件使用

下面是gradle配置:

subprojects {
    test {       
        useJUnitPlatform()
    }

    jacocoTestReport {
        afterEvaluate {
            classDirectories.from = files(classDirectories.files.collect {
                fileTree(dir: it, exclude: '*Test.java')
            })
        }

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

感谢任何帮助

共有2个答案

谷梁子濯
2023-03-14

基于单模块检测示例https://stackoverflow.com/a/31916686/7096763(以及@MikaelF更新的gradle 5版本),下面是多模块检测示例:

subprojects {  subproject ->
    subproject.ext.jacocoOfflineSourceSets = [ 'main' ]
    task doJacocoOfflineInstrumentation(dependsOn: [ classes, subproject.configurations.jacocoAnt ]) {
        inputs.files classes.outputs.files
        File outputDir = new File(subproject.buildDir, 'instrumentedClasses')
        outputs.dir outputDir
        doFirst {
            project.delete(outputDir)
            ant.taskdef(
                    resource: 'org/jacoco/ant/antlib.xml',
                    classpath: subproject.configurations.jacocoAnt.asPath,
                    uri: 'jacoco'
            )
            def instrumented = false
            jacocoOfflineSourceSets.each { sourceSetName ->
                if (file(sourceSets[sourceSetName].output.classesDirs[0]).exists()) {
                    def instrumentedClassedDir = "${outputDir}/${sourceSetName}"
                    ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
                        fileset(dir: sourceSets[sourceSetName].output.classesDirs[0], includes: '**/*.class')
                    }
                    //Replace the classes dir in the test classpath with the instrumented one
                    sourceSets.test.runtimeClasspath -= files(sourceSets[sourceSetName].output.classesDirs[0])
                    sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
                    instrumented = true
                }
            }
            if (instrumented) {
                //Disable class verification based on https://github.com/jayway/powermock/issues/375
                test.jvmArgs += '-noverify'
            }
        }
    }
    test.dependsOn doJacocoOfflineInstrumentation
}

完整示例如下:https://github.com/lizardeye/jacocomultimodulesample

不过,我认为这是一个durty黑客攻击,可以很容易地用gradle或jacoco更新来破解

费学
2023-03-14

我尝试了一个测试项目,类似于你所描述的,但是我不能重现这个问题。我看到你的项目和我的项目之间唯一的区别是,我使用了maven而不是gradle。

以下是测试项目:https://github.com/gybandi/jacocotest

如果这对你没有帮助,你能把你的测试项目上传到github或其他地方吗?

编辑:@MikaelF发布了另一个答案的链接,显示了如何为jacoco添加离线检测。

在我添加了下面的块来构建之后,这里描述的解决方案对我来说很有效。格雷德尔:

task instrument(dependsOn: [classes, project.configurations.jacocoAnt]) {

    inputs.files classes.outputs.files
    File outputDir = new File(project.buildDir, 'instrumentedClasses')
    outputs.dir outputDir
    doFirst {
        project.delete(outputDir)
        ant.taskdef(
                resource: 'org/jacoco/ant/antlib.xml',
                classpath: project.configurations.jacocoAnt.asPath,
                uri: 'jacoco'
        )
        def instrumented = false
            if (file(sourceSets.main.java.outputDir).exists()) {
                def instrumentedClassedDir = "${outputDir}/${sourceSets.main.java}"
                ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
                    filehtml" target="_blank">set(dir: sourceSets.main.java.outputDir, includes: '**/*.class')
                }
                //Replace the classes dir in the test classpath with the instrumented one
                sourceSets.test.runtimeClasspath -= files(sourceSets.main.java.outputDir)
                sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
                instrumented = true
            }
        if (instrumented) {
            //Disable class verification based on https://github.com/jayway/powermock/issues/375
            test.jvmArgs += '-noverify'
        }
    }
}
test.dependsOn instrument

jacoco插件的github上似乎也有关于这一点的公开信息:https://github.com/gradle/gradle/issues/2429

 类似资料:
  • 我正在使用Jacoco使用ANT查找单元测试的代码覆盖率,但报告没有生成,我得到了这个错误序列: 我已经阅读了这些答案,但似乎没有一个可以帮助我解决问题。 Jacoco代码覆盖报告生成器显示错误:“捆绑包‘代码覆盖报告’中的类与执行数据不匹配” jacoco:对于报告生成,必须使用与运行时相同的类文件 我在Eclipse上编译了所有类,并使用ANT构建工具对这些类执行代码覆盖。由于一些依赖关系,我

  • 我在应用程序中工作。流程和往常一样:。 我用注释了服务层类,从而将该类中的每个方法都标记为事务性的。在服务类中,我调用以获取某个域对象,然后将其转换为对象,该对象将传递给Controller。为了将域对象转换为,我编写了另一个自定义静态类(只有静态方法的类),如,它将进行此转换。 现在,域对象有一些子对象(),它被懒洋洋地加载。因此,当我在中访问子getter方法时,会发出一个额外的数据库调用,该

  • 试图收集和理解@事务性注释的要点并越过了一点。因此,在使用事务性注释时,我们需要记住的主要事情是: 事务注释只能应用于公共方法[根据Spring@Transactional属性对私有方法起作用吗? 事务性注释应该应用于具体的类,而不是接口[根据我应该将@transactional annotation放在接口定义还是实现类的位置? 事务注释应应用于服务级别[根据Spring@Transaction

  • 当运行此操作时,可以看到批处理仍被打印为。我在设置交互时做错了什么?

  • 问题内容: 我有一个用@Transactional注释的方法。我从Oracle数据库检索一个对象,更改一个字段,然后从该方法返回。我忘了保存对象,但是发现无论如何都会更新数据库。 applicationContext 我的方法 我的问题是为什么MyObject会持久保存到数据库? 问题答案: 因为hibernate将自动检测对持久实体所做的更改并相应地更新数据库。hibernate参考手册的第11