编写可测试的代码

本章最后一个要讨论的方面是如何使用 SOLID 软件设计原则编写可测试的代码。正如我们已经多次看到的,设计良好的代码也是容易测试的代码。难以测试的应用代码通常是应用难以修改和维护的信号。

这五个强大的原则最早由 Robert C. Martin 于 2000 年在一篇论文中提出,后来在他的书《敏捷软件开发:原则、模式与实践》中出版。这些原则帮助敏捷团队交付可维护、易于重构的代码。

图 3.5 总结了 SOLID 设计原则:

image 2025 01 04 16 41 05 063
Figure 1. Figure 3.5 – The SOLID design principles

让我们回顾一下 SOLID 原则及其对测试编写的意义:

  1. 单一职责原则(SRP):实体应该有一个单一的职责和单一的变化原因。这个原则将使测试代码变得简单,因为类提供的功能范围较小。这样我们可以将精力集中在覆盖边界情况上,而不是覆盖大量的方法。

  2. 开放封闭原则(OCP):实体应该对扩展开放,但对修改封闭。这个原则意味着确保代码变化能够扩展现有功能,但不破坏现有行为。通过设计实现向后兼容的代码,不需要修改大量的测试。新的或扩展的功能可以通过新的测试用例来覆盖,确保测试套件保持稳定。

  3. 里氏替换原则(LSP):每个子类或派生类应该满足其父类或基类的行为。由于 Go 没有继承,你可能会觉得这个原则不适用。然而,我们通过接口实现了多态,因此可以通过它们设定的契约来表达这个原则。维护可替换接口的代码将很容易测试,因为它不会要求对现有测试套件做出大量修改。

  4. 接口隔离原则(ISP):客户端代码不应该被迫实现它不使用的方法或接口。这个原则鼓励使用小型接口,只有围绕单一职责进行封装。接口应该在客户端/调用代码的一侧定义,客户端只应该定义它们有兴趣使用的接口方法。小接口导致小的 mocks,从而使测试设置和断言变得简单。

  5. 依赖倒置原则(DIP):实体应该依赖于抽象,而不是依赖于具体实现。这个原则鼓励使用接口来表示依赖关系。我们在本章的依赖注入部分讨论了这个原则。我们看到,结合接口的强大功能,使用这种技术将产生松耦合的代码,减少了需要设置的跨包依赖,从而使得代码更容易测试。

正如我们所看到的,接口在 Go 中是实现 SOLID 原则的核心。它们应该被用来编写易于维护和重构的代码。由于重构是 TDD 实践的核心,易于重构的代码也将是易于测试的代码。

SOLID 实体

请记住,在 Go 中,实体应该是包,而不是结构体。包提供自己的 API,它们建立了自己的契约和接口。在设计你的包 API 时,牢记 SOLID 原则。