测试原则

我们一般认为有这三种测试类型:

  • 单元测试(Unit test)
  • 集成测试(Integration test ,在测试的过程中会 mock API )
  • 端到端测试(End-to-End test, e2e test)

它们各有优缺,为获得更好的开发体验,我们在编写测试时应当遵守以下原则。

我们编写和维护单元测试的主要目的是:

  • 外在目的:保证函数的功能符合设计预期
  • 内在目的:促进编码者使用更合理的代码结构和面向接口编程

单元测试的测试对象是一个类或者是一段算法代码。理论上来说,有着较为独立逻辑的函数应该至少拥有一个单元测试(不包括 getter 和 setter )。

编写单元测试的基本原则如下:

  • 大道至简(Keep It Simple and Stupid)
  • 一个用例只覆盖一个场景
  • Mock 工具(例如 golang/mock )虽然很强大,但是我们并不推荐使用。当你觉得在单元测试中需要使用 Mock 工具,那么你真正要做是集成测试甚至端到端测试。

单个测试需要保证高效率,例如在重构代码的过程中,一旦你修改了一处函数逻辑,这将会触发整个模块的单元测试以保证这些修改符合预期。

集成测试将组装系统中部分高度关联的模块,并测试模块间的交互是否符合预期。

例如 KubeVela 中的集成测试主要分为两类:

  • 对于 vela core 控制器来说,你需要为主要流程添加对应的测试,例如渲染 (render)、编排 (orchestrate) 和将 CRD 部署到 Kubernetes 的过程。在这种情况下,一般会使用 mock 服务器来 mock Kubernetes 的 API ,这你就能在不使用真正的 Kubernetes 集群和 CRD 控制器的情况下编写覆盖这些自动化流程的测试了。
  • 对于 addon 来说(例如 VelaUX、apiserver 和 vela cli )你可以添加与创建、摧毁和更新 Application 相对应的集成测试,而不需要运行一整个控制器。

Vela core 部分的集成测试并不关心 CRD 是否在工作,因此它能够通过 mock CRD 控制器的行为来提高测试效率。而外部 CLI 部分的测试不关心 Application 的动作,它只需要保证新的 Application 的 spec 正确即可。

端到端测试的目的是模拟真实用户的行为,用以验证整个项目。

对于下列情况,我们建议使用端到端测试:

  • 与上游或下游项目交互,例如:
    • 进行渐进式部署的过程中 Application 控制器需要与 rollout workload 交互
    • 在同一个命令中,CLI 依赖控制器的回应以进行下一步操作
  • 核心功能或核心场景:每个核心功能或场景必须至少有一个端到端测试

测试的目的是保证软件持续交付的质量,重点在于“持续”。我们不仅要确保当前交付的质量,还要确保未来软件交付的质量。发挥好三种测试类型各自的优势,将它们结合起来,保证软件的整体质量是尤为重要的。

时间消耗测试稳定性能否模拟用户行为
单元测试
集成测试基本能
端到端测试

运行所消耗的时间在这里很容易理解。单项测试所涵盖的软件功能规模越大,环境准备和测试运行所花费的时间就越多,这就导致测试效率也降低了。 在稳定性方面,测试覆盖率越高,它可能遇到的问题就越多,而且有些问题并不是我们想要发现的真正的错误(只是噪声而已),这就导致测试稳定性降低了。对于模拟真实用户行为,只有端到端测试可以覆盖端到端,保证整个链路可以协同工作。

至于长期价值,是指现有测试在持续的软件迭代过程中的价值。对于单元测试,在代码重构时会根据类和函数的变化进行调整。 因此,代码库将会与软件迭代中的热点保持一致并不断发展。

但是集成测试或端到端测试通常被拆分为子系统边界,其外部接口相对稳定(分布式系统软件迭代过程中很少有功能变化,一般是为了前向兼容)。而且集成测试或端到端测试的代码库相对稳定,这对系统未来的演进非常重要,这样才能及时发现新功能是否损坏了现有功能。

结合三者的特点,最好的平衡方式就是遵从金字塔模型: 底部是单元测试,中间是集成测试,最上层是端到端测试。

  1. \
  2. / \
  3. / \
  4. / \
  5. / e2e \ (端到端测试)
  6. /---------\
  7. / \
  8. / integration \ (集成测试)
  9. / \
  10. /-----------------\
  11. / \
  12. / unit-test \(单元测试)
  13. / \
  14. ---------------------------

我们希望你在 KubeVela 项目中遵循 70/20/10 原则,即 70% 的单元测试、20% 的集成测试和 10% 的端到端测试。尽管每个模块有所差异,但是一般层级越高,测试覆盖率越大,测试用例越小。为了遵守金字塔模型,你需要避免以下几种情况:

  • 倒金字塔,全靠端到端测试
  • 沙漏模型,大量的单元测试和端到端测试,但没有集成测试

由于我们目前主要关注功能而无暇顾及测试稳定性,这使得我们很难维持测试的质量。但对于我们所有的 maintainer 来说,我们有责任追求高质量的测试,这样才能确保社区长期良好发展。

Last updated on 2023年8月4日 by Daniel Higuero