如何测试服务?——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,上面的“理论”可能有点抽象,不是很容易理解。来点实践吧。
假设场景:一个账户服务,可以开/关户、存/取钱。