---
url: /zh/guide/application-testing.md
description: 在领域规格之后，验证 Wow 应用的生成元数据、运行时装配、真实适配器、恢复与安全边界。
---

# Wow 应用测试

本页面面向使用 Wow 构建业务应用的团队。领域 DSL 回答“模型是否按不变量工作”，应用测试回答“这个模型能否在实际装配、协议和基础设施中安全运行”。两者是连续门禁，不能互相替代。

::: tip 完成信号
应用测试完成时，应有当前构建产生的元数据证据、至少一个端到端业务入口、生产同型 Adapter 的重启/重投证据和安全反例。下一层是在目标环境执行发布、恢复与可观测性验收，而不是运行 Wow 框架仓库的基准。
:::

## 三类测试不要混在一起

| 范围 | 证明什么 | 不证明什么 |
| --- | --- | --- |
| [领域 DSL](./test-suite.md) | 命令决策、拒绝、事件、溯源状态、Saga 命令 | Spring、HTTP、KSP 打包、真实存储或 Broker |
| 应用集成门禁 | 应用自己的模块、配置、入口和生产 Adapter 能闭环 | Wow 框架在其他配置下的通用性能或正确性 |
| [框架仓库测试与基准](./test-runtime.md) | Wow 源码、TCK、容器集成和指定 JMH 工作负载 | 你的应用已具备发布条件或生产容量 |

业务应用不需要复制 `allLocalTest`、`allContractTest`、`allIntegrationTest` 或 Codecov flag 结构。这些是 Wow 仓库的维护任务。应用应按自己的模块和发布风险设置门禁。

## 应用测试阶梯

| 层次 | 最小证据 | 失败时先查 |
| --- | --- | --- |
| 领域规格 | 所属领域模块的 `test`/`check` 通过 | 不变量、事件和溯源函数 |
| 编译元数据 | 注解模块生成非空 `META-INF/wow-metadata.json` | KSP 与 `wow-compiler` 是否应用在正确模块 |
| 运行时装配 | 服务加载元数据并暴露预期命令/状态入口 | Starter capability、模块运行时依赖、配置 |
| 协议垂直切片 | 一个业务命令从入口到持久化结果可观察 | 请求契约、路由、等待语义、错误映射 |
| 真实 Adapter | 生产同型存储/Broker 上读写和重投正确 | 序列化、版本冲突、幂等和网络边界 |
| 恢复与安全 | 重启/重放后状态一致，越权请求 fail closed | 快照、位点、租户/owner/space 与 ABAC |

每天先运行最窄的受影响模块，合并或发布前再逐层扩大。不要因为根 `build` 很大就跳过能直接指向失败层的模块任务。

## 1. 领域门禁

每条聚合不变量至少覆盖：

* 成功命令产生的事件和最终状态；
* 业务拒绝及状态不变；
* 关键生命周期转换；
* 删除、恢复或其他启用的框架生命周期；
* Saga 对正反两类输入事件产生正确命令或不产生命令。

本仓库的可运行参考是：

```bash
./gradlew :example-domain:check
```

业务应用应替换为自己的领域模块。详细 DSL 见[领域测试套件](./test-suite.md)。

## 2. 生成元数据门禁

含 Wow 注解模型的模块必须应用 KSP 和 `wow-compiler`，并把生成资源带入服务运行时 classpath。可用仓库示例验证完整生成链路：

```bash
./gradlew :example-domain:clean :example-domain:kspKotlin :example-domain:test
test -s example/example-domain/build/generated/ksp/main/resources/META-INF/wow-metadata.json
```

在业务应用中替换模块路径，并检查每个含注解的模块。完成信号不只是 KSP 任务退出 0：文件必须非空，服务模块必须依赖该模块，启动证据必须显示资源已被加载。不要手写或提交生成文件。

## 3. 运行时与协议垂直切片

为至少一个真实业务聚合保留应用级测试，覆盖以下闭环：

1. 使用与生产入口相同的 Spring 配置启动应用；
2. 通过公开协议发送一个带唯一聚合 ID 和请求 ID 的命令；
3. 断言协议状态、命令结果和错误映射；
4. 从公开状态或查询入口读取结果；
5. 证明读到的状态来自刚才持久化的事件，而不是测试内共享对象。

这个切片应使用应用现有的 Cart、Order 或其他真实模型，不要另造一个只为测试路由的“演示聚合”。内存 Adapter 可以作为快速门禁，但测试报告必须明确它没有覆盖生产存储、Broker、重启和鉴权。

## 4. 真实 Adapter 与失败语义

生产使用的每种持久化或传输能力至少有一个容器或隔离环境测试：

* 使用与生产一致的 Starter capability 和序列化配置；
* 写入真实 EventStore 后能加载同一事件流；
* 相同请求 ID 或 Broker 重投不会重复产生业务副作用；
* 乐观锁或版本冲突以应用定义的方式返回，不丢失已成功数据；
* 外部依赖失败不会把部分结果报告为完整成功；
* SnapshotStore 启用时，快照加载结果与完整事件重放一致。

按应用拥有的 Adapter 取舍，不要为了“完整”测试未使用的数据库或 Broker。

## 5. 重启、重放与升级

进程内成功不等于可恢复。至少证明：

1. 写入事件后停止应用；
2. 使用同一持久化数据重新启动；
3. 读取状态并与重启前结果比较；
4. 在禁用或清除快照后从完整事件历史得到相同状态；
5. 投影、消费者位点和补偿任务能够按已记录步骤恢复；
6. 若事件 revision 或序列化形状发生变化，旧历史数据有明确的兼容或迁移证据。

恢复测试必须保留输入数据版本和环境配置，否则一次成功截图无法作为后续发布证据。

## 6. 安全与隔离反例

安全测试优先覆盖失败路径：

| 场景 | 期望 |
| --- | --- |
| 匿名访问受保护命令或查询 | `401` 或 `403` |
| 伪造 tenant、owner 或 space | 请求被拒绝且作用域不扩大 |
| 主体缺少必要 ABAC 标签 | fail closed，不退化为全量匹配 |
| 跨租户或跨 owner 查询 | 不返回越权数据 |
| 普通请求尝试访问原始查询工厂 | 没有可达入口 |

完整边界见[数据权限](./data-access.md#必须完成的安全闭环)。安全通过需要实际应用的 Spring Security/CoSec 配置，领域规格无法替代。

## 发布前证据表

| 证据 | 完成条件 |
| --- | --- |
| 领域 | 成功、拒绝、关键生命周期规格通过 |
| 生成 | 每个注解模块的元数据非空并被运行时加载 |
| 协议 | 公开入口到可读取结果的垂直切片通过 |
| 基础设施 | 生产同型 Adapter 的写入、重投、冲突和失败路径通过 |
| 恢复 | 重启与无快照重放得到一致状态 |
| 安全 | 鉴权、租户隔离和 ABAC 反例全部 fail closed |
| 运维 | 备份、恢复、回滚、Trace、指标与告警在目标环境有证据 |

只有修改 Wow 框架源码、实现 Adapter 或调查框架性能时，才进入[框架测试与基准](./test-runtime.md)。框架 CI 绿色或历史基准数字都不能替代这张应用证据表。
