EDD 测试指南
大约 2 分钟
Title
EDD 测试指南:副作用驱动架构下的多层次测试策略
一、测试的结构定位
在 Effect-Driven Design (EDD) 架构中,系统被分为以下五层:
core ──► domain ──► app ──► event ──► infra
每层的副作用等级不同,测试方法、测试目标也随之不同。
| 层级 | 目标 | 副作用等级 | 测试策略 |
|---|---|---|---|
| core | 纯函数业务逻辑 | 0 | 单元测试 + 属性测试 + 形式化验证(可选) |
| domain | trait 抽象接口 | 1 | mock注入测试 + contract约束 |
| app | usecase 组合逻辑 | 2 | trait+mock组合测试 + YAML驱动测试生成 |
| event | 副作用调度逻辑 | 2/3 | 事件断言测试 + event schema 测试 |
| infra | 副作用真实实现 | 3 | 集成测试(db/api/log) + 端到端测试 |
二、core 层测试
目标:100% 纯函数测试
- 所有业务模型(User, Post, Order)的方法都不应有副作用
- 所有校验函数(validate_开头)都应测试全路径
- 未来可采用形式化测试,比如说
quick check
#[test]
fn test_validate_username() {
assert!(validate_username("ab").is_err());
assert!(validate_username("valid_user").is_ok());
}
三、app 层 usecase 测试
目标:
- 使用 mock 实现隔离所有副作用(DB/API/AB决策)
- 断言返回值 + 断言 events 中的副作用语义
推荐结构
#[tokio::test]
async fn test_register_user_success() {
let mut mock_repo = MockUserRepository::new();
let mut mock_ab = MockABSelector::new();
mock_repo.expect_exists().return_const(false);
mock_ab.expect_is_in_experiment().returning(|_, _| Ok(true));
let result = register_user(input, &mock_repo, &mock_ab).await.unwrap();
assert!(result.events.contains(&AppEvent::SendWelcomeEmail { .. }));
}
四、event 层测试(事件语义验证)
目标:
- 对于每种事件,确保:
- 构造正确(payload 合法)
- schema 正确(可序列化)
- 分发机制逻辑正确
#[test]
fn test_send_email_event_serialization() {
let evt = AppEvent::SendWelcomeEmail { to: "a@b.com".into() };
let json = serde_json::to_string(&evt).unwrap();
assert!(json.contains("SendWelcomeEmail"));
}
五、infra 层测试(副作用实现)
目标:
- 实际发邮件 / 调用 API / 写数据库 的行为正确
- 支持集成测试(用 testcontainers 或 test SQLite)
#[tokio::test]
async fn test_user_repo_save_and_list() {
let db = setup_test_db().await;
let repo = SeaUserRepository { db };
let user = User::new(...);
repo.save(&user).await.unwrap();
let list = repo.list().await.unwrap();
assert_eq!(list.len(), 1);
}
六、测试覆盖规划
| 层 | 建议覆盖率 |
|---|---|
| core | 100% 单元测试 |
| domain | 100% mock/trait行为路径 |
| app | usecase主路径全覆盖 + 多副作用分支测试 |
| event | 所有事件结构测试 + 流路径测试 |
| infra | 核心接口集成测试 + 副作用可观测性测试 |
七、其他建议
- 所有 usecase 应测试其
Outcome<T, Vec<AppEvent>>的结构完整性 - 所有副作用 trait 都应有 mockable + test impl
- 所有事件建议在 CI 中跑 schema 检查
- 建议引入
proptest对纯逻辑进行属性测试 - 使用
cargo-nextest加速异步 usecase 级测试执行
八、结语
副作用驱动架构中,测试本身也必须“语义化”:测试的目标不再是命中路径,而是验证意图清晰的语义行为是否被正确组织与触发。 测试设计就是 usecase 设计的镜像 —— 它也是架构表达力的一部分。