CI/CD 的自动化测试

持续集成和部署 (CI/CD) 的目的是使开发团队能够经常向用户交付行之有效的软件,从而在提供价值的同时,获得关于其产品在现实世界中使用情况的实用反馈。 许多组织已经接受 DevOps 做法,以此保持竞争力。

然而,更快交付的业务压力不应该降低产出的质量。 毕竟,无论用户多么渴望亮眼的新事物,稳定且行之有效的软件都是最低标准。 因此,对于持续集成和交付做法而言,可靠而透彻的自动化测试流程对打造值得信赖的最新构建至关重要。

为什么需要自动执行 CI/CD 测试?

测试是确保软件质量的关键,早已成为软件开发实践的一部分。

在瀑布环境中,手动测试或 QA 阶段开始于代码开发和集成之后,目的是检查应用程序是否符合规范。

这种线性方法会减慢发布流程,使开发者无法及时检查自己构建的内容能否按预期工作,浪费以其为基础进行构建花去的时间。 相比之下,CI/CD 自动化流程可实现短迭代周期的敏捷方法,提供快速反馈,并允许少量多次地发布更新。 测试是这些短迭代周期的关键部分,用于自动验证新代码是否有效和具有破坏性。

持续集成包括定期将代码更改提交到 master 分支或主干,如果适用则触发构建,并每次确保软件质量。 为了切实获得 CI 的好处,您的团队成员应尽可能每天都提交更改。 然而,即使在一个小团队中,手动执行这种程度的自动化测试也会对 QA 工程师提出相当高的要求,并涉及重复性非常高的工作。 这就是自动化测试的用武之地。

自动化测试是重复性任务的理想选择,并且产生的结果比手动测试更一致,因为当人们重复执行同样的步骤时,不可避免地会有遗漏细节或不一致地执行检查的风险。

自动化测试不仅比手动运行同等测试更快,还可以并行运行,因此在时间紧迫时,您可以扩展质量保证规模(在基础架构允许的范围内)。 虽然编写自动化测试涉及到前期的时间投入,但当团队成员开始定期提交更改并更频繁地发布到生产时,很快就能收回成本。

虽然自动化测试省去了许多枯燥、重复的任务,但这并不会让 QA 工程师团队无事可做。 除了定义相关用例并确定其优先级外,QA 团队还参与编写自动化测试,通常是与开发者合作。 无法自动化的部分也需要工程师,我们将在后面讨论。

测试在 CI/CD 流程的什么位置?

答案是,测试在整个管道的多个阶段进行。

如果您不熟悉持续集成和部署,这听起来可能有些过分,但 CI/CD 和 QA 自动化的重点在于紧密的反馈循环,让团队尽早发现问题。

在引入问题后的短时间内更容易修复问题,因为这样可以避免在不良基础之上编写更多代码。 这也让您的团队成员能够在进入下一步之前,更有效率地根据并未远离的环境做出更改。

许多自动化构建测试工具都支持 CI/CD 工具集成,因此您可以将测试数据馈送到管道,分阶段运行测试,并在每一步后提供结果。 基于您的 CI 工具,您还可以根据上一步的测试结果选择是否将构建移至下一阶段。

为了通过自动化测试充分利用管道,通常需要对测试排序,优先运行最快的构建测试。 这能让您更快地得到反馈,并更有效地使用测试环境,因为您可以确保在运行更长、更复杂的测试之前,初始测试已经通过。

关于如何优先创建和运行自动化测试,可以从测试金字塔的角度来考虑。

构建测试金字塔

测试金字塔是一种方便的方法,可以根据相对数量和执行顺序来概念化 CI/CD 管道中持续集成测试优先级的确定。

测试金字塔最初由 Mike Cohn 定义,底部为单元测试,中间为服务测试,顶部为 UI 测试。

虽然命名可能并不准确,但前提足够合理:先以快速且易于运行的自动化单元测试打下坚实基础,然后是编写更复杂和运行时间更长的测试,最后以少量最复杂的测试结束。 您应该考虑哪种类型的 CI/CD 测试? 我们来了解一下各个选项。

单元测试

单元测试构成了测试金字塔的基础。 这些测试旨在处理尽可能小的行为单元,确保代码按预期运行。 对于决定加强单元测试编写的团队,开发者通常会在编写代码的同时负责编写单元测试。 这在执行测试驱动开发 (TDD) 时会自动跟进,但 TDD 对于编写单元测试并非必要。

如果您使用的是现有系统,并且以前没有进行过单元测试,那么从头开始为整个代码库编写单元测试通常是一个难以逾越的障碍。 虽然使用单元测试实现广泛覆盖是推荐做法,但是您仍然可以从已有的基础开始,随着时间推移再行扩充。 现实的策略可以是将单元测试添加到您接触的任何代码段,从而确保覆盖所有新代码,并根据开发时与之交互的内容对现有代码进行优先级排序。

集成测试

通过集成测试,可以确保软件的多个部分按预期交互,例如一些应用程序代码和数据库之间的交互。 可以将集成测试分为广义和狭义。 在狭义集成测试中,与另一个模块的交互是使用测试替身而不是实际模块测试,而广义集成测试则使用实际的组件或服务。

