持续测试做错的 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 团队中进行功能讨论和实施,并且可以立即发现和修复对测试脚本的影响。这当然是一个好主意,但这并不是真正的重点。更好的做法是让整个 scrum 团队负责自动化测试。产品负责人、开发人员和测试人员必须共同努力,改进功能验收标准,创建测试用例,并优先考虑自动化。

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

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

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

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

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

 

agile_test_pyramid.png

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

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

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

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

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

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

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

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

在每个集成点周围都有测试套件,旨在在与外部系统建立真实连接的环境中运行,这很有价值,但测试应该非常小,侧重于业务风险,并涵盖核心客户旅程。相反,考虑创建 测试替身,以表示与所有外部系统的连接,并在开发和/或早期阶段环境中使用它们,以便您的测试套件更快,测试结果具有确定性。如果您是测试替身概念的新手,但听说过模拟和存根,您可以在这篇 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本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.