@Test
fun 测试我的注解处理器
() {
val kotlinSource = SourceFile.kotlin(
"KClass.kt", """
class KClass {
fun foo() {
// 测试环境中的类对编译的源文件是可见的
val testEnvClass = TestEnvClass()
}
}
"""
)
val javaSource = SourceFile.java( "JClass.java", """ public class JClass { public void bar() { // 编译的Kotlin类对Java源文件是可见的 KClass kClass = new KClass(); } } """)
配置编译
```Kotlin
val result = KotlinCompilation().apply {
sources = listOf(kotlinSource, javaSource)
// 传入你自己的注解处理器实例
annotationProcessors = listOf(MyAnnotationProcessor())
// 传入你自己的编译器插件实例
compilerPlugins = listOf(MyComponentRegistrar())
commandLineProcessors = listOf(MyCommandlineProcessor())
inheritClassPath = true
messageOutputStream = System.out // 实时查看诊断信息
}.compile()
断言结果
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
// 测试编译器的诊断输出
assertThat(result.messages).contains("我的注解处理器被调用了")
// 加载编译后的类并通过反射检查生成的代码
val kClazz = result.classLoader.loadClass("KClass")
assertThat(kClazz).hasDeclaredMethods("foo")
}
特性
- 混合源代码集:在单次运行中编译Kotlin和Java源文件
- 注解处理:
- 对Kotlin和Java源代码运行注解处理器
- 生成Kotlin和Java源代码
- Kotlin和Java源代码都可以访问生成的源代码
- 直接向编译器提供自定义的注解处理器实例,而不是让编译器使用服务定位器创建它们
- 调试注解处理器:由于编译在与应用程序相同的进程中运行,您可以轻松调试它,而不必手动将IDE的调试器附加到编译过程
- 继承类路径:编译后的源代码可以访问应用程序中的类
- 兼容Project Jigsaw:Kotlin-Compile-Testing可以与JDK 8以及JDK 9及更高版本一起使用
- JDK跨版本编译:提供自己的JDK来编译代码,而不是使用主机应用程序的JDK。这使您可以轻松测试所有JDK版本上的代码
- 在主机类路径上自动查找依赖项
安装
该软件包可在 Maven Central 上获取。
在模块的 build.gradle
文件中添加依赖项:
dependencies {
// ...
testImplementation("com.github.tschuchortdev:kotlin-compile-testing:1.5.0")
}
如果觉得有用,请记得点个星
兼容的编译器版本
Kotlin-Compile-Testing 与所有本地编译器版本兼容。您用于编译项目的编译器版本并不重要。
然而,如果您的项目或其任何依赖项直接依赖于编译器构件,如 kotlin-compiler-embeddable
或 kotlin-annotation-processing-embeddable
,那么它们的版本必须与 Kotlin-Compile-Testing 使用的版本相同,否则将出现传递依赖冲突。
- 当前
kotlin-compiler-embeddable
版本:1.9.24
由于 Kotlin 编译器的内部 API 在不同版本之间经常发生变化,我们一次只能支持一个kotlin-compiler-embeddable
版本。
Kotlin 符号处理 API 支持
Kotlin 符号处理(KSP)是一种新的注解处理管道,它构建在 Kotlin 编译器的插件架构之上,而不是像 kapt
那样委托给 javac。
要测试 KSP 处理器,您需要使用 KSP 依赖:
dependencies {
testImplementation("com.github.tschuchortdev:kotlin-compile-testing-ksp:1.6.0")
}
这个模块为 KotlinCompilation
添加了一个新函数,用于指定 KSP 处理器:
class MySymbolProcessorProvider : SymbolProcessorProvider {
// KSP API 中 SymbolProcessorProvider 的实现
}
val compilation = KotlinCompilation().apply {
sources = listOf(source)
symbolProcessorProviders = listOf(MySymbolProcessorProvider())
}
val result = compilation.compile()
KSP 处理器生成的所有代码都将写入 KotlinCompilation.kspSourcesDir
目录。
使用Kotlin-Compile-Testing的项目
- androidx/room
- google/dagger
- square/moshi
- uber/motif
- arrow-kt/arrow-meta
- foso/mpapt
- kotlintest/kotlintest
- bnorm/kotlin-power-assert
- JakeWharton/confundus
- kotest/kotest
- ZacSweers/aak
- apollographql/apollo-kotlin
- patxibocos/poetimizely
- AhmedMourad0/no-copy
- ansman/auto-plugin
- livefront/sealed-enum
- him188/kotlin-jvm-blocking-bridge
- Strum355/lsif-kotlin
- mars885/hilt-binder
- Guardsquare/proguard-core
- Guardsquare/proguard
- komapper/komapper
- SimonMarquis/SealedObjectInstances
- ansman/kotshi
- mcarleio/konvert
- 你的项目...
Java 16 兼容性
随着Java 16的发布,新的Jigsaw模块系统的访问控制开始由JVM强制执行。不幸的是,这对kotlin-compile-testing造成了影响,因为KAPT仍然试图访问jdk.compiler模块未导出的javac类,导致出现如下错误:
java.lang.IllegalAccessError: class org.jetbrains.kotlin.kapt3.base.KaptContext (in unnamed module @0x43b6aa9d) cannot access class com.sun.tools.javac.util.Context (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.util to unnamed module @0x43b6aa9d
为了缓解这个问题,你需要在模块的build.gradle
文件中添加以下代码:
if (JavaVersion.current() >= JavaVersion.VERSION_16) {
test {
jvmArgs(
"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
)
}
}
或者对于Kotlin DSL:
if (JavaVersion.current() >= JavaVersion.VERSION_16) {
tasks.withType<Test>().all {
jvmArgs(
"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
)
}
}
由于Kotlin编译测试在与测试运行器相同的进程中运行,这些选项必须手动添加,kotlin-compile-testing库无法自动设置。
许可证
版权所有 (C) 2023 Thilo Schuchort 根据Mozilla公共许可证2.0授权 如需定制许可协议,请直接与我联系