Repository navigation
Guide Testing
本页回答:Coco Framework 仓库里有哪些测试、怎么命名、怎么运行,以及新增或修改 Feature 时需要同步改哪些测试。
内容对应仓库
main分支;以源码为准,发现出入请提 Issue 或 PR。命令取自AGENTS.md、CONTRIBUTING.md、release.md与.github/workflows/*.yml;执行前请先确认它们适用于你的环境和改动面。
| 项 | 现状 | 来源 |
|---|---|---|
| 单元测试框架 | JUnit Jupiter + AssertJ | 各模块 pom.xml 的 test 依赖 |
| Spring 相关测试 |
spring-boot-test / spring-boot-starter-test;自动配置测试常用 ApplicationContextRunner
|
例:coco-features/coco-lock/src/test/java/io/github/coco/feature/lock/CocoLockAutoConfigurationTest.java
|
| 日志实现 |
logback-classic(test 依赖) |
各 Feature 模块 pom.xml
|
| 覆盖率 / 容器化测试 | 全仓库 pom.xml 中未发现 JaCoCo、Testcontainers 的配置 |
对 git ls-files '*pom.xml' 检索 jacoco/testcontainers/mockito 无匹配 |
| 单元测试规模 |
src/test 下 *Test.java 共 114 个(量级参考,随提交变化) |
以 git ls-files 的已跟踪文件统计 |
| 集成测试规模 | 全仓库仅 2 个 *IT(见第 3 节) |
CocoStarterSmokeIT、FeatureArchiveSmokeIT
|
框架不维护业务样例应用。业务 / HTTP 验收由独立仓库 coco-admin 的 framework-acceptance 负责(AGENTS.md 与 coco-support/coco-document/release.md 均有说明)。因此框架内“能跑起来”的只有上述两个 IT 夹具,没有可独立运行的 demo。
| 类型 | 约定 | 示例 |
|---|---|---|
| 单元测试 | 类名以 Test 结尾,放在模块自己的 src/test/java,包名与被测类一致 |
coco-foundation/coco-feature-model/src/test/java/io/github/coco/feature/model/StandardCocoFeaturesTest.java |
| 集成测试 | 类名以 IT 结尾,由 Failsafe 在 integration-test 阶段执行 |
coco-spring/coco-spring-boot-starter/src/test/java/io/github/coco/spring/boot/CocoStarterSmokeIT.java |
| 自动配置测试 | Coco<X>AutoConfigurationTest |
CocoLockAutoConfigurationTest |
| 默认实现测试 | Default<X>Test |
DefaultCocoLockManagerTest |
| 存储实现测试 |
InMemory<X>StoreTest、Redis<X>StoreTest
|
InMemoryCocoLockStoreTest、RedisCocoLockStoreTest
|
| 兼容门面探针 |
<Facade>CompatibilityCompileProbe,位于门面模块 src/test
|
coco-build/coco-compatibility/coco-config/src/test/java/io/github/coco/compatibility/config/CocoConfigCompatibilityCompileProbe.java |
说明:coco-lock 的 src/test/java/io/github/coco/feature/lock/ 下正好是上表前四类的一套样板(4 个测试类),新增同类模块时可以对照。自动配置测试统一使用 ApplicationContextRunner:仓库里全部 27 个 *AutoConfigurationTest 都使用它(或其 Servlet 变体 WebApplicationContextRunner),共有 31 个测试类引用了 ApplicationContextRunner。
根 pom.xml 对两个插件的配置很简单:
| 插件 | 版本属性 | 配置 |
|---|---|---|
maven-surefire-plugin |
3.6.0 |
useModulePath=false |
maven-failsafe-plugin |
3.6.0 |
useModulePath=false、failIfNoTests=false;在根 pom.xml 的 build/plugins 中绑定 execution integration-tests(继承根 pom 的模块都会带上),目标为 integration-test 与 verify
|
由此得到的行为:
-
mvn test只跑 Surefire(*Test),不会执行*IT。 -
mvn verify/mvn install会走到integration-test与verify阶段,Failsafe 才执行*IT。CI 跑的是mvn -B -ntp install。 - 因为
failIfNoTests=false,没有 IT 的模块不会因此失败。
| 测试 | 做什么 |
|---|---|
coco-spring/coco-spring-boot-starter/src/test/java/io/github/coco/spring/boot/CocoStarterSmokeIT.java |
方法 startsServletApplicationWithStarterDefaults:以 starter 默认值启动一个 Servlet 应用,断言 CocoMessageService、CocoWebRequestContextResolver 两个 Bean 存在 |
coco-support/coco-feature-archive-smoke/src/test/java/io/github/coco/fixture/archive/FeatureArchiveSmokeIT.java |
方法 verifiesCurrentReactorArchiveManifestAndPrunedLibraries:读取系统属性 coco.feature.archive.path(由该模块 pom.xml 的 Failsafe 配置设为 ${project.build.directory}/${project.build.finalName}.jar),校验 Boot 归档里的 Feature 清单与被裁剪的库 |
coco-feature-archive-smoke 的父 POM 是 coco-parent,所以它会真实执行 coco:features 与 coco:prune-package;夹具应用类是 coco-support/coco-feature-archive-smoke/src/main/java/io/github/coco/fixture/archive/FeatureArchiveSmokeApplication.java。它的 maven.deploy.skip / maven.install.skip 为 true,不会发布。release.md 说明该冒烟测试在 mvn -B install 的 Failsafe 阶段执行。
| 测试 | 位置 | 守护什么 |
|---|---|---|
StandardCocoFeaturesTest |
coco-foundation/coco-feature-model/src/test/java/io/github/coco/feature/model/ |
逐 Feature 断言 id、artifactId、自动配置 FQCN、Feature 依赖。新增 / 改名 Feature 必须同步更新 |
CocoRateLimitStarterDependencyContractTest |
coco-spring/coco-spring-boot-starter/src/test/java/io/github/coco/spring/boot/ |
starter 的直接依赖里不得出现 coco-rate-limit 与 coco-idempotency;二者由 coco:features 在构建期按 Feature 计划装配 |
CocoMybatisPlusStarterDependencyContractTest |
同上目录 | starter 与 MyBatis-Plus 相关的依赖契约(具体断言以源码为准) |
CocoSpringCompositionClasspathTest |
同上目录 | starter 与兼容门面同在 classpath 时,已迁移的自动配置与 spring.factories 扩展点只注册一次 |
*CompatibilityCompileProbe(9 个) |
coco-build/coco-compatibility/*/src/test/... |
以编译期引用的方式钉住旧坐标曾对外暴露的公开类型(例:CocoConfigCompatibilityCompileProbe 引用 io.github.coco.config.CocoProperties 等);这些类型被移动、改名或降低可见性时,该模块的测试编译会失败 |
CocoFeaturesMojoTest、CocoPackagePruneMojoTest、CocoAnnotatedFeatureScannerTest、CocoBuildFeatureConfigurationLoaderTest、CocoMavenPluginDescriptorTest
|
coco-build/coco-maven-plugin/src/test/java/io/github/coco/maven/ |
构建期装配、包裁剪、@CocoFeatures 扫描、application.yml 读取、插件描述符 |
starter 模块的 src/test 共 7 个类:上表中的 3 个(两个依赖契约测试和 CocoSpringCompositionClasspathTest),加 CocoAutoConfigurationTest、CocoLifecycleLoggerTest、CocoSpringApplicationRunListenerTest,再加 1 个 IT(CocoStarterSmokeIT)。
先按 开发环境与常见陷阱 配好 JDK 21(编译目标是 17):
$env:JAVA_HOME='<JDK21_HOME>' # 换成你自己的 JDK 21 安装目录
$env:Path="$env:JAVA_HOME\bin;$env:Path"
java -version
mvn -v| 目的 | 命令 | 来源 |
|---|---|---|
| CI 同款完整验证 | mvn -B -ntp install |
.github/workflows/reusable-tests.yml |
| 贡献前的完整验证 | mvn -B clean verify |
CONTRIBUTING.md |
快速 verify
|
mvn verify |
AGENTS.md |
| 单模块及其上游依赖的测试 |
mvn -pl :module-artifact-id -am test,例如 mvn -pl :coco-web -am test
|
AGENTS.md |
| 插件相关反应堆检查 | mvn -B -pl :coco-maven-plugin -am verify |
AGENTS.md |
| 归档裁剪冒烟(含 IT) | mvn -B -pl :coco-feature-archive-smoke -am verify |
由根 pom.xml 的 Failsafe 绑定与该模块 pom.xml(artifactId coco-feature-archive-smoke)推导得出 |
| 只编译测试代码(静态分析前置) | mvn -B -ntp -DskipTests test-compile |
.github/workflows/reusable-static-analysis.yml |
| release profile 冒烟(无 GPG) | mvn -B -Prelease '-Drevision=1.0.0' '-Dgpg.skip=true' '-DskipTests' verify |
AGENTS.md、release.md
|
PowerShell 提示:CONTRIBUTING.md 在 release 冒烟命令里给 -D 参数加了单引号('-Drevision=1.0.0' '-Dgpg.skip=true' '-DskipTests'),因为 PowerShell 可能把带点的参数拆开;上表沿用同样的写法。
-Dtest=ClassName 是 Surefire 的标准用法,本仓库没有为它做专门配置(根 pom.xml 只设置了 useModulePath=false):
# 只在目标模块里跑一个测试类
mvn -pl :coco-feature-model -Dtest=StandardCocoFeaturesTest test
# 带 -am 时,上游模块里没有同名测试会报 “No tests matching”,可以加上这个标准开关
mvn -pl :coco-feature-model -am -Dtest=StandardCocoFeaturesTest -Dsurefire.failIfNoSpecifiedTests=false testIDE 里直接运行单个 JUnit 类同样可行;如果 IDE 导入时报 ${revision} 相关问题,见 开发环境与常见陷阱。
- 在
StandardCocoFeaturesTest补上新 Feature 的 id、artifactId、自动配置 FQCN 与依赖断言。 - 决定 starter 是否直接依赖该模块:
- 要直接依赖:改
coco-spring/coco-spring-boot-starter/pom.xml,并确认现有契约测试没有被破坏。 - 不直接依赖(和
coco-rate-limit、coco-idempotency一样由构建期装配):参照CocoRateLimitStarterDependencyContractTest加一个契约测试。取舍理由见AGENTS.md的“starter 只组合能力”;哪些模块应走这条路径,设计意图未见文档说明,行为以coco-spring/coco-spring-boot-starter/pom.xml与现有契约测试为准。
- 要直接依赖:改
- 在新模块里按第 2 节的命名补一套:
Coco<X>AutoConfigurationTest、Default<X>Test、(有存储抽象时)InMemory<X>StoreTest/Redis<X>StoreTest。 - 如果改了旧坐标门面或重命名了模块,对应的
*CompatibilityCompileProbe需要保持可编译。 - 提交前至少跑一次
mvn -B -ntp install,因为 Failsafe 的两个 IT 只在verify之后的阶段执行。
完整步骤见 指南:新增一个 Feature 模块。
coco-support/coco-test/src/main/java/io/github/coco/test/CocoTestSupport.java 是该模块唯一的类,提供:
-
configurationMetadata(Class):在测试里读取spring-boot-configuration-processor生成的META-INF/spring-configuration-metadata.json,用来断言配置键、默认值是否真的出现在元数据里。 -
jsonResource(Class, String):读取测试资源里的 JSON。 -
findNamedNode、requiredNamedNode:在 JSON 树里按名称查找节点。
当前只有 coco-security 以 test 作用域依赖 coco-test。
文档与实现存在出入:module-layout.md 的迁移映射把它的新主制品名写作 coco-test-support,而当前 Reactor 的实际模块是 coco-support/coco-test(artifactId coco-test),以根 pom.xml 的 <modules> 为准。更多说明见 支持模块与仓库工具。
改 .github/ 下的脚本、工作流或 README 片段时,CI 会额外运行这些检查,本地可以直接复用:
python .github/scripts/test_agent_review.py
python .github/scripts/test_auto_merge.py
python .github/scripts/test_contributor_gate.py
python .github/scripts/test_verify_boot_archive_feature_coordinates.py
python .github/scripts/test_release_file_count_preflight.py
python -m ruff check .github/scripts
python -m ruff format --check .github/scripts
node --test .github/readme/tests/readme.test.mjs
node .github/readme/scripts/render.mjs --check-
CONTRIBUTING.md只列出了前两条 Python 测试;其余三条来自.github/workflows/reusable-static-analysis.yml。 - Python 依赖清单在
.github/requirements/static-analysis.txt;CI 使用 Python 3.12。 -
README.md与README_CN.md是生成文件,不要直接改;改.github/readme/fragments/后运行node .github/readme/scripts/render.mjs --write,再用--check验证。
改 docs/wiki/ 下的页面时,用 Wiki 工具自检(详见 指南:维护与发布本 Wiki):
python .github/scripts/test_wiki_sync.py
python .github/scripts/wiki_sync.py validate这两条命令需要自行在本地运行:它们目前没有列入 reusable-static-analysis.yml 的显式命令清单,但 .github/scripts/ 下的新脚本仍会被上面的 ruff check 与 ruff format --check 覆盖。
SpotBugs 与 Checkstyle 没有绑定到 Maven 生命周期,所以本地 mvn install 不会运行它们。CI 里单独调用:
mvn -B -ntp spotbugs:check # 目前 continue-on-error,只出报告
mvn -B -ntp checkstyle:check # 失败会阻塞 CI gate根 pom.xml 里 Checkstyle 的 configLocation 是 google_checks.xml,即 Checkstyle 内置的 Google 规则集;仓库内没有被跟踪的自定义 checkstyle / spotbugs 配置文件,所以实际生效的就是这套内置规则与 SpotBugs 的 effort=Max、threshold=Low 设置。
- 编译或测试阶段报
InvalidConfigurationMetadataException ... End of input at character 0:多半是残缺的target/产物。构建被中断后,target/classes/META-INF/spring-configuration-metadata.json可能留下 0 字节文件;执行mvn clean后重新构建即可,详见 FAQ 与故障排查。 - 某个模块编译时报
package ... does not match、class ... should be declared in a file named或重复类:先用git status -sb与git diff检查是否有尚未收尾的本地改动,确认类的package声明与所在目录一致,再修正这些改动,或先git stash暂存后重试(git restore会丢弃改动,使用前请确认)。 - 默认
JAVA_HOME可能不是 JDK 21,先用java -version与mvn -v自检。
仓库 · CHANGELOG · CONTRIBUTING · GOVERNANCE · SECURITY · 许可证 Apache-2.0
本 Wiki 镜像自仓库的 docs/wiki 目录,请通过 PR 修改,不要在网页上直接编辑。
开始
架构
模块参考
约定与指南
速查与排障
仓库