Skip to content

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 到单元测试里。

常见误区 ​

  1. "Mock Repository 测试领域服务"——错。Repository 是基础设施,不应该 mock 它测试领域——用 in-memory fake
  2. "测 toString / equals"——错。这些是语言/工具保证的,不是领域逻辑
  3. "领域测试也要 Spring"——错。Spring 测试慢、启动重——把领域测试剥离 Spring
  4. "覆盖率 100% = 安全"——错。覆盖率是必要不充分条件——业务规则全覆盖 才是目标
  5. "测试通过 = 业务正确"——错。测试只验证你写下的 期望——业务专家验收 ≠ 测试通过

关键洞察 ​

领域模型测试是"业务规则的形式化"——把 Ubiquitous Language 翻译成可执行断言。

控制论视角:领域测试 = 状态方程的离线验证。没验证过的状态方程 = 不能上生产。 IDDD Ch 5-10 视角:聚合(Ch 10)的不变量必须 100% 测试覆盖——业务规则就在那里。 SE@G Ch 11 视角:领域测试属于"小测试"——毫秒级、数量大、独立运行。 EDM Ch 9 视角:领域事件(Ch 8)的 schema 演进也要有测试——旧 Consumer 还能反序列化旧事件吗?

相关链接 ​

从名家方法论与工程化思路中蒸馏出自己的工程体系。