BitzOrcas 既是应用模板,也是框架包和商业交付产品。测试要回答的不只是“这个方法返回值对不对”,还要回答模块边界、ORM 等价性、模板消费方式和发布来源是否可信。
五层证据链
| 层 | 要回答的问题 | 典型项目或门禁 |
|---|---|---|
| 代码级 | 领域规则、状态转换和失败分支是否正确 | Unit、Application、Licensing tests |
| 架构级 | 错误依赖、旧路径、反射和包目录漂移是否被阻止 | Architecture tests |
| 适配器级 | SqlSugar、EF Core、消息和存储是否遵守同一语义 | Integration + Testcontainers parity |
| 消费级 | 客户能否只用模板与 PackageReference 构建 | ConsumerTemplate、Consumer Contract |
| 发布级 | Feed、签名、provenance、SBOM 和外部 Consumer 是否同源可信 | Commercial GA |
低层测试不能替代高层证据。单元测试全绿,不能证明 EF Core 与 SqlSugar 的并发语义一致;本地 Consumer 合同全绿,也不能证明正式包已经签名并上传到受控 Feed。
主要测试项目
仓库的测试项目已经超过早期文档所写的 5 个。常用入口如下:
| 项目 | 关注点 |
|---|---|
BitzOrcas.Unit.Tests | 领域原语、值对象、纯业务规则 |
BitzOrcas.Application.Tests | Handler、管线、授权和应用编排 |
BitzOrcas.Architecture.Tests | 依赖方向、文件红线、CI 与商业目录契约 |
BitzOrcas.Integration.Tests | API Shell、数据库、ORM parity、消息与迁移 |
BitzOrcas.CodeGeneration.Tests | 模板渲染和代码生成红线 |
BitzOrcas.Generator.Package.Tests | Generator 作为 NuGet analyzer 的消费行为 |
BitzOrcas.ConsumerTemplate.Tests | Profile 与模板组合输出 |
BitzOrcas.Framework.ConsumerContract.Tests | 隔离 Feed 的包消费闭环 |
BitzOrcas.Licensing.Tests | 在线、离线、Grace、过期、撤销和防重放 |
BitzOrcas.Workflow.*Tests | 工作流引擎与持久化合同 |
完整清单以 tests/ 和 BitzOrcas.Modern.slnx 为准。
按改动选择测试
| 改动 | 开发中的最小相关集 |
|---|---|
| 领域规则或值对象 | Unit + 对应 Application tests |
| 模块依赖、项目引用、目录迁移 | Architecture tests |
| Store、Repository、mapping、migration | Unit + Architecture + 对应 Docker parity |
| Source Generator | Generator tests + Generator Package tests + Architecture |
| Profile、模板、商业包 manifest | ConsumerTemplate + verify-template.sh + Consumer Contract |
| CI workflow 或脚本 | CiQualityGateTests + 目标脚本 |
| Licensing | Licensing + Architecture + Consumer Contract |
| 纯文档 | 内容检查、链接检查、git diff --check;命令片段要与源码核对 |
常用命令
# 架构边界dotnet test tests/BitzOrcas.Architecture.Tests --configuration Release
# 无 Docker 集成测试dotnet test tests/BitzOrcas.Integration.Tests \ --configuration Release \ --filter "Category!=Docker"
# 本地 PackageReference Consumer 合同dotnet test tests/BitzOrcas.Framework.ConsumerContract.Tests \ --configuration Release \ --blame-hang-timeout 12m
# 合并前 14 步完整本地门禁scripts/build/verify-all.shDocker 合同由 docker-integration-contracts workflow 拆成 13 个分片。需要本地复现时,复制失败 Job 日志里的精确 --filter,不要一开始就串行运行整个 Docker 集。
Consumer Contract 的边界
本地 Consumer Contract 会从当前源码 pack 一次候选包,建立临时 Feed,并隔离 NUGET_PACKAGES、HTTP cache、plugin cache 和 DOTNET_CLI_HOME。然后让仓库外项目完成:
restore → build → test → Release publish(当前 `PublishTrimmed=false`)→ 包资产检查它证明 PackageReference 闭包、生成器资产和源码暴露红线正确。正式发布仍需独立 Commercial GA 验证生产 Feed、签名和发布证据。
本地 Consumer 合同不能证明 trimming/Native AOT 安全。产品仓库的 verify-all.sh 会对完整 API Host 执行 PublishTrimmed=true,但这也不等于每个物理裁剪后的 Consumer Profile 都已验证;差异与补全计划见Consumer 合同。
测试所有权与重复边界
| 事实 | 主要所有者 | 高层只保留什么 |
|---|---|---|
| 聚合状态转换 | Unit | 一条关键 HTTP/事务路径 |
| 授权/验证管线 | Application | 真实 Host 的代表性拒绝路径 |
| ORM 翻译与并发 | Docker parity | 业务结果,不重复内部 SQL |
| Generator 诊断 | Generator unit/snapshot | NuGet analyzer 可消费合同 |
| 包闭包与源码隔离 | Consumer Contract | GA 对正式 artifact 的再验证 |
| 签名/SBOM/provenance | Commercial GA | 目标环境只消费已通过批次 |
同一规则在每层复制几十个相同断言,会增加维护成本却不增加独立证据。高层测试应跨越真实边界,低层测试应穷举业务分支。
合并前的证据记录
每个可验证切片至少记录:执行的精确命令、configuration、filter、通过/失败/跳过数、Docker 或外部依赖是否参与、未运行项及原因。只写“测试通过”无法区分无 Docker 快速集、完整 11 分片和正式 GA。
对于耗时或外部门禁,允许明确记录“未运行/外部前置条件未就绪”,但不能把历史运行或较低层测试替代为当前结论。
测试失败怎么读
- 找第一个失败步骤,后续错误可能只是连锁反应。
- 用相同 configuration、RID 和 filter 单独复现。
- Docker 失败先区分镜像/容器/就绪问题与业务断言。
- parity 失败要比较两套适配器的可观察语义,不只比较 SQL 或内部实现。
- Consumer 失败从临时
NuGet.Config、Package Source Mapping 和第一个缺包开始查。 - 修复后先跑最小回归,再补齐切片要求的完整门禁。
另见
本章核心导航
- 01/07
架构测试
用程序集、项目文件、源码契约与机器可读清单守住依赖方向、模块边界、生成器、商业交付和删除门禁。
- 02/07
单元测试
为聚合、值对象、Result 错误、请求规则与应用管线编写快速、确定且能表达业务合同的测试。
- 03/07
集成测试
区分 API Shell 与 Docker 合同,验证 ORM parity、迁移、消息、租户隔离和真实基础设施语义。
- 04/07
测试夹具与种子数据
选择纯对象、WebApplicationFactory、Testcontainers 和模块自有 CSV 种子,并建立确定、隔离、可重放的测试数据。
- 05/07
运行测试
按改动风险选择单元、架构、无 Docker、Docker、模板、包消费和完整验收入口,并保留可复现证据。
- 06/07
编写新测试
从风险和证据出发,把回归放进正确项目,写出能先失败、可复现、可维护并进入对应门禁的新测试。
- 07/07
性能基线与回归判断
为构建门禁、API、数据库、消息和后台作业建立可重复的性能证据,正确区分现有耗时报告与待建设的运行时基准。