Domain Model Test
解决什么问题:领域逻辑是核心资产——没有领域测试 = 业务规则无验证 = 回归必爆。领域测试是金字塔底部,ROI 最高。
它是什么
Domain Model Test(领域模型测试) 是对领域层的单元测试——验证实体、值对象、聚合、领域服务的行为正确性。它不依赖数据库、不依赖框架、不依赖外部服务:
java
// 纯 POJO 测试,毫秒级
Order order = new Order(customerId);
order.addItem(productId, quantity: 2);
assertEquals(2, order.totalQuantity());IDDD Ch 5:"The domain model should be tested in isolation from infrastructure."
V2 视角(V2.7 B4 抽象):领域测试 = "从具体技术栈抽象出纯业务规则"——测试本身就是 Ubiquitous Language 的执行验证。
关键属性
| 属性 | 值/含义 |
|---|---|
| 测试对象 | 实体、值对象、聚合、领域服务、Domain Policy |
| 依赖 | 无(纯 POJO) |
| 速度 | 毫秒级(千个测试几秒跑完) |
| 覆盖率目标 | 领域层 ≥ 90%(业务规则核心) |
| 反模式 | Mock 一切 / 测试 toString / 测试 getter |
| 核心约束 | 不依赖 Spring / DB / 网络——纯 Java/Kotlin |
| 判错标准 | 领域测试用 @SpringBootTest = 测试架构错误 |
怎么用
场景 1:测试聚合不变性
不变性(Invariant) 是聚合的核心约束——测试就是验证不变量:
java
@Test
void addItem_exceedsStock_throws() {
Product product = new Product(stock: 5);
Order order = new Order(customerId);
assertThrows(InsufficientStockException.class,
() -> order.addItem(product.id(), quantity: 10));
}
@Test
void placeOrder_emptyOrder_throws() {
Order order = new Order(customerId);
assertThrows(EmptyOrderException.class, order::place);
}场景 2:测试值对象
值对象的不变性靠构造约束——测试构造边界:
java
@Test
void money_negativeAmount_throws() {
assertThrows(IllegalArgumentException.class,
() -> new Money(Currency.USD, -1));
}
@Test
void money_add_returnsNewInstance() {
Money a = Money.usd(10);
Money b = Money.usd(20);
assertEquals(Money.usd(30), a.plus(b));
assertEquals(Money.usd(10), a); // 不变性
}场景 3:测试领域服务
领域服务处理跨聚合逻辑——用真实聚合,不用 mock:
java
@Test
void pricingService_appliesDiscountToVIP() {
Customer vip = new Customer(tier: VIP);
Order order = new Order(...);
order.addItem(productId, 1);
Money finalPrice = new PricingService().calculate(order, vip);
assertTrue(finalPrice.lessThan(order.subtotal()));
}V2 B4 视角:用真实对象 = "按对象分解" 的测试版本——不要 mock 到单元测试里。
常见误区
- "Mock Repository 测试领域服务"——错。Repository 是基础设施,不应该 mock 它测试领域——用 in-memory fake
- "测 toString / equals"——错。这些是语言/工具保证的,不是领域逻辑
- "领域测试也要 Spring"——错。Spring 测试慢、启动重——把领域测试剥离 Spring
- "覆盖率 100% = 安全"——错。覆盖率是必要不充分条件——业务规则全覆盖 才是目标
- "测试通过 = 业务正确"——错。测试只验证你写下的 期望——业务专家验收 ≠ 测试通过
关键洞察
领域模型测试是"业务规则的形式化"——把 Ubiquitous Language 翻译成可执行断言。
控制论视角:领域测试 = 状态方程的离线验证。没验证过的状态方程 = 不能上生产。 IDDD Ch 5-10 视角:聚合(Ch 10)的不变量必须 100% 测试覆盖——业务规则就在那里。 SE@G Ch 11 视角:领域测试属于"小测试"——毫秒级、数量大、独立运行。 EDM Ch 9 视角:领域事件(Ch 8)的 schema 演进也要有测试——旧 Consumer 还能反序列化旧事件吗?
相关链接
- Test Pyramid——领域测试在金字塔底部
- Contract Test——集成测试的另一种形态
- [V3 涌现作为] · 抽象——领域测试 = 抽象业务的执行验证
- [V3 涌现之时] · 演化——业务变了领域测试先变
- 源典:IDDD Ch 5-10——领域测试的对象
- 源典:SE@G Ch 11——小测试的最佳实践