持续测试做错的 5 个迹象

在 DevOps 和敏捷时代,避免这些常见的测试自动化错误。
241 位读者喜欢这篇文章。
magnifying glass on computer screen, finding a bug in the code

Opensource.com

在过去的几年里,许多公司投入巨资来自动化生产环境中部署功能的每个步骤。测试自动化已被公认为关键的推动因素

“我们发现测试自动化是持续交付的最大贡献者。” – 2017 年 DevOps 现状报告

假设您已经开始采用敏捷和 DevOps 实践来加快您的上市时间,并尽快将新功能交付到客户手中。您实施了持续测试实践,但您正面临可扩展性的挑战:在所有系统级别为包含数千万行代码的代码库实施测试自动化,涉及到许多开发人员和测试人员团队。更复杂的是,您需要支持大量的浏览器、移动设备和操作系统。

尽管您投入了精力和资源,结果很可能是一个维护成本高、执行时间长的自动化测试套件。更糟糕的是,您的团队不信任它。

以下是五个常见的测试自动化错误,以及如何使用(在某些情况下)开源工具来缓解这些错误。

1. 孤岛式的自动化团队

在拥有数百甚至数千名工程师的中大型 IT 项目中,不可维护且昂贵的自动化测试最常见的原因是将测试团队与交付功能的开发团队分开。

这种情况也发生在遵循敏捷实践的组织中,在这些组织中,分析师、开发人员和测试人员共同制定功能验收标准和测试用例。在这些敏捷组织中,自动化测试通常部分或全部由 scrum 团队之外的工程师管理。如果想要随着时间的推移改进自动化测试套件,低效的沟通可能会迅速成为瓶颈,尤其是在团队地理位置分散的情况下。

此外,当在没有开发人员参与的情况下编写自动化验收测试时,它们往往与 UI 紧密耦合,因此脆弱且不良地分解,因为大多数测试人员不了解 UI 的底层设计,并且缺乏创建抽象层或针对公共 API 运行验收测试的技能。

一个简单的建议是拆分您孤岛式的自动化团队,并将测试工程师直接纳入 scrum 团队,在这些团队中进行功能讨论和实施,并且可以立即发现和修复对测试脚本的影响。这当然是一个好主意,但这不是重点。更好的是让整个 scrum 团队负责自动化测试。产品负责人、开发人员和测试人员必须共同努力,以完善功能验收标准、创建测试用例并确定自动化测试的优先级。

当开发团队内部或外部的不同角色参与运行自动化测试套件时,一种可以提升整体协作流程的实践是 BDD,或行为驱动开发。它有助于创建可以被整个团队理解的业务需求,并有助于为自动化测试提供单一的事实来源。诸如 CucumberJBehave 和 Gauge 等开源工具可以帮助您实施 BDD,并保持测试用例规范和测试脚本自动同步。这些工具允许您创建具体的示例,通过使用包含 Given-When-Then 场景的简单文本文件来说明业务规则和验收标准。它们被用作可执行的软件规范,以自动验证软件的行为是否符合预期。

2. 您的自动化套件大部分由用户界面测试组成

您应该已经知道,用户界面自动化测试是脆弱的,即使是很小的更改也会立即破坏所有引用特定已更改 GUI 元素的测试。这是技术/业务利益相关者认为自动化测试维护成本高昂的主要原因之一。诸如 SeleniumRecorder 等用于生成 GUI 自动化测试的录制和回放工具与 GUI 紧密耦合,因此很脆弱。这些工具可以在创建自动化测试的第一阶段使用,但需要第二个优化阶段来提供抽象层,以减少验收测试与被测系统 GUI 之间的耦合。诸如 PageObject 等设计模式可以用于此目的。

但是,如果您的自动化测试策略仅专注于用户界面,它将很快成为瓶颈,因为它资源密集、执行时间长,并且通常难以修复。实际上,解决 UI 测试失败可能需要您遍历所有系统级别以发现根本原因。

更好的方法是在正确的级别优先开发自动化测试,以平衡维护成本,同时尝试在软件 部署管道 的早期阶段发现错误(持续交付中引入的关键模式)。

 

agile_test_pyramid.png

正如上面显示的 敏捷测试金字塔 所建议的那样,绝大多数自动化测试应该由单元测试(后端和前端级别)组成。单元测试最重要的特性是它们应该非常快速地执行(例如,5 到 10 分钟)。

服务层(或组件测试)允许在 API 或服务级别测试业务逻辑,您不会受到用户界面 (UI) 的阻碍。级别越高,测试变得越慢且越脆弱。

通常,单元测试在每次开发人员提交时运行,并且在测试失败或测试覆盖率低于预定义阈值(例如,当单元测试覆盖的代码行少于 80% 时)的情况下,构建过程会停止。一旦构建通过,它将被部署在阶段环境中,并执行验收测试。任何通过验收测试的构建通常都可以用于手动和集成测试。

