web-dev-qa-db-ja.com

バンドルのJacocoクラスがトランザクションメソッドと一致しない

JUnit5でアプリをテストし、カバレッジレポートにJacocoを使用しています。テストが実行されますOkおよびテストレポートが存在します。

ただし、サービスに@Transactionalアノテーションが付けられたメソッドが含まれている場合、Jacocoレポートには次のログがあります

[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. 

このエラーは、@ Transactionalアノテーションが付けられたすべての@Serviceクラスメソッドで発生します。プレーンクラスのカバレッジは正常に計算されます。

ここにサンプルテストがあります:

@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クラスの周りにプロキシを作成します。しかし、Jacocoがこのような一般的な状況を処理するための独創的な方法はありませんか?

さまざまなフラグバリエーションで@EnableAspectJAutoProxyアノテーションを試して、最新のJupiterエンジンとJacocoプラグインが使用されていることを確認しました

これがgradle configです:

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

助けてくれてありがとう

5
Ermintar

シングルモジュールインストルメンテーションの例 https://stackoverflow.com/a/31916686/709676 (および@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

それでも、これは耐久性のあるハックだと思います。これは、gradleまたはjacocoの更新で簡単に壊れる可能性があります

1
Ermintar