跳转至

测试体系、Fixture 与 CI 门禁

IntelliJ Platform 插件测试不是普通纯单元测试。官方建议以模型级功能测试为主:在 headless 环境中使用真实平台组件,加载测试文件,执行功能,再比较输出。这样测试更慢,但稳定性和真实度远高于大量 mock。

测试分层

层级 适合测试
纯单元测试 与平台无关的解析、算法、DTO、协议
Light tests PSI、补全、检查、Quick Fix、Action 上下文
Heavy tests 多模块项目、真实项目结构、模块/库/Facet 变更
Integration/UI tests 真实 UI 流程、Split Mode UI、端到端插件行为
Plugin Verifier 二进制兼容、API 可用性、目标 IDE 兼容

默认优先写 light tests。只有确实需要多模块、真实项目重建或更完整环境时才写 heavy tests。

测试依赖

Gradle 2.x 需要显式声明测试框架:

dependencies {
    intellijPlatform {
        testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform)
    }
}

测试 Java PSI 或 Java 相关功能时,还需要 Java 插件依赖和对应测试框架能力:

dependencies {
    intellijPlatform {
        bundledPlugin("com.intellij.java")
        testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform)
    }
}

外部系统集成测试可使用:

dependencies {
    testImplementation("com.jetbrains.intellij.platform:external-system-test-framework")
}

Base Class 与 Fixture

两种写法:

  • 继承标准 base class,例如 BasePlatformTestCase
  • 手动用 IdeaTestFixtureFactory 创建 fixture,不绑定某个测试框架。

常用基类:

基类 场景
BasePlatformTestCase 不依赖 Java 的 light test
LightPlatformTestCase 平台级 light test
LightJavaCodeInsightFixtureTestCase JUnit 3 Java PSI / code insight
LightJavaCodeInsightFixtureTestCase4 JUnit 4 Java PSI / code insight
LightJavaCodeInsightFixtureTestCase5 JUnit 5 Java PSI / code insight
HeavyPlatformTestCase heavy test

Light tests 会尽量复用项目实例。Heavy tests 每个测试创建新项目,成本更高。

LightProjectDescriptor

Light test 需要 SDK、库、Facet、模块类型时,用 LightProjectDescriptor

override fun getProjectDescriptor(): LightProjectDescriptor {
    return JAVA_17
}

如果相邻测试返回同一个 descriptor,项目可以复用;descriptor 不同则重建项目。descriptor 的 equals() 会影响复用行为。

testdata 目录

推荐结构:

src/test/kotlin/com/example/
src/test/testData/
  inspections/
  completion/
  formatter/
  rename/

第三方插件必须覆写 getTestDataPath()

override fun getTestDataPath(): String {
    return "src/test/testData"
}

常用 fixture 方法:

myFixture.configureByFile("completion/basic.example")
myFixture.configureByFiles("rename/before.example", "rename/other.example")
myFixture.copyFileToProject("project/config.example", "config.example")
myFixture.copyDirectoryToProject("project", ".")

testdata 文件不应放在 source root 下,因为它们往往不是可编译源码。

特殊标记

编辑器测试支持特殊标记:

标记 含义
<caret> 光标位置
<selection> / </selection> 选择区
<block> / </block> 列选择区

示例:

foo.<caret>bar

Highlighting 测试

检查、Annotator、parser error highlighting 都可以用:

myFixture.enableInspections(ExampleInspection())
myFixture.configureByFile("highlighting/warning.example")
myFixture.checkHighlighting(true, true, true)

expected markup:

<warning descr="Unknown property">missing.key</warning>

支持 severity:

  • <error>
  • <warning>
  • <weak_warning>
  • <info>
  • <inject>
  • custom severity name

SyntaxHighlighter 的 lexer 级高亮可用 EditorTestUtil.testFileSyntaxHighlighting()。推荐先让测试生成 answer file,再人工审查,避免手写大量 token attribute 输出。

Completion、Quick Fix、Formatter 测试

Completion:

myFixture.configureByFile("completion/basic.example")
myFixture.completeBasic()
assertContainsElements(myFixture.lookupElementStrings!!, "expectedName")

Quick Fix:

myFixture.enableInspections(ExampleInspection())
myFixture.configureByFile("quickfix/before.example")
val action = myFixture.findSingleIntention("Create property")
myFixture.launchAction(action)
myFixture.checkResultByFile("quickfix/after.example")

Formatter:

myFixture.configureByFile("formatter/before.example")
CodeStyleManager.getInstance(project).reformat(myFixture.file)
myFixture.checkResultByFile("formatter/after.example")

Mock 策略

官方不推荐大规模 mock 平台组件。插件代码通常依赖真实 PSI、VFS、Project Model、Service、Extension Point 和索引;mock 这些对象很容易测到一个与真实 IDE 不一致的系统。

适合 mock 的对象:

  • 外部 HTTP 客户端。
  • 自己定义的纯业务接口。
  • 调试协议或 LSP/MCP 后端。
  • 时间、随机数、文件下载等边界。

不建议 mock:

  • Project
  • PsiElement
  • VirtualFile
  • Document
  • DumbService
  • ExtensionPointName

CI 门禁

PR 门禁:

./gradlew test
./gradlew buildPlugin
./gradlew verifyPlugin

Nightly 或 release candidate:

./gradlew runPluginVerifier

Release:

./gradlew signPlugin
./gradlew publishPlugin

多产品插件建议构建 verifier matrix:

维度 示例
IDE 产品 IC、IU、PY、WS、GO、DB
版本 since-build、最新稳定、EAP
可选依赖 有/无 JavaScript、Database、Kotlin
运行模式 单体 IDE、Split Mode

失败排查

现象 优先检查
测试偶发找不到文件 in-memory FS 缓存不一致,清理 sandbox system
checkHighlighting() 多出高亮 是否开启了额外 inspection;ignoreExtraHighlighting 是否应为 true
Completion 为空 光标标记、文件类型、Dumb Mode、依赖插件
Java PSI 测试失败 Mock JDK / Java plugin / project descriptor
Heavy test 很慢 是否能降级为 light test
CI 通过本地失败 Gradle JVM、IDE sandbox、环境变量差异

参考来源