如何测试服务?——Go DDD实践

编写能跑的程序,是编程的最低要求,软件工程的真正难点在于架构和测试。大多数人对测试存在误解。

常见误解

测试是QA团队的事
最常见的误解是,测试是QA团队的事。RD只负责编写代码,测试完全由QA负责,无需(或没必要)花太多精力在测试上。事实上,可测试性是架构的一部分,编写可测试的代码对架构提出更严格的要求,需要付出努力才能达到。能否编写可测试代码的衡量一个人软件工程能力的重要标准。

编写测试影响开发效率
项目赶进度,编写测试影响开发效率?大量统计表明,编写测试可以提升开发效率。其中主要原因有两方面:自动化测试可以帮助迭代开发中快速定位异常,缩短定位时间;测试对系统架构提出更严格要求,好的架构更容易迭代。

单测覆盖面越广越好
测试的目的是帮忙我们快速定位异常位置,理想的测试应该是足够精细,以至于当用例报错时,可以直接定位到异常原因。覆盖面太广会使问题难以定位,

设计思想

在DDD实践中,我们将系统分层:应用层、领域层、设施层。

层级 简写 说明
应用层 Application 聚合领域服务,并对外提供接口
领域层 Domain 通过领域服务对业务逻辑建模
设施层 Infra 领域服务的设施实现

在一个DDD系统中,领域层只负责业务逻辑,与具体设施实现无关。事实上,一个领域服务可以有多个不同的设施实现(比如:Infra1使用mysql作为repo实现、Infra2使用mongo)。

为方便理解,引入记号。

记号 说明
$D$ 领域服务
$I$、$I_n$ 设施实现,eg: $I_1$、$I_2$

$D$和$I$组合成一个具体的领域服务实现——$D\ I_1$、$D\ I_2$。

对于测试,我们进行分层。

记号 说明
$TD$ 领域层测试。用于验证业务逻辑
$TI$ 设施层测试。用于保证Infra实现正确

$TI$类型签名为 $TI :: I \rightarrow ()$,表示接收一个设施实现作为参数,返回测试结果。
$TD$类型签名为 $TD :: D \rightarrow I \rightarrow ()$,表示依次接收一个领域服务和设施实现作为参数,返回测试结果。

这么设计的一个好处是,测试与设施解耦,可自由组合。
比如,$TI\ I_1$ 表示对Infra1进行测试、 $TD\ D\ I_2$ 表示对由Infra2实现的领域服务进行测试。

实践

Talk is cheap. Show me the code.

Okay,上面的“理论”可能有点抽象,不是很容易理解。来点实践吧。

假设场景:一个账户服务,可以开/关户、存/取钱。

REPO: https://github.com/a3d21/goddd


如何测试服务?——Go DDD实践
https://a3d21.github.io/2022/01/04/2022-01-04-how-to-test-your-service/
作者
a3d21
发布于
2022年1月4日
许可协议