经验告诉用户避免使用最新版本的软件应用程序,直到发布不可避免的维护版本和补丁。虽然每个人都知道初始版本 V 和稳定版本 Vn 之间存在的 软件质量差距 ,但在解决该问题方面并没有取得太大进展。
本文将讨论五个可操作的想法,以帮助开发团队缩小质量差距:
-
使用代码覆盖率分析来衡量测试完整性
-
通过单元测试提高测试覆盖率
-
使测试易于运行,使测试结果易于理解
-
实施自动化、并行和基于变更的测试
-
不断重构代码以提高可维护性
1. 使用代码覆盖率分析来衡量测试完整性
所有开发软件的组织都会在发布之前执行系统测试,以确保它在最终用户部署时能够正常工作。然而,系统测试的挑战通常是确保测试完成。许多开发组织将开发映射到应用程序(如果可用)的书面要求或映射到用户文档的测试程序。这种类型的测试将通过代码执行标称路径,但不太可能测试边界或错误条件。 这种方法通常只能提供 60% 或 70% 的代码覆盖率。
确保系统测试完成的唯一方法是在测试期间收集和分析代码覆盖率数据,以确定应用程序的哪些部分已被每次系统测试执行——更重要的是,应用程序的哪些部分没有执行被任何测试执行。因此,代码覆盖率分析是 准确 确定应用程序测试完整程度的最佳方法,也是测试完整性的唯一可靠指标。它可用于从单个开发人员测试到最终发布测试的整个应用程序生命周期。
虽然实现“100% 代码覆盖率”并不能证明应用程序是完美的,但它是工程高质量软件的关键组成部分。事实上,许多与安全关键软件开发相关的标准都要求将代码覆盖率作为开发过程的一部分。其中包括 DO-178B/C(航空航天)、ISO 26262(汽车)、IEC 61508(工业控制)、FDA 和 IEC 62304(医疗设备)以及 CENELEC 标准(铁路应用)。
图 1:分析源代码覆盖率是衡量测试活动完整性的最佳方式。
2. 通过单元测试提高测试覆盖率
一旦开始测量代码覆盖率的过程,很可能会发现现有测试提供的覆盖率远低于 100%。这种覆盖范围的差距是由于测试人员专注于标称用例而不是错误案例或边界条件造成的。
缩小覆盖率差距的明显方法是添加额外的功能测试,但可能有 20% -30% 的应用程序代码很难在生产环境中使用功能测试进行测试,因为很难注入所需的故障触发错误处理。
图 2:覆盖范围差距是由测试人员专注于标称用例而不是错误案例或边界条件造成的。
毫不奇怪,该领域中出现的大多数严重错误都是对应用程序的奇怪刺激组合的结果,这是从未预料到的。进入传说中的 Heisenbug,当人们试图探测或隔离它时,它会消失或改变其行为。对于 C 程序员,这些被认为是未初始化的自动变量的结果,并且是沮丧的根源,因为仅仅观察代码似乎正在改变它。 [1]
这就是使用低级单元测试至关重要的地方。单元测试是构建健壮且无错误的应用程序的重要部分,因为它允许测试人员更轻松地激发应用程序的低级功能,并证明低级需求已得到正确实现。
在执行单元测试时,设置参数,这些代码单元与模拟对象和主题隔离运行。因此,它提供了许多好处,包括帮助开发人员在开发过程中发现问题,从而更容易解决和修复问题。单元测试还允许以生产环境中不可能的方式将故障注入到错误处理测试中。
3. 使测试易于运行,测试结果易于理解
从理论上讲,这听起来像是一个简单的计划:让测试易于运行并且测试结果易于理解。然而,在实践中,这可能是一个挑战。从历史上看,不同类型的测试由不同的工程师构建和维护,通常使用不同的工具,例如:
- 用于证明应用程序低级构建块正确性的 开发人员测试
- 为证明完整子系统的正确运行而构建的 集成测试
- 系统测试 以从最终用户的角度证明正确性
图 3:启用此工作流程的关键是一个通用的测试协作平台,该平台捕获所有测试及其先决条件和预期结果。
当以这种方式划分测试时,每种类型的测试都由不同的工程师组拥有和维护,而不是在开发团队的所有成员之间共享。事实上,在许多组织中,QA 工程师不可能运行开发人员测试或开发人员无法运行系统测试。
为了提高软件质量,需要打破这些障碍,以便开发团队的任何成员都可以在任何时间对应用程序的任何版本运行任何测试。启用此工作流程的关键是一个通用的测试协作平台,该平台可以捕获所有测试及其先决条件和预期结果。
工程师应该能够通过“单击按钮”运行单个测试或所有测试。此外,工程师能够快速调试失败的测试也很重要。
4. 实施自动化、并行和基于变更的测试
一旦通过代码覆盖率分析提高了测试完整性,并且在整个组织中部署了测试,下一步就是确保测试快速运行。测试在多个组之间进行分区的原因之一是完整的系统测试可能需要数小时甚至数天才能运行。
我们如何才能减少测试时间,同时仍然确保测试的完整性?通过使用并行和基于更改的测试构建可扩展的测试基础架构。
单个测试必须是原子的、小的和快速的。随着时间的推移,测试套件常常变得紧密耦合,新测试只是简单地插入现有测试中。这使得测试变得脆弱并且测试维护非常耗时。设计测试时要记住的一个简单想法是,每个测试都应该定义自己的先决条件——而不是依赖于其他测试的输出。
图 4:每个测试都应定义自己的前提条件,而不依赖于其他测试的输出。
除了测试维护的好处之外,将测试重新架构为原子性还可以:
- 基于变更的测试 ,只运行那些受每个软件变更影响的测试
- 并行测试执行 ,同时运行数百个单独的测试
虽然许多组织已经开发了允许无人值守的增量应用程序构建的软件构建系统,但大多数组织尚未实施增量测试。很多时候,测试是定期执行的,而不是通过完全自动化不断地、增量地执行。
基于变更的测试
基于变更的测试 (CBT) 分析代码库的每组变更,并智能地选择受这些变更影响的所有测试的子集。这导致在完整测试运行的一小部分时间内完成测试。此外,基于变更的测试为实施严格的持续集成 (CI) 开发流程提供了一种可访问的方法;在 CI 的签入阶段,CBT 提供了一种有效的方法来验证构建并及早发现问题。
并行测试执行
为了进一步提高速度,并行测试或将测试平台与持续集成服务器和虚拟化测试机集成,可以将总测试时间从几小时缩短到几分钟——或几分钟到几秒钟。
5.不断重构代码库以提高可维护性
代码重构是在不改变其外部行为 (API) 的情况下重构应用程序组件的过程。如果不进行重构,应用程序代码会变得过于复杂,并且随着时间的推移难以维护。随着新功能和错误修复被固定到现有功能上,最初的优雅设计往往是因果关系。
代码重构提高代码可读性并降低复杂性以降低维护成本。如果执行得当,代码重构有望通过简化底层逻辑并消除不必要的复杂程度来解决系统中隐藏的、休眠的或未发现的计算机错误或漏洞。
图 5:构建测试以形式化预期行为使组织能够自信地重构这些脆弱的模块
重构的最大障碍之一是缺乏将现有行为形式化的测试。
每个应用程序都有脆弱和有缺陷的部分,开发人员由于担心破坏现有功能而不愿更改这些部分。自信地重构这些脆弱模块的唯一方法是确保构建测试以形式化预期行为。
结论
在过去的三十年里,工具、设计模式和开发范式的转变源源不断。其中许多都承诺在不增加时间或精力的情况下提高质量。然而,显然没有灵丹妙药可以免费提供这种改进的质量。
提高软件质量是每个人的工作。提高软件质量的唯一明智方法是提高软件测试的有效性。本文提出的五个步骤可以由任何规模的任何开发组织实施,并为项目建立一个自动化且易于使用的测试协作平台。
[1] 伊万·赫里斯托夫。 2012 年 9 月 16 日。使用等待从 AKKA 演员集成测试中追逐 Heisenbug。 https://honeysoft.wordpress.com/category/heisenbug/