Gradle插件单元测试不能直接用./gradlew test运行,必须使用GradleRunner启动真实构建进程进行集成测试,因插件逻辑依赖Gradle运行时环境,mock Project/Task等对象不可靠且易误判。

Gradle 插件的单元测试不能靠 ./gradlew test 直接跑通——它默认只测项目源码里的 Java/Kotlin 类,而插件逻辑(尤其是 Plugin<t></t> 实现、Extension、Task 注册)需要模拟 Gradle 运行时环境。不 mock Gradle API,测试会立即抛 NoClassDefFoundError 或空指针。
用 GradleRunner 启动真实 Gradle 实例做集成测试
Gradle 官方推荐且最可靠的方式是用 GradleRunner(来自 org.gradle.testkit:gradle-test-kit),它会在临时目录下启动一个轻量级、隔离的 Gradle 构建进程,加载你的插件并执行配置/任务。
- 必须在
src/test/kotlin/(或src/test/java/)中写测试类,不能放在buildSrc或主工程里 - 测试类需用
@Test标记,且依赖gradleTestKit()(Kotlin DSL)或testImplementation 'org.gradle.testkit:gradle-test-kit'(Groovy) - 每个测试需构造一个最小
settings.gradle.kts和build.gradle.kts,显式plugins { id "your.plugin.id" } -
GradleRunner.create()需指定withProjectDir(指向临时构建目录)、withArguments("tasks")、withPluginClasspath()(把当前插件编译产物加入 classpath) - 断言不是检查返回值,而是检查
buildResult.output是否含预期日志,或projectDir.resolve("build/...")下文件是否存在
MockK / Mockito 只适合测纯逻辑,别碰 Project 或 Task 实例
你可以用 MockK 测试 FlutterPluginUtils.kt 里独立的工具函数(比如 resolveFlutterEngineAar()),但千万别 mock Project、Task 或 Extension 对象本身——它们重度依赖 Gradle 内部状态,mock 后行为不可控,容易误判。
- 适合 mock 的:工具函数、校验逻辑、字符串处理、版本解析等无副作用代码
- 不适合 mock 的:
project.tasks.register()、extension.property = value、task.doFirst {}——这些必须走GradleRunner - 如果硬要 mock
Project,至少用ProjectBuilder.builder().build()(Gradle 自带),但它不支持 AGP 或 Android 特有 DSL,对 Flutter Gradle Plugin 这类依赖AndroidComponentsExtension的插件基本无效
Flutter Gradle Plugin 的测试必须声明式 apply,否则直接失败
你看到的 flutter.gradle 文件里那句 throw new GradleException("Use plugins { id '...'} instead") 不是摆设。任何用旧式 apply from: "flutter.gradle" 方式加载插件的测试,都会在 GradleRunner 启动阶段崩溃。
- 测试用的
build.gradle.kts必须用plugins { id("dev.flutter.flutter-gradle-plugin") version "1.0.0" }(版本号需与你本地插件发布版本一致) - 不能在测试中调用
project.apply(from: "..."),哪怕路径正确也不行 - 若插件尚未发布到 Maven 仓库,需先
./gradlew publishToMavenLocal,再在测试里用version "1.0.0"+repositories { mavenLocal() } - FlutterPlugin.kt 中的
apply()方法被调用时,会校验是否通过plugins块加载,否则 throw
真正难的不是写第一个测试,而是让测试能稳定复现插件在真实构建中的行为——比如 flutter_assets 拷贝时机、libapp.so 编译触发条件、ABI 过滤逻辑。这些都藏在 FlutterPlugin 的 afterEvaluate 或 AndroidComponentsExtension.onVariants 回调里,必须靠 GradleRunner 触发完整生命周期才能验证。跳过这步,测试就只是个“能编译通过”的装饰品。


















