如何在Jacoco报告中排除所有Jetpack Compose的Preview函数?
批量排除Jetpack Compose Preview函数的Jacoco覆盖率统计方案
方案1:通过Gradle Jacoco配置结合ASM过滤带@Preview注解的方法
这种方式无需修改业务代码,直接在模块构建脚本中添加过滤逻辑,自动识别并排除所有带有@Preview注解的函数。
Kotlin DSL 配置示例
tasks.withType<JacocoReport> { afterEvaluate { classDirectories.setFrom(files(classDirectories.files.map { classDir -> fileTree(classDir) { // 可选:先按文件名过滤减少ASM分析工作量 exclude("**/*Preview*.*") exclude("**/PreviewKt.class") // 核心:通过ASM扫描类文件,排除含@Preview注解的方法所属类 filter { file -> file.inputStream().use { inputStream -> val classReader = org.objectweb.asm.ClassReader(inputStream) var hasPreviewMethod = false val classVisitor = object : org.objectweb.asm.ClassVisitor(org.objectweb.asm.Opcodes.ASM9) { override fun visitMethod( access: Int, name: String?, desc: String?, signature: String?, exceptions: Array<out String>? ): org.objectweb.asm.MethodVisitor? { return object : org.objectweb.asm.MethodVisitor(org.objectweb.asm.Opcodes.ASM9) { override fun visitAnnotation(desc: String?, visible: Boolean) { if (desc == "Landroidx/compose/ui/tooling/preview/Preview;") { hasPreviewMethod = true } super.visitAnnotation(desc, visible) } } } } classReader.accept(classVisitor, org.objectweb.asm.ClassReader.SKIP_DEBUG or org.objectweb.asm.ClassReader.SKIP_FRAMES) !hasPreviewMethod } } } })) } }
Groovy 配置示例
tasks.withType(JacocoReport) { afterEvaluate { classDirectories.setFrom(files(classDirectories.files.collect { classDir -> fileTree(classDir) { exclude "**/*Preview*.*" exclude "**/PreviewKt.class" filter { file -> def inputStream = file.newInputStream() def classReader = new org.objectweb.asm.ClassReader(inputStream) boolean hasPreviewMethod = false def classVisitor = new org.objectweb.asm.ClassVisitor(org.objectweb.asm.Opcodes.ASM9) { @Override org.objectweb.asm.MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new org.objectweb.asm.MethodVisitor(org.objectweb.asm.Opcodes.ASM9) { @Override void visitAnnotation(String desc, boolean visible) { if (desc == "Landroidx/compose/ui/tooling/preview/Preview;") { hasPreviewMethod = true } super.visitAnnotation(desc, visible) } } } } classReader.accept(classVisitor, org.objectweb.asm.ClassReader.SKIP_DEBUG | org.objectweb.asm.ClassReader.SKIP_FRAMES) inputStream.close() return !hasPreviewMethod } } })) } }
方案2:基于命名约定的简单过滤
如果项目中所有Preview函数遵循统一命名规范(比如函数名/文件名含Preview),可以直接用文件过滤规则,这种方式更轻量高效:
Kotlin DSL 示例
tasks.withType<JacocoReport> { classDirectories.setFrom(files(classDirectories.files.map { fileTree(it) { exclude("**/*Preview*.class") exclude("**/PreviewKt.class") } })) }
Groovy 示例
tasks.withType(JacocoReport) { classDirectories.setFrom(files(classDirectories.files.collect { fileTree(it) { exclude "**/*Preview*.class" exclude "**/PreviewKt.class" } })) }
额外说明:Release变体无需配置
默认情况下,Compose编译器在Release构建变体中会自动移除所有带@Preview注解的函数,因此Release变体的Jacoco统计不会包含这些函数,无需添加排除规则,仅Debug变体需要配置上述方案。
内容的提问来源于stack exchange,提问作者Elye
相关产品推荐
相关产品推荐

