我们做了大量工作,可自动化 UI 测试依旧实现不了

发表于:2019-10-09来源:infoq作者:Steven Lemon点击数: 标签:
对开发者而言,测试的重要性不言而喻。在发布新功能前,开发者需要确保已有功能有效,这就需要将每个发布版本给到 QA 团队执行人工回归测试。然后,测试人员或 QA 团队花费数天时
为什么需要自动化 UI 测试
随着时间的推移,软件增加的新功能会越来越多,脚本规模越来越大,执行人工测试的时间越来越长。对于人工测试的依赖开始变得棘手,因此,开发人员开始寻找替代方案,自动化 UI 测试开始被注意,这会用自动化框架代替人工继续运行相同的回归测试脚本。毕竟,人工测试的缺点已经非常明显,比如:
人工回归测试是一项繁琐的工作,每个人都乐意看到替代方案;
自动化 UI 测试解放了 QA 团队针对临时的和探索性案例的测试时间;
人工回归测试需要花费很长时间才能完成,很小的延迟就会让发布面临风险。也许需要重新测试,或者开始时间被向后推迟几天,或者回归环境需要同时给 2 个不同的发布版本共享;
发布节奏受到人工回归测试的限制。两天以上的人工回归测试意味着最好的情况下能够一个月发布两次。而且,开发者需要一次性发布所有东西。要么全部发布,要么什么都发布不了,因为需要将所有东西一起测试;
自动化测试是可视化的,可以在特定设备上运行,并展示给用户;
自动化意味着可以一边开发一边进行回归测试,减少等待时间。
当然,即便人工测试有很多缺点,手工编写自动化测试也不是唯一的解决方案,购买一款商业工具也可以创建并管理测试。或者,也许框架自带一个内置的自动化方案。那么,这篇文章不适合你。或者,你可以考虑使用 Selenium 或者 Appium 之类的工具来手写测试。但是,经过数月的尝试,我们团队决定放弃其他的测试方式,因为那些被证明对于我们的测试套件、架构设计和期望来说并不是一个好的选择。在这个过程中,我们吸取了很多教训,遇到了许多本应该提前考虑到的问题。
遇到了哪些问题?
应用程序架构
根据应用程序的组织结构以及增长趋势,你可能会发现自动化需要花费不太合理的时间来设置。
UI 自动化是编写测试步骤的一部分,也是设置测试基础设施的一部分。如果遵循 Page Object Model 模式,那么你会为应用程序中的每个页面和控件创建模型,因而测试可以找页面或控件上的元素并与之交互,需要编写的基础设施代码量取决于项目本身。你是否有一些页面有许多不同的输入,或者许多工作流分布在许多特定的页面上?是否有一个可复用的控件库,还是每个控件都随着 UI 的变化而定制?在此之前,你开发应用程序的方式决定了需要花费多少精力来编写测试基础设施。反过来,这也影响了需要多长时间来编写自动化 UI 测试。
预期效果
在开始之前,要想清楚你需要自动化测试的目的,确定好预期效果。如果是对回归和生产中先前发现的 Bug 进行记录,你期望用自动化 UI 测试捉到其中的多少 Bug 呢?
有许多情况,UI 测试是发现不了的,比如:
不在人工回归脚本路径内的问题。如果不是在测试步骤中显式包含的 Bug,多久会被发现;
功能及其测试都不正确的时候;
边缘情况或者不常见场景的 Bug;
单元测试和集成测试中能够捕获到的 Bug;
在应用程序中无法看到结果的任何操作。避免只是为了自动化测试而隐藏应用程序中的数据;
可视化错误;
性能问题;
任何太复杂和难以自动化的测试案例。
自动化测试在流程中扮演什么角色?它们是如何支持 QA 团队和回归流程的?也许,你的目的是解放 QA 的时间,而不是发现 Bug。你可以跳过 QA 能够覆盖的区域,执行更详尽的临时性和探索性测试。你期望自动化 UI 测试能够发现的内容,应该告诉你选择包含哪些部分以及计划为多少部分编写测试。
期望的回报
比较编写测试的时间和可能节省的时间是比较容易的。比如自动化一个功能可能会花费 200 个小时,而这为每次发布带来的时间节省可能是 20 分钟。所以,在开始之前,开发人员需要想清楚期望得到的回报,以及愿意为此花费的时间。
负责编写测试的人员
你可能会期望通过使用 Page Object Model 模式,开发者可以编写测试基础设施来给 QA 用来编写测试。我们的经验并不是这样的,开发者需要同时编写基础设施和测试。
测试基础设施可能并不能在多个测试用例间复用。如果不复用,你会需要一边编写支持代码一边编写测试代码;
编写测试可能需要对应用程序做许多更新;
自动化框架并没有提供足够多的信息来知道测试失败是由于基础设施还是测试用例;
如果 QA 团队缺乏编码或自动化经验,你可能很难让这个框架易用;
测试用例需要太多应用程序内部的知识;
测试的脆弱性导致开发人员需要不断修复测试基础设施;
当践行自动化测试理念时,请让所有有意扩展和维护测试的人参与进来。确保你正在做的事情适合他们的技术栈并且理解你的应用程序。
干净的,用于测试的数据集
刚开始的时候,你可能使用平时开发所用的同一个数据库。然而,不久之后,你就会花费越来越多的时间来处理数据集。
你需要发现先决条件而不是预先设置好,或者它们应该很容易创建;
随着更多的数据被创建,你的 UI 会发生变化。例如,额外的数据将一个元素挤出页面,测试用例会失败,因为它们不能与之交互;
同一个测试可能会在同一分钟内重复运行。你需要检查一个元素是由当前测试创建的还是由先前测试创建的;
测试用户遇到了预料之外的状态而导致测试用例失败,需要人工干预或者测试用例预先筛选处于每个无效状态的用户;
对数据集的彻底更改会改变你一直使用的数据。例如,你可能定期清除开发数据库或者从另一个系统导入数据来刷新;
并行的测试运行或者开发者使用同一个数据库导致的意外交互和测试失败;
针对多个环境运行测试用例。在开发时,针对 dev 数据库而在验收时针对回归环境。
每一个测试都有各种需要设置的先决条件。依赖自动化测试来安装它们的先决条件会将每一个测试转变成一条长动作链。这些额外的步骤不仅会使测试的编写和运行速度更慢,还会使测试更加脆弱,而且使跟踪失败点更困难。使用干净的数据集,可以拥有已知的测试条件和测试用户,类似于如何在单元测试中使用母对象。
开发人员想要一个可以重置并填充固定数据的数据库。如果还没有这种数据库,那么将需要大量新的基础设施:一个新的数据、一个填充合法测试数据的工具、指向新数据库的 API 以及用于部署这个环境的构建管道。
UI 框架和组件
每一个测试都需要考虑 UI 组件能做的所有事情,比如,不能在测试期间重置数据库等。
我们点中某个元素,则测试通过;
后续运行向这个列表增加更多选项,将目标挤出屏幕,我们需要更新测试在点击之前跳到这个元素;
这个列表变得如此长以至于 UI 虚拟化,目标不再存在于页面上,不能再跳到这个元素,而是需要缓慢滚动来在列表中搜索目标;
列表中的重复显示,需要指出哪个元素是当前测试的目标;
另一个元素变大,使整个列表处于屏幕之外。在与之交互前,需要先滚动到这个列表;
之前运行的测试由于失败没有完成,而留下测试实体处于隐藏整个列表的状态。
自动化测试经常失败
自动化测试经常失败,而且通常,并不知道失败的原因,因为可能的情况有很多,比如:
使用的自动化框架不能获取屏幕上的元素;
自动化框架不能识别应用程序是否已经启动;
测试驱动不能连接;
遇到了一个 UI 组件的边缘情况;
一个元素被挤出屏幕,自动化框架不能与之交互;
测试在不同的屏幕尺寸和分辨率上的运行不同,因为不同的元素可能在屏幕上,也可能不在屏幕上;
每次测试运行,没有一个干净的、独立的数据实例而导致前面提到的所有问题。
我们使用 Appium 和 WinAppDriver,而对于大部分失败测试,我们得不到有用的错误信息,没有日志和异常跟踪栈,比如因为一个元素找不到而导致测试失败,但是我们没办法知道是哪个元素找不到。更糟糕的是,由于失败是断断续续的,而且可以是特定设备或环境,因此需要花费很长的时间来确定失败原因。
解决测试不稳定性的一种方案是运行每个测试直到它通过。这引起了一些问题:测试周期更长;更难从测试中及时获取反馈等。其次,这使得编写新的测试用例更困难,而且可能要等待 10 分钟或更久来测试单个变化。理想情况下,持续跟踪测试用例,并将收到的模糊错误信息分组。当没有可用日志时,知道测试用例什么时候开始是一个很关键的线索。
为了跟踪测试的不稳定性,我们维护了一长串可能导致错误的因素,这包括测试套件和应用程序中所有的边缘测试用例和 UI 交互。创建这个列表不仅花费很长时间并需要很多尝试和错误,而且还增加了其它开发者共享测试套件的学习曲线。
重构困难
自动化 UI 测试很难重构。测试运行可能需要数个小时,使得很难获取反馈并进行修正。一些测试可能严重依赖精心安排的时间点,一旦偏离则可能发生任何事情。
由于自动化测试对团队来说可能比较陌生,你需要面临由于开发者尝试找出且争取在测试用例中应用的最佳策略而导致的多种不同方案。拥有不同的方案使得参与项目的新人很难分辨哪个是最佳方案。每当对应用程序的 UI 做出改变时,都会产生后果。你可能发现自己需要修改大量自动化 UI 测试,每一个都需要不同的实现。
人的因素
当引入一个新工具、技术或者流程到一个团队,需要考虑各种人的因素:
使用新工具的体验如何?是沮丧还是缓慢?
有人愿意成为新技术的捍卫者吗?他们离开了,谁来接管?
当工具延迟时怎么办?当你没有时间时,自动化测试会被放弃么?业务能容忍为了给新功能增加自动化测试的额外时间吗?
如果这个工具在团队中的声誉不好怎么办?
每一个人都认同编写自动化测试的价值,还是认为这是在浪费时间?
正如已经总结的,关于这些测试的价值有很多潜在痛点和问题。如果没有解决办法,你的测试套件不可能持续很久。
注意事项
也许,创建自动化 UI 测试看起来不是一个有吸引力的选项。然而,你也不想花费很长时间来进行人工回归测试,那么有哪些其它选项吗?
不要尝试自动化所有人工回归脚本
事实上,对于自动化测试所能涵盖的范围,并不是原来所用的全部人工回归套件,因为有些组件太过复杂或者太耗时间,是不值得进行自动化的,比如:
长链动作无法拆分。UI 测试的不可靠性使得通过一次运行完成所有测试具有挑战性;
测试与其他的应用程序交互;
检查 PDF 和其它生成文件的输出;
Windows 系统或者 Windows 文件系统交互的测试任务;
后续运行将有不同结果的测试:测试可能会被之前运行的测试结果影响。人工回归可能两周发生一次,而自动化 UI 测试可能一分钟或一小时重试同一个测试许多次,增加了冲突的机会。
如果测试运行失败或者半途崩溃可能导致应用程序处于不一致的状态。这时,或许需要人工进行干预来修复。
如果对应用程序某个部分中显示的数据没有足够的控制,则很难设置测试的先决条件。测试人员是否必须在应用程序中寻找匹配的数据,而不是创建数据或者直接导航到相应的场景?
注意:别太教条,没必要强制在不适合的场景中使用 UI 自动化测试。这些测试不仅很难编写,也很不可靠并很难维护。在你开始之前,最好认识清楚哪些部分可以自动化,任何将测试自动化的开发者都有说“不”的自由。
先补充测试金字塔的其它部分
任何单种测试不能提供百分百的覆盖率。你想要各种级别特异性和独立性的测试,金字塔底层的众多特定独立的测试。然后,越来越少的测试变得更不具体且覆盖的应用程序更多。单元测试在底层,然后是集成测试,再然后是端对端测试。
金字塔的每一层都协调配合,拥有不同的优势和弱势。
如果可能,我们将在单元测试和集成测试覆盖尽可能多的部分。这些测试易于编写,提供更多详细反馈,而且可以在开发期间运行。单元测试更适合覆盖边缘测试案例和错误场景。根据应用程序,自动化 UI 测试可以覆盖 UI 逻辑,而单元测试可能不能覆盖。自动化 UI 测试还可以保证应用程序中的多个部分如预期那样工作。
没有一种测试可以提供完全的测试覆盖。如果已经有单元测试和集成测试,可能已经覆盖了人工回归测试脚本的步骤。编写完全的自动化 UI 测试来覆盖已经覆盖的东西是价值不大的。与其用自动化 UI 测试完全取代人工回归测试,为什么不用其他各种类型的测试的结合来取代它们呢?
其他选择
你是编写 UI 自动化来测试 UI,还是用来促进端对端测试?如果不需要测试 UI 层,那么“皮下”测试(subcutaneous testing,指在 UI 层之下执行的测试)可能是一个更好的选择。这个方案允许在 UI 层之下执行端对端测试。与其点击按钮或者填写文本框,开发者可以调用事件处理器并直接在视图模型上设定公开属性。这个方案避免了与 UI 交互和使用自动化框架的困难。这个方案的缺点是,根据应用程序使用的技术,可能没有太多特定的指南。我们的应用程序是用 UWP 编写的,因此不得不自己找方法在模拟 UI 的测试框架中运行。一旦开始生效,它会被证明比自动化 UI 测试更快且更易用。
结束语
UI 自动化测试的潜在优势令人兴奋:发现 Bug,解放 QA 时间,消除人工回归测试,开发者在过程中获取反馈。然而,与任何新技术一样,需要一些提前调查。在开始手动编写自动化人工回归测试套件之前,上面已经提出了一些问题。期望发现的回归 Bug、应用程序的架构或期望编写和维护测试用例的人可能都不是很适合。这个过程存在一些挑战:处理不可靠数据、UI 可能做的工作要比预期多、自动化框架的不稳定性和糟糕的错误信息,谁负责编写测试用例以及谁在进展不顺利时提供支持。最后,是否已经将手工编写测试与其它商业产品和写更多集成测试、单元测试的方式进行了比较,或者与编写“皮下”测试进行了比较。

原文转自:https://medium.com/@steven.lemon182/our-teams-troubles-with-hand-written-automated-ui-tests-cb189cbbff90