根据项目的复杂程度以及所涉及的内部和外部服务的数量,您可能需要先编写一层狭义集成测试,这层测试比广义集成测试运行得更快(因为它们不需要系统的其他部分),再编写一套广义集成测试,针对系统中优先级较高的区域。

端到端测试

端到端测试也称全栈测试,着眼于整个应用程序。 虽然这些测试可以通过 GUI 运行,但不一定必须如此;API 调用也可以操作系统的多个部分(虽然 API 也可以用集成测试来测试)。 测试金字塔建议减少这些测试的数量,不仅是因为它们需要更长的运行时间,还因为它们往往较为脆弱。

用户界面的任何更改都可能破坏这些测试,导致构建测试结果中出现无用的噪声以及花费额外的时间来更新测试。 仔细设计端到端构建测试,并了解低级测试已经覆盖的内容,才可充分发挥其作用。

性能测试

尽管测试金字塔未涉及性能测试,但将其纳入自动化测试套件会有所帮助,特别是对于将稳定性和速度列为关键要求的产品。

性能测试领域具有一系列测试策略,用于检查您的软件在实际环境中的表现。 负载测试检查系统在需求增加时的表现,压力测试故意超出预期使用量,而浸泡(或耐久)测试则衡量持续高负载下的性能。

这些类型的测试的目的不仅仅是确认软件能否处理定义的参数,还要测试在超出这些参数的限度时的表现,理想情况是优雅地结束,而不是立即崩溃。

测试环境

性能测试和端到端测试都需要与生产非常相似的环境,还可能需要构建测试数据。 为了使自动化测试制度能够增强被测软件的可靠性,每次构建测试都必须以相同的方式运行,这包括确保测试环境在运行之间保持一致(但是在发生更改时,应将其更新以匹配生产)。

手动管理环境可能是一项耗时的工作,因此值得考虑在每个新的构建中自动化创建和拆除预生产环境的步骤。

处理反馈

作为 CI/CD 做法的一部分,运行自动化测试的目的是获得对刚刚做出的更改的快速反馈,因此听取并响应这些反馈至关重要。

CI 服务器通常与自动化测试工具集成,因此您可以将所有结果集中在一处。 开发团队通常会将最新结果的仪表板或辐射器显示与自动通知沟通平台(例如 Slack)相结合,使其了解最新构建的表现。

当测试失败时,了解测试所涉及的代码库区域并查看测试所产生的任何信息(例如堆栈跟踪、输出值或屏幕截图),可以加快找到根本原因的过程。 值得花时间仔细设计测试,让每一个测试都能检查一个方面并明确标记,这样您就能明白失败的原因。 持续集成测试和 CI 工具提供了与测试失败相关的额外信息,也可以帮助您更快地恢复构建。

与以往一样,工具和实践只是方案的一部分。 一个真正优秀的 CI/CD 自动化做法需要一种团队文化,这种文化不仅要认可自动化 CI/CD 测试的价值,还要认识到快速响应失败测试的重要性,使软件保持可部署状态。

CI/CD 和自动测试是手动测试的终结者吗?

在那些刚接触 CI/CD 的人中,一个常见的误解是,自动化测试抹除了对手动测试的需要,也抹除了对专业 QA 工程师的需要。

虽然 CI/CD 自动化为 QA 团队成员腾出了一些时间,但并没有使他们成为多余的人。 工程师可以专注于定义相对用例、编写自动化测试,并将创造力和独创性应用于探索性测试,而不必将时间花在重复性任务上。

与经过脚本精心编写以供计算机执行的自动化构建测试不同,探索性测试的要求较为宽松。 探索性测试的价值在于发现计划性、结构化测试所忽略的方面。 基本上,您要寻找的是尚未考虑过和尚未编写过测试用例的问题。 在决定探索区域时,既要考虑新功能,也要考虑如果在生产中出现问题会造成最大危害的系统区域。

探索性测试不应成为手动重复性测试;它的目的是不要每次都进行相同的测试。 在探索性测试期间发现并修复问题时,要花时间编写自动化测试,以便再次发生时可以尽快将其捕获。 为了有效利用测试人员的时间,只有在所有自动化测试都通过后才应进行手动测试。

测试自动化的持续改进

自动化测试在任何 CI/CD 管道中都扮演着核心角色。

虽然编写自动化测试需要投入时间和精力,但快速反馈和看得见的代码可部署性益处意味着自动化测试很快就能收回成本。 但是,测试套件的构建不能只做一次就放到一边。

您的自动化构建测试应与其余代码一样构成应用程序的一部分,需要进行维护以保持相关性和实用性。 因此,您应用于代码的持续改进也适用于您的测试。

不断为新功能构建自动化测试,并馈入探索性测试的结果,将使您的测试套件保持快速和有效。 您也应当花时间了解测试表现,思考是否应该重新排序或分解工作中的步骤,以更快得到一些反馈。

CI 工具可以提供各种指标来帮助您优化管道,不稳定测试指示器可以标记出不稳定测试,避免让您产生错误的信心或担忧。 但是,尽管指标可以帮助您改善自动化测试流程,但要避免认为测试覆盖率本身就是目标。 真正的目标是定期向您的用户提供行之有效的软件。 自动化通过提供快速、可靠的反馈来实现这一目标,让您可以放心将软件部署到生产。