如果你有自动的端到端测试,那么你就已经拥有了测试架构,但你该如何确保测试架构实现自动测试工作的目标呢?
本文中,我们将跟随作者以一家视频会议解决方案公司——Neat公司为视角,探讨在测试架构构建中的选择。Neat 公司的测试架构涵盖从代码到企业架构等多方面,虽其细节(如嵌入式软件和Python 端到端测试)可能与大家有所不同,但他们思考的问题对所有自动测试团队都有一定的参考价值。Neat采取措施来捕获回归、减少误报、编写易维护测试、理解结果,来帮助开发团队高效工作,从而为公司、客户和开发团队创造更高的价值。
一、测试架构:不仅仅是测试代码
测试架构,简单来说,是构建工件(包含测试代码、框架、工具等)与人工元素(像团队组织、流程和实践)相互融合的综合体,它处于软件架构和企业架构的交汇点上。在我看来,其根本目标就是稳固地创造价值并持续保持。
我发现许多团队都未能将测试与价值创造建立起有效的联系。他们未能针对测试工作进行精准化的定制,没有为开发团队打造出一张能主动且即时用于验证更改的安全网,从而使开发团队难以满怀信心地快速果断地推进工作。
从公司的角度来看,测试架构直接关乎公司的声誉。测试的不眠夜来自于你漏掉了什么,经过深思熟虑而精心构建的测试架构,可以减少未捕获的缺陷数量。这一点很重要,因为那些没有抓住的缺陷会逐步削弱公司的声誉,最终对业绩造成损害。
▏核心原则:权衡与目的导向
1.权衡:寻找最佳平衡点
测试架构中的每一项决策都要求在不同优先级之间达成平衡。以某团队为例(可类比实际情况),其使用的工具使团队能够聚焦于测试趋势。当报告工具显示单个未通过的端到端测试之前存在显著的通过趋势时,该团队通常会降低对这一单个失败测试的调查优先级。
如此一来,开发人员可能会在数天内才收到有关其在夜间构建中引入缺陷的反馈,而非立即获取。然而,这种权衡背后的逻辑在于,通过选择不针对每个测试失败进行深度剖析,转而关注整体趋势,团队所面临的干扰得以减少,上下文切换频率降低,从而能够将精力集中于更为重要的事务上。最终,这种方式能够更有效地捕获缺陷,减少流向客户的缺陷数量。
2.“为什么”而非 “如何”:明确决策背后的目的
在测试架构领域,理解并清晰传达决策选择背后的目的至关重要,特别是在偏离常规既定做法时。例如,测试架构师在倡导使用特定代码结构时,必须始终明确自身期望达成的目标。假设某种代码结构能够带来类型安全保障,在类型安全问题不太可能凸显的特定情境下,架构师应秉持开放态度,避免过度使用该结构而徒增复杂性。
在实际操作中,当架构师在代码审查环节提出与团队惯常做法不符的解决方案,进而给出看似矛盾的建议时,团队成员往往会产生困惑或不安情绪。此时,明确阐释 “为什么”,即解释倡导该做法的原因,就显得尤为关键。这有助于团队成员理解其意图,确保整个团队朝着共同的目标前进,避免因误解而产生的不必要的抵触情绪,从而保障测试架构的有效构建与优化。
二、Neat公司的测试架构
▏组织结构:专业团队与协作模式
Neat 公司设立了专门的测试自动化团队,专注于测试自动化相关领域的工作。作为一家专注于视频会议解决方案的成熟初创公司,Neat 约在三年前启动了测试自动化进程。
起初,团队致力于扩大产品现有功能的测试覆盖范围,同时着力打造一系列框架、工具及流程。在这一过程中,公司内部的各学科开发团队(涵盖音频、视频、平台等多个领域)在开发新功能时,逐渐认识到这些框架、工具和流程所带来的附加价值,并开始积极采用。这使得学科团队成员能够凭借自身的领域知识,运用测试自动化团队提供的工具编写新的测试,而测试自动化团队则主要通过对其他团队编写的测试代码进行代码审查的方式提供专业指导,双方紧密协作,共同推动测试工作的高效开展。
▏测试自动化的演变:从基础到协作
Neat的测试自动化经历了一个有趣的演变过程。最初,他们从基本覆盖率起步,就像盖房子先打好地基一样。随着时间的推移,逐渐过渡到协作框架。在这个框架下,功能团队能够更加自主地参与到测试编写中来。他们利用测试自动化团队提供的丰富工具,结合自身对业务功能的深入理解,编写针对性强、高效的测试。这种演变不仅提高了测试的效率,还增强了整个团队的协作能力,使得测试工作不再是测试团队的单打独斗,而是全员参与的一场战斗。
▏框架和 CI 基础设施:强大的技术支撑
-
测试框架:
Neat 公司选用 Python Behave 作为主要的测试框架。在测试自动化团队成立初期,便精心构建了大量可复用的测试步骤实现,这些步骤涵盖了在视频会议设备上执行测试时所需的大部分基础操作。因此,当功能团队中的开发人员着手为新功能编写测试时,往往发现所需的大部分测试代码已然就绪。他们只需在 Gherkin(given - when - then)测试用例中引入现有步骤,然后集中精力开发与新功能特定内容相关的少量剩余步骤即可。这种方式极大地提升了开发人员创建新测试的效率,使其在测试编写过程中事半功倍。
-
CI 基础设施:
Neat构建了一套简洁易用的持续集成(CI)基础设施,用于执行测试、生成报告并跟踪结果。公司始终将重点置于创建易于使用且可复用的组件之上,这些组件通过便捷快速地开发新测试任务,为整个测试流程创造了显著价值。在实际操作中,Neat借助 Gitlab CI 来运行测试,当创建新作业时,用户可继承预设的配置,这些配置能够自动处理诸如使用新固件刷新设备、执行测试以及生成报告等关键任务,这仅仅是可复用基本配置所实现的部分核心功能。
-
报告机制:
在报告环节,采用 Elasticsearch 作为数据存储工具,团队成员可以方便地复制公司定制的 Kibana 仪表板,并通过添加筛选器,使其仅跟踪团队关注的特定测试结果和趋势,从而实现个性化的测试结果分析与监控。
三、测试的战略方法:精准出击
尽管我们将主要精力聚焦于夜间构建工作,但在测试策略上,我们选取了一组数量最少的门控测试,我们将其命名为 “基本” 子集。
随后,我们还会执行数百个功能测试,这些测试并不属于门控类型,因此不会阻断持续集成(CI)流程中的软件构建进程。这是因为,尽管我们已经竭尽全力,但端到端测试不可避免地会受到众多变量的干扰。
基于此,我们着重于在每晚尽可能多地运行测试。一旦测试过程中发生故障,我们能够迅速恢复设备状态,并即刻开展下一次测试,确保不会因之前的测试失败而阻碍后续测试的正常进行。
与此同时,我们配备了专门用于分析限定趋势的工具,以此精准过滤干扰信息,有效识别实际存在的问题。为此,我们构建了一套专门的框架,用于对每个测试用例的趋势进行精准限定,并将该测试用例在 30 天内的趋势存储为存储于 Elasticsearch 文档数据库中的每个测试结果的一项属性。这种趋势限定以两种形式呈现:一种是带有红色(代表失败)和绿色(代表通过)趋势带的直观图形,另一种是文本分类形式,如 “间歇性失败”“持续通过” 或 “片状” 等类别。其中,后一种文本分类是通过一个基于 Python 编写且受函数式编程启发的框架来实现的,在该框架中,每个类别均由一个特定函数负责识别判定。当其中某个函数返回匹配结果时,相应的测试结果便会被赋予对应的类别标识。结果在我们的控制面板上如下图所示:
四、为开发团队创造价值:全方位支持
如前文所述,我们为特性团队构建了一个框架,此框架极大地方便了新测试的编写工作,并且能够使这些新测试在持续集成(CI)环境中迅速启动并运行起来。这仅仅是作为测试自动化团队为功能开发团队创造价值的一个方面。
另一个极具代表性的例子是我们开发的聊天代理工具。该工具赋予了 QA 团队成员(特别是开发人员)一项强大的能力,即可以订购仅针对特定域或功能集的定制化测试运行。这些定制测试运行具有高度的灵活性,能够针对任何构建版本执行,其中包括功能团队开发人员的个人构建版本。
在完成测试运行后,我们会提供全面且详细的报告,这些报告具备一项重要功能,即能够自动对已知问题进行分类整理。如此一来,开发人员或测试人员便可以快速且准确地区分预期失败(例如某些特定场景下正常会出现的错误情况)和那些与即将合并到主分支的更改相关的问题,从而更加高效地定位和解决问题,提升开发效率。
五、测试自动化中的机器学习:智能助力
在测试自动化流程中,机器学习发挥着重要作用。当开发人员订购自定义测试运行,以对其考虑集成的更改进行验证时,他们会获取一份详尽的报告。该报告能够清晰地区分预期的失败情况以及其更改可能引发的问题。不仅如此,开发人员甚至可以对背后的机器学习模型进行指示,使其在下次因正在验证的代码更改而出现新发现的问题时,自动执行分类操作。值得一提的是,这一操作可以直接在开发人员用于查看自定义测试运行结果的同一报告工具中完成。
如此一来,作为测试自动化团队,我们得以避免采用极为不理想的解决方案,即无需关闭那些预计会失败的测试。与此同时,我们依旧能够为开发人员提供清晰可读且具有实际操作指导意义的测试结果。由于我们持续跟踪测试趋势,因此当定期失败的测试和回归问题得到修复时,开发人员能够自动收到相关警报。
倘若我们在 6 个月后才启动与机器学习相关的计划,或许可以选择借助大语言模型(LLM)来承担繁重的工作任务。然而,在项目开始,尽管我们使用的是成熟且易于操作的库,我们仍不得不自行构建模型。我们的首个计划聚焦于对测试失败的类型进行分类,虽然取得了一定程度的成功,但并未对我们的工作模式产生根本性的改变。不过,那次经历为我们深入理解如何运用机器学习实现测试失败的自动分类奠定了坚实的基础。
而这一理解上的突破,改变了我们的工作方式。现在我们能够允许预期失败的测试继续进行,不会浪费过多时间,并且在区分预期失败和新出现的失败情况时,依然能够保持清晰的判断。可以想象,如果我们仅仅是被动地使用黑盒式的 LLM 来解决分类问题,没有真正洞悉这些模型的工作原理,那么我们很难实现这次飞跃。而这一飞跃对于探索该技术在未来可能的进一步应用具有至关重要的意义。
对于我们团队而言,实现与已知问题相关故障的自动分类无疑是一项无可比拟的成功。我们的机器学习模型不存在人类所具有的偏见,也不会轻易得出错误结论。在实际工作中,我们常常会遇到这样的情形:当某个经常失败的测试突然以不同的方式出现失败时,我们的机器学习模型会停止对其进行自动分类。这一现象看似不利,实则为我们团队带来了宝贵的价值。因为收到测试失败方式变化的警报,有助于我们团队增强对正在测试的软件的态势感知能力。在执行分类任务时,机器学习模型拥有比人类大得多的内存缓存。因此,利用这些模型,实际上是让机器充分发挥其优势,弥补我们人类先天存在的弱点,进而完善我们作为质量保证(QA)团队的专业技能。
六、跨团队信任和指导:携手共进
相较于其他公司的测试团队,我们团队团队处于独特的位置,我们为软件开发人员提供代码审查和指导,以在我们的视频会议解决方案中构建新功能。这一工作与功能团队正在开展的新的端到端测试有关,其目的在于全面测试他们在产品中所实现的新功能。
我们功能团队的开发人员深刻认识到,作为一个专门的测试自动化团队,我们在提升测试的可读性、可维护性,尤其是可靠性方面具备独特的专长。不仅如此,我们还积极协助他们探寻并有效运用我们所创建的测试工具。这种信任和协作是双向的。我们测试自动化团队依赖功能团队并与之协作,以确保将必要的测试插桩内置到我们的产品中,从而使我们需要涵盖的许多测试自动化场景首先实现自动化。
七、检测策略与公司价值观一致:追求卓越用户体验
Neat特别关注我们的测试自动化策略,以消除称为“微故障”的东西。微故障可被描述为系统中存在的摩擦情况,其表现形式为最终用户在使用过程中遭遇困难,比如需要多次重复相同操作才能达成目的。由此可见,“微故障” 与完全故障(即 “宏故障”,此时系统某些功能完全失效)截然不同。
微故障具备危害性,原因在于其发生时客户往往觉得不值得为此专门报告。但如果客户频繁遭遇此类微故障,可能会非常让人烦躁,甚至可能在我们不明就里的情况下更换供应商。
为缓解这一问题,我们采取的方法是随着时间推移对微故障进行识别与跟踪。具体而言,持续处理问题直至发现其已减少或被消除。这就要求我们创建的测试工具在遇到首个困难时不应直接判定测试失败,而是进行再次尝试,并记录尝试次数以及最终测试是否成功完成。为此,我们专门构建了一个框架,用于收集所跟踪的每个微故障的数据,从而能够追踪每个微故障随时间推移的变化趋势。如此一来,我们便能清晰判断问题是在改善还是恶化,并据此持续评估针对每个微故障所做缓解努力是否取得成效。
对于neat而言,消除微故障至关重要,因为我们努力为使用我们的视频会议解决方案的客户提供无摩擦的体验。这一举措正是我们的测试架构与公司努力追求的目标保持高度一致的有力体现。
八、测试创造价值:不可忽视的重要环节
测试自动化工作充满挑战,对于那些未能将其视为 “真正的” 编程领域的人而言,它或许并不被当作一门学科。若缺乏良好的测试自动化资源,公司极有可能完全舍弃端到端测试,转而片面地专注于单元测试,或者过度依赖手动测试。
然而,这样做会造成严重损失。因为测试自动化能够持续捕捉那些虽通过单元测试却可能在后续环节出现的回归问题,从而为客户提供价值保障。相比之下,基于人工的测试缺乏足够的规律性,难以确保每次都能精准捕获所有回归问题。
持续稳定地交付价值的核心在于精心设计测试架构。合理的测试架构能够释放手动测试资源,使其投入到人类独具优势的探索性测试领域。通过这样的方式,我们既能充分发挥测试自动化的高效性和准确性,又能借助人类的智慧和创造力,在测试过程中发现那些潜在的、不易被机器察觉的问题,从而实现更全面、更可靠的软件质量保障,推动软件开发与交付过程朝着更加优化、高效的方向发展。
- end -