lsekfe 发表于 2020-7-14 14:02:40

单元测试的五个主要准则

 自动化测试是所有大型软件项目不可或缺的一部分。它是提高质量、生产力和灵活性的一种手段。 因此,对系统架构进行合理地设计以便利后续的开发和自动化测试变得至关重要。
  自动化测试的好处
  1. 质量得以提高
  因为自动化测试让我们能在开发阶段早日发现并解决问题,这避免了在变更部署到生产环境并提交给最终用户使用时发现问题。
  2. 生产力得到提高
  因为在开发周期中发现问题的时间越早,修复该问题的成本越低,这不言而喻。如果软件开发人员能够在将代码集成到主代码仓库前运行自动化测试套件,那么可以快速发现新引入的 bug 并将其修复。但是,如果没有这样的测试套件,那么新引入的 bug 可能仅在最终用户使用测试阶段中出现,甚至出现更晚,这会导致开发人员暂停常规开发工作流程来对 bug 进行调查和修复,影响项目进度。
  3. 灵活性得到改善
  有了测试套件的帮助,开发人员在进行代码重构、升级代码依赖包及修改系统特性时会更有信心,因为测试套件有非常高的测试覆盖率,可以方便地评估代码变更带来的影响。
  在讨论自动化测试时,我也喜欢将风险管理的话题引入进来。作为首席软件工程师,风险管理是我工作的重要组成部分,它涉及指导开发团队进行工作和流程管理,减少产品技术退化的风险。 从上面列出的好处中可以明显看出,进行充分的自动化测试非常必要,这可以帮助减轻软件项目中的风险。
  自动化测试类型
  接下来,我们可以根据实现和运行自动化测试的策略将其分为至少三种不同类型,如下图著名的测试金字塔所示::
从时间和资源使用而言,单元测试的开发及运行成本低,并且单元测试专注于测试与外部依赖项隔离的单个系统组件(例如,业务逻辑)。
  集成测试向前更进一步,并且在不隔离外部依赖关系的情况下进行开发和运行。在这种情况下,我们有兴趣评估所有系统组件构建在一起并面临集成约束(例如:联网、存储、处理等)时是否按预期进行交互。
  最后,在金字塔的顶端,GUI 测试是整个自动化测试中代价最高的。他们通常依靠 UI 输入 / 输出脚本以及回放工具来模仿最终用户与系统图形用户界面的交互。
  在本文中,我们将重点介绍测试金字塔的基础——单元测试,以及采用单元测试的系统体系结构在构建时的注意事项。
  有效单元测试的属性
  首先,让我们说明一下什么是有效的,设计良好的单元测试。
  简短——只有一个测试目的
  简单——设置及拆卸方便
  快速——可以快速执行
  标准——遵循严格的约定
  理想情况下,单元测试应具有所有上述这些属性,下面将详细说明原因。
  如果单元测试不够简短,将很难阅读并理解其目的,确切地说是很难理解测试内容。因此,出于这个原因,单元测试应该有一个明确目标,并且只评估测试一件事,而不是尝试同时执行多个测试目的。这样,当某个单元测试失败时,开发人员将更加轻松快捷地定位问题并进行修复。
  如果单元测试需要大量精力来设置他们的测试环境,然后将其拆除,那么开发人员通常会开始质疑,花费在编写这些测试上的时间是否值得。因此,我们需要提供一个编写单元测试的环境,该环境要管理测试上下文的所有复杂性,例如依赖注入,数据预加载,缓存清除等。编写单元测试越容易,开发人员创建它们的动力就越大!
  如果执行一组单元测试需要花费大量时间,则开发人员自然会减少执行频率。这里的问题在于拥有如此冗长的单元测试套件变得不切实际,开发人员会跳过运行单元测试或有选择地运行,从而降低了其有效性。
  最后,如果测试没有一定的标准,不久之后你的测试套件开始看起来像未拓荒的美国西部一样,编写单元测试所使用的编码风格有时会有所不同,甚至会发生冲突。因此,在整个单元测试的范围内追求系统设计的连贯性在整个系统中都是有价值的。
  一旦我们对有效的单元测试的架构达成共识,就可以开始定义提升其性能的系统架构准则,如以下各节所述。
  一、软件复杂度
  除其他因素以外,软件复杂度还源于系统内组件之间不断增加的交互及其内部状态的演变。随着复杂度的提高,无意识地干扰复杂的组件交互网络的风险也随之增加,这可能导致在代码变更时引入缺陷。
  此外,通常情况下,系统的复杂性越高,维护和测试就越困难,这引出第一个(一般)准则:
  密切关注软件的复杂度并遵循设计原则来控制它
  在提高测试性能的同时管理复杂性的方面,值得一提的一个实践方法是,在系统设计中尽可能采用纯函数和不变性。 纯函数是具有以下属性的函数:
  对于相同的参数,其返回值是相同的(不随局部静态变量,非局部变量,可变引用参数或来自 I/O 设备的输入的变化而变化)。
  它的评估测试不会产生副作用(局部静态变量,非局部变量,可变引用参数或 I/O 流不会因测试受到影响)。
  从其属性可以明显看出,纯函数非常适合单元测试。它们的使用也消除了许多补充性实践的需求,这些补充性实践将在以下各节中讨论,以处理大部分为有状态的组件。
  不变性起着同等重要的作用。不可变对象是创建后状态无法修改的对象。它们更易于交互和具有可预测性,从而有助于降低系统复杂性,消除全局状态。
  二、依赖隔离
  按照单元测试定义,单元测试旨在隔离测试各个系统组件,因为我们不希望组件的单元测试结果受到其依赖项的影响。隔离程度会根据被测组件的具体情况以及每个开发团队的偏好而有所不同。我个人不担心隔离轻量级的内部业务类,因为我发现,用功能几乎相同的测试组件替代它们不会显示有什么附加影响。这里的策略可能很简单:
  在组件设计中应用依赖反转模式
  依赖反转模式(DIP)指出,高级和低级对象都应依赖抽象(例如接口),而不是特定的具体实现。一旦将系统组件从其依赖关系中解耦出来,我们就可以在单元测试的上下文中通过简化的、针对测试的具体实现轻松地替换它们。下面的类图可以展示这种结构:


lsekfe 发表于 2020-7-14 14:11:49

剩余内容请各位下载文档观看。

szc123qq 发表于 2020-7-14 15:32:22

:time:
页: [1]
查看完整版本: 单元测试的五个主要准则