单元测试是任何自动化测试策略的重要组成部分,但它们通常不能提供足够高的应用程序可以发布的信心。服务和 UI 级别的验收测试的目标是证明您的应用程序可以满足客户的需求,而不是按照程序员认为的方式工作。单元测试有时可以共享这个重点,但并非总是如此。

为了确保应用程序为最终用户提供价值,同时平衡测试套件的成本和价值,您必须牢记敏捷测试金字塔,自动化服务/组件和 UI 验收测试。

有关测试类型、级别和工具的更多信息,请阅读 ThoughtWorks 的这篇综合性文章

3. 外部系统在您的部署管道中集成得太早

与外部系统集成是常见问题的来源,并且很难做好。这意味着仔细有效地测试此类集成点非常重要。问题在于,如果您将外部系统本身包含在自动化验收测试的范围内,您对系统的控制就会减少。很难设置外部系统的起始状态,这反过来会导致不可预测的测试运行,并且大部分时间都会失败。您剩余的时间可能都花在与外部提供商讨论如何修复测试失败上。但是,我们持续测试的目标是尽早发现问题,为了实现这一目标,我们的目标是持续集成我们的系统。显然,这里存在一种紧张关系,并且不存在“一刀切”的答案。

围绕每个集成点拥有一套测试套件,旨在在具有与外部系统的真实连接的环境中运行,这是有价值的,但测试应该非常小,专注于业务风险,并涵盖核心客户旅程。相反,考虑创建 测试替身 来表示与所有外部系统的连接,并在开发和/或早期阶段环境中使用它们,以便您的测试套件更快,并且测试结果是确定性的。如果您是测试替身概念的新手,但听说过 mocks 和 stubs,您可以在这篇 Martin Fowler 博客文章中了解它们之间的区别。

在他们的著作 《持续交付:通过构建、测试和部署自动化实现可靠的软件发布》中,Jez Humble 和 David Farley 建议:“当以下情况发生时,测试替身几乎总是必须用于桩化外部系统的一部分

  • 外部系统正在开发中,但接口已提前定义(在这些情况下,请为接口更改做好准备)。

  • 外部系统已经开发出来,但您没有可用于测试的该系统的测试实例,或者测试系统太慢或错误太多,无法充当常规自动化测试运行的服务。

  • 测试系统存在,但响应不确定,因此无法验证自动化测试的测试结果(例如,股票市场数据源)。

  • 外部系统采用另一个应用程序的形式,该应用程序难以安装或需要通过 UI 进行手动干预。

  • 您的自动化持续集成系统施加的负载以及它需要的服务级别,压倒了为应对少量手动探索性交互而设置的轻量级测试环境。”

假设您需要集成一个或多个正在积极开发中的外部系统。反过来,模式、合同等可能会发生变化。这种情况需要仔细和定期测试,以识别不同团队出现分歧的点。微服务架构就是这种情况,它涉及部署多个独立系统来测试单个功能。在这种情况下,请重新审视整体自动化测试策略,以支持更可扩展和可维护的方法,例如 消费者驱动合同 中使用的方法。

如果您不处于这种情况,我发现以下开源工具对于从 API 合同规范开始实施测试替身非常有用

  • SoapUI 模拟服务:尽管名称如此,但它可以模拟 SOAP 和 rest 服务。

  • WireMock:它只能模拟 rest 服务。

  • 对于 rest 服务,请查看 OpenAPI 工具 中的“模拟服务器”,这些工具能够从 OpenAPI 合同规范开始生成测试桩。

4. 测试和开发工具不匹配

将测试自动化工作卸载到开发团队以外的团队的后果之一是,它会在开发工具和测试工具之间造成分歧。这使得开发工程师和测试工程师之间的协作和沟通更加困难,增加了测试自动化的总体成本,并助长了不良做法,例如测试脚本版本和功能代码未对齐或根本未进行版本控制。

我见过很多团队都在努力使用昂贵的 UI/API 自动化测试工具,这些工具与 Git 等标准版本控制系统的集成性很差。其他工具,尤其是具有可视化工作流功能的基于 GUI 的商业工具,会产生一种错误的期望——主要是在测试经理之间——您可以轻松地期望测试人员开发可维护和可重用的自动化测试。即使这是可能的,它们也无法随着时间的推移扩展您的自动化测试套件;测试必须像功能代码一样进行管理,这需要开发人员级别的编程技能和最佳实践。

