npx skills add ...
npx skills add full-stack-skills/ddd-skills --skill ddd-testing-strategist
DDD testing strategy — domain model unit testing, aggregate root testing, repository testing with mocks, application service integration testing, adapter E2E testing, test pyramid for DDD layered/hexagonal/clean architectures, and Test-Driven Domain Design (TDDD). Use when user asks about testing strategy, 测试策略, DDD testing, aggregate test, repository test, test-driven domain design, or needs to test DDD applications.
npx skills add full-stack-skills/ddd-skills --skill ddd-testing-strategist
DDD testing strategy — from value object unit tests to end-to-end validation across all DDD architectures. Covers the DDD test pyramid, layer-specific test patterns, mock strategies by architecture, Test-Driven Domain Design (TDDD), CI/CD pipeline integration, and N+1 query detection.
什么时候用:用户询问 DDD 测试策略、聚合根测试、Repository 测试、Mock 策略、CQRS 测试、TDDD、CI/CD 测试集成时触发。
This skill is designed for: Backend developers (implementing DDD architectures), Software architects (evaluating and selecting patterns), Tech leads (reviewing team implementations), and DDD beginners (learning domain-driven design fundamentals).
DDD 金字塔与经典金字塔的关键区别:Domain 层(VO + AR + DS)占 60%,是测试核心。
覆盖:构造验证、运算逻辑、等值比较、不变式。
覆盖:每个状态转移独立测试类 → happy path + 边界条件 + 不变量 + 事件。
Mock 原则:只 Mock Repository / Gateway(Interface),不 Mock Domain Service 或 Aggregate Root。
App Service:Mock 所有外部端口。Repository:Testcontainers + 真实 DB。Adapter:MockMvc + 真实 DB。
| 测试目标 | Repository | Gateway | EventBus | External |
|---|---|---|---|---|
| Value Object | N/A | N/A | N/A | N/A |
| Aggregate Root | N/A | N/A | Capture | N/A |
| Domain Service | Mock | Mock | Capture | Mock |
| Application Service | Mock | Mock | Mock | Mock |
| Repository Integration | Real DB | N/A | N/A | N/A |
| API Integration | Real DB | Mock | Mock | Mock |
| E2E | Real DB | Real | Real | Real |
| 原则:Domain 层不需要 Mock;只 Mock 接口(Port)不 Mock 实现类;领域事件用 Capture 模式。 |
| 架构 | 测试重心 | 差异说明 |
|---|---|---|
| Layered | Aggregate Root + Repository | Domain 层全量测试(60%+) |
| Hexagonal | Port Mock + Adapter Integration | Mock 接口即 Mock 整个外部 |
| Clean | Entity + UseCase Interactor | UseCase 层 Mock 输出端口 |
| COLA | Domain + App Service | Domain 零依赖;额外测 CQRS 分流 |
| CQRS | Command + Query Read Model | 写模型用聚合测试;读模型直接查视图 |
| Event Sourcing | Event Replay + Projection | 聚合重放测试(必测);投影读模型测试 |
四步工作流:RED → GREEN → REFACT → REPEAT
| 层 | 目标 | 关键覆盖点 |
|---|---|---|
| Value Object | ≥ 95% | 构造验证、运算逻辑、等值比较 |
| Aggregate Root | ≥ 95% | 状态转移(每个路径)、领域事件 |
| Domain Service | ≥ 90% | 跨实体编排、外部数据计算 |
| Application | ≥ 80% | Use Case 完整路径 + 异常路径 |
| Repository | ≥ 80% | 聚合完整性、N+1 查询 |
| Adapter (API) | ≥ 70% | 协议转换、错误映射 |
| 建议优先追踪分支覆盖率,Domain 层 ≥ 90%。 |
| 问题 | 回答 |
|---|---|
| 聚合根测试需要 Spring 吗? | 不需要。纯 POJO,直接用 JUnit + AssertJ。 |
| Repository 应 Mock 还是真实 DB? | Domain Service 用 Mock;Repository 用真实 DB。 |
| CQRS 怎么测 Query 端? | 直接访问读模型,不需要 Mock。 |
| Event Sourcing 的必要测试? | Event Replay:给定事件序列 → 重放 → 验证状态。 |
| 怎么避免 N+1 查询? | 集成 SQLStatementCountValidator。 |
| TDDD 和传统 TDD 的区别? | TDDD 以"领域行为/状态转移"为单元。 |
DDD testing, test pyramid, aggregate root test, value object test, domain service test, repository test, application service test, adapter test, CQRS testing, Event Sourcing test, TDDD, mock strategy, Testcontainers, ArchUnit, N+1 test, domain event test, branch coverage, CI/CD testing, integration test, E2E test, event replay test
| 文件 | 用途 |
|---|---|
| references/testing.md | 测试金字塔 + 各层 TypeScript 示例 |
| references/mock-integration-patterns.md | Mock 实现 + Java 集成测试 |
| references/clean-ddd-hexagonal-testing.md | Clean/Hexagonal/DDD 多层测试 |
| references/unit-testing-strategies.md | VO/AR/DS 单元测试 + 状态转移 |
| references/integration-test-strategies.md | Repository/API 集成测试 + N+1 |
| references/cqrs-event-sourcing-testing.md | CQRS + ES 重放/投影/快照测试 |
| references/test-coverage-targets.md | 覆盖率目标 + 分支覆盖率 + JaCoCo |
| references/ci-cd-test-stages.md | CI/CD 三阶段 + GitHub Actions |
| references/architecture-testing-comparison.md | 各架构测试差异 + 项目阶段策略 |
| references/test-driven-domain-design.md | TDDD 四步工作流 + 完整案例 |
| 文件 | 用途 |
|---|---|
| examples/domain-test-examples.md | VO + AR + DS 完整测试示例 |
| examples/app-adapter-test-examples.md | App Service + Adapter + E2E |
| examples/cqrs-es-test-examples.md | CQRS + ES 重放/投影/快照测试 |
| examples/builder-mock-patterns.md | Builder + Mock Repository/EventPublisher |
| examples/architecture-test-examples.md | ArchUnit 架构测试(分层/聚合隔离) |
This skill is pure documentation. It contains no executable scripts, collects no user data, accesses no external services or networks.
📍 You are here:
ddd-testing-strategist— Step 6 ← Previous: hand off toddd-domain-designerskill(Install:npx skills add full-stack-skills/ddd-skills --skill ddd-domain-designer). → Next: hand off toddd-devops-integrationskill(Install:npx skills add full-stack-skills/ddd-skills --skill ddd-devops-integration). 🔗 Related: hand off toddd-code-reviewerskill(Install:npx skills add full-stack-skills/ddd-skills --skill ddd-code-reviewer) |ddd-cqrs-architectureskill(Install:npx skills add full-stack-skills/ddd-skills --skill ddd-cqrs-architecture). 🏠 Home: hand off toddd-architecture-awesomeskill(Install:npx skills add full-stack-skills/ddd-skills --skill ddd-architecture-awesome).
@Test void money_should_prevent_negative_amount() {
assertThrows(IllegalArgumentException.class, () -> new Money(-1.0, "CNY")); }
@Test void money_add_should_sum_same_currency() {
assertEquals(new Money(30.0, "CNY"), new Money(10.0, "CNY").add(new Money(20.0, "CNY"))); }class OrderPayTest {
@Test void pay_changes_status_to_paid_when_draft() { ... }
@Test void pay_emits_orderPaidEvent() { ... }
@Test void pay_fails_when_already_paid() { ... }
@Test void pay_fails_when_cancelled() { ... } }@Test void pricing_service_applies_vip_discount() {
when(orderRepository.findById(order.getId())).thenReturn(Optional.of(order));
pricingService.calculatePrice(order.getId());
assertEquals(new Money(90.0, "CNY"), order.getTotalAmount()); }@Test void place_order_creates_and_saves() {
var orderId = handler.handle(new PlaceOrderCommand("cust-123", ...));
assertNotNull(orderId); }
@SpringBootTest @Testcontainers
class OrderRepositoryImplTest {
@Test void persists_and_retrieves_complete_aggregate() { ... } }
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class OrderControllerTest {
@Test void post_orders_returns_201() { ... }
@Test void post_invalid_product_returns_400() { ... } }@Test void tddd_order_should_not_allow_paying_twice() {
Order order = Order.create(customerId, items);
order.pay(mockGateway);
assertThrows(OrderException.class, () -> order.pay(mockGateway)); }
public void pay(PaymentGateway gateway) {
if (this.status == OrderStatus.PAID) throw new OrderException("Already paid");
this.status = OrderStatus.PAID;
addDomainEvent(new OrderPaidEvent(this.getId())); }