Gradle插件功能测试必须用GradleTestKit而非普通JUnit,因其需在真实构建上下文中验证Project实例、Task图、Extension注册及类路径加载等生命周期行为,手动调用apply()无法模拟插件应用全过程。

Gradle插件的功能测试不能靠手动跑 ./gradlew build 验证,必须用 GradleTestKit 模拟真实构建环境——否则你写的插件在别人项目里大概率会报 Could not find method xxx() on extension 'myExt' 或 Task not found。
为什么不用普通JUnit断言,而必须用TestKit
普通单元测试只能验证类方法是否抛异常、返回值对不对;但插件行为发生在 Gradle 构建生命周期中,依赖 Project 实例、Task 图、Extension 注册、甚至 buildSrc 类路径加载顺序。这些只有在真实(或模拟真实)的 Gradle 运行上下文中才能触发。
TestKit 本质是启动一个隔离的、最小化的 Gradle 进程,加载你的插件 + 一段模拟的 build.gradle,再运行指定 task,最后检查输出、文件生成、task 状态等结果。
常见错误现象:
- 本地
buildSrc下能跑通,但发布成 jar 后别人引入就ClassNotFoundException - 插件里用了
project.afterEvaluate { ... },但测试没触发,导致 task 没注册 -
extensions.create()后,在测试里读不到配置项,因为没写 DSL 块
TestKit 测试结构怎么组织
测试类必须放在 src/test/java 或 src/test/kotlin,且要声明 @Rule(JUnit 4)或使用 @RegisterExtension(JUnit 5),配合 TemporaryFolder 创建干净的临时构建目录。
关键点:
- 测试代码里不能直接 new Plugin 实例调用
apply()——那只是“调用方法”,不是“应用插件” - 必须用
GradleRunner启动一次构建,就像终端里敲./gradlew myTask - 模拟的
build.gradle必须显式apply plugin: 'com.example.my'或用plugins { id 'com.example.my' } - 如果插件有
Extension,测试的build.gradle里必须写对应 DSL 块,否则配置不生效
示例片段(JUnit 5):
@RegisterExtension
static GradleTestKitExtension testKit = new GradleTestKitExtension();
@Test
void "applies plugin and registers task"() {
def projectDir = testKit.testProjectDir
new File(projectDir, "build.gradle").text = """
plugins { id 'com.example.my' }
myExt { outputDir = 'generated' }
"""
def result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments("myTask", "--stacktrace")
.withPluginClasspath() // 关键:把当前项目编译产物加进 classpath
.build()
assert result.output.contains("Generated files in generated/")
}
容易踩的坑:classPath 和插件 ID 不匹配
这是最常导致 Plugin with id 'xxx' not found 的原因:
-
withPluginClasspath()只包含当前模块build/classes和依赖,**不自动包含META-INF/gradle-plugins/xxx.properties** - 如果你的插件 ID 是
com.example.my,必须确保src/main/resources/META-INF/gradle-plugins/com.example.my.properties存在且内容为implementation-class=com.example.MyPlugin - 用 Kotlin DSL 写插件时,
implementation-class要写成com.example.MyPluginKt(Kotlin 文件顶层函数会编译成 *Kt 类) - 如果插件工程用了
java-gradle-plugin插件,它会自动处理 properties 文件;但纯java-library+ 手动配置就不会
测试慢?关掉不必要的 Gradle 特性
TestKit 默认启用所有 Gradle 功能,但多数插件测试不需要构建缓存、配置缓存或 Daemon。加上这些反而让测试变慢、失败更难排查:
- 在
GradleRunner构建前加.forwardOutput()方便看日志 - 避免用
--configuration-cache,除非你明确在测兼容性——否则大概率因Configuration cache may not be supported失败 - 不要在测试里调用
gradle --stop或复用 Daemon,每个测试必须用干净进程 - 如果测试涉及 Android 插件,务必先检查
android是否已 apply,否则project.extensions.findByType(AppExtension)返回 null
真正难的不是写第一个 TestKit 测试,而是让测试覆盖到插件和用户项目的交互边界:比如当用户同时 apply 了 kotlin 和 java 插件时,你的 task 依赖是否仍正确;或者当 myExt 配置被多次调用时,会不会覆盖默认值。这些边界不靠 TestKit 模拟,根本暴露不出来。


