有几种开源工具可以帮助您编写自动化验收测试并重用开发团队的技能。如果您的主要开发语言是 Java 或 JavaScript,您可能会发现以下选项很有用

  • Java

    • Cucumber-jvm 用于在 Java 中为 UI 和 API 自动化测试实施可执行规范

    • REST Assured 用于 API 测试

    • SeleniumHQ 用于 Web 测试

    • ngWebDriver 定位器用于 Selenium WebDriver。它针对使用 Angular.js 1.x 或 Angular 2+ 构建的 Web 应用程序进行了优化

    • Appium Java 用于使用 Selenium WebDriver 进行移动测试

  • JavaScript

    • Cucumber.js 与 Cucumber.jvm 相同,但在 Node.js 平台上运行

    • Chakram 用于 API 测试

    • Protractor 用于 Web 测试,针对使用 AngularJS 1.x 或 Angular 2+ 构建的 Web 应用程序进行了优化

    • Appium 用于 Node.js 平台上的移动测试

5. 您的测试数据管理未完全自动化

为了构建可维护的测试套件,拥有创建和维护测试数据的有效策略至关重要。它需要自动迁移数据模式和测试数据初始化。

使用大型数据库转储进行自动化测试很有吸引力,但这使得难以对其进行版本控制和自动化,并且会增加测试执行的总时间。更好的方法是在 DDL 和 DML 脚本中捕获所有数据更改,这些脚本可以轻松地进行版本控制并由数据管理系统执行。这些脚本应首先创建数据库的结构,然后使用应用程序启动所需的任何参考数据填充表。此外,您需要增量地设计脚本,以便您可以迁移数据库,而无需每次都从头开始创建数据库,最重要的是,不会丢失任何有价值的数据。

诸如 Flyway 等开源工具可以帮助您根据数据库中包含其当前版本号的表来协调 DDL 和 DML 脚本的执行。在部署时,Flyway 检查当前部署的数据库版本和正在部署的应用程序版本所需的数据库版本。然后,它计算出要运行哪些脚本才能将数据库从当前版本迁移到所需版本,并按顺序在数据库上运行它们。

使您的自动化验收测试套件随着时间推移可扩展的一个重要特征是测试数据的隔离级别:测试数据应仅对该测试可见。换句话说,一个测试不应依赖于其他测试的结果来建立其状态,并且其他测试不应以任何方式影响其成功或失败。将测试彼此隔离使它们能够并行运行以优化测试套件性能,并且更易于维护,因为您不必按任何特定顺序运行测试。

在考虑如何为验收测试设置应用程序状态时,Jez Humble 和 David Farley 在他们的 书中 指出,区分以下三种数据很有帮助

  • 测试参考数据: 这是与测试相关但对被测行为影响很小的数据。此类数据通常由测试脚本读取,并且不受测试操作的影响。它可以使用预填充的种子数据进行管理,该种子数据在各种测试中重复使用,以建立测试运行的一般环境。

  • 测试特定数据: 这是驱动被测行为的数据。它还包括在测试执行期间创建和/或更新的事务数据。它应该是唯一的,并使用测试隔离策略来确保测试在不受其他测试影响的良好定义的环境中开始。测试隔离实践的示例是在测试执行结束时删除特定于测试的数据和事务数据,或使用功能分区策略。

  • 应用程序参考数据: 此数据与测试无关,但应用程序启动时需要此数据。

应用程序参考数据和测试参考数据可以以数据库脚本的形式保存,这些脚本在应用程序初始设置中进行版本控制和迁移。对于特定于测试的数据,您应该使用应用程序 API,以便系统始终通过执行业务逻辑(否则,如果您使用脚本直接将测试数据加载到数据库中,则会绕过业务逻辑)而处于一致状态。

结论

敏捷和 DevOps 团队在持续测试方面仍然不足——持续测试是 CI/CD 管道的关键要素。即使作为一个单一过程,持续测试也由必须协调工作的各种组件组成。团队结构、测试优先级、测试数据和工具都在持续测试的成功中起着至关重要的作用。敏捷和 DevOps 团队必须把每个部分都做好才能看到好处。

接下来阅读什么
User profile image.
Java 企业架构师、DevOps 负责人和敏捷产品负责人。我目前正在帮助世界各地的敏捷团队构建药房 IT 产品。在此之前,我为多家电信、公用事业、银行公司设计、开发和领导了 Java 企业关键任务系统。

3 条评论

嗨 Davide,
很棒且有趣的文章。
只是添加一些我在工作经验中看到的坏习惯
1. 为了避免 UI 测试,有时开发人员尝试使用 API 测试来编排底层服务来替换 UI 测试。如果可以确定 UI 不执行任何业务逻辑,则此方法可以工作,但是此方法涉及重用服务的响应作为第二个服务的输入。这种依赖链并不总是可管理的,并且在发生错误时,很难理解原因。
2 使用随时间变化的输入数据(例如今天的日期):这会使函数的输出根据其运行时间而变化。这使得函数的可测试性非常困难。

很棒的文章!一个小建议:也许您可以在测试管理部分提供一个带有实际示例的概念方案(以便更好地说明测试的这个关键部分)。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.