一次新特性测试规划的实践

三年前一次面试过程中,对方面试官问的一系列问题,至今难忘。

“如何保证测试覆盖率?”

“对requirements的覆盖率尽量达到100%。”

“如何保证requirements的覆盖率到100%?”

“保证每条requirement都有对应的test case。”

“如何保证单条requirement是被全覆盖的?”

“…”

Table Of Contents

  1. Origin
  2. Ideas
  3. Implementation
  4. Known issues
  5. Summary

Origin

特性需求的覆盖率,算是测试界经久不衰的话题。不同产品不同层级的测试,对需求的覆盖要求和测试粒度都不尽相同。但无论如何,即使测试资源充足,100%需求覆盖率也是不可能的。更何况现实中,测试资源和交付日期永远是紧张的。盲目追求所谓100%覆盖率,只会使边际效应愈发明显,性价比颇低。

那么,需求覆盖率多少才算达标呢?

其实,覆盖率本身就是一个伪命题。若软件质量没有保证,覆盖率就无从谈起。但质量如此虚无缥缈难以量化,导致现实中,往往只能将覆盖率设定为质量的一项重要指标,甚至等同于质量本身。有经验的测试工程师,也很难避免在平时工作中,掉入这种本末倒置的陷阱。

因此,问题应是,如何规划测试来保证软件质量?

Ideas

一切还是得从客户需求谈起。

通常客户需求相对简略,我厂系统架构师会分析客户需求,丰满为特性需求文档Feature Requirements和用户故事User Stories。有时一条requirement中可能存在多条分支,并包含大量异常处场景。

这样的需求,对开发和测试人员来说,还是太粗放。我们需要更进一步的需求分析。以往,在需求分析的同时,我们会基于预设场景将需求映射到测试用例中,这样的问题是对需求覆盖率(看,还是覆盖率)无法整体把握。

参加和组织过数次新特性测试规划,对规划过程有些新的想法,大致如下。总想着总结为一套可复用的流程。

  1. 先拆分需求为粒度可接受的,包括场景和check points;
  2. 基于经验,补充可测点;
  3. 基于重要性和风险等,对可测点排优先级;
  4. 设计测试用例,保证优先级最高的可测点全覆盖;
  5. 分析未覆盖可测点风险,并采取相应措施;

Implementation

最近我厂基于全新硬件平台,在为某国顶尖运营商定制一款WCDMA/LTE基站。我负责其中几个新特性的测试规划和技术支持,正好可以实践一把。

Background

本次规划的测试层级是系统组建测试,高于UT/MT。产品规模较大,涉及的大软件模块主要有4个。为了保证开会讨论的效率,每个模块组我只邀请了一人参与讨论。四人中有测试背景也有开发背景的,因此某些技术问题的深入讨论,可邀请一到两名直接相关工程师参与当期会议。

Split requirements

首先我们对需求进行了深入的拆解,将每条requirement里提及的正常以及异常分支,都按场景列出来。此阶段不考虑是否值得测或者资源是否允许。比如如果该需求适用与多种用户类型,就列出相应的场景。如果该需求还适用于不同信令操作流程,也相应的列出那些场景。至于用户类型和信令操作之间的交叉,由于组合可能性过多,除非是相互间的差别和风险都非常大,我们暂时未进一步划分。不同场景之间的组合,将在“设计测试用例”的过程中进行。

由于有成员在会前未深入学习相关材料,导致会议进程较慢。不过好在过程中发现了不少问题,比如不同组件对接口的定义,或需求的理解不一致等等。

作为主持者,我做得更多的是引导讨论的主题不要跑偏,并保证会议效率。一旦发现有话题讨论僵持不下的情况,就立即建议中止该话题的讨论。这种情况往往是争论一方或双方,在细节上不够清楚,需要会后找更熟悉的同事讨论确认。因此在会议记录上记一个action point,下次会议再回顾这个话题即可。Action point的owner最好只设置为特定的一人,并设置相应deadline。

这一步我主要使用XMind思维脑图进行会议记录,快速直观。大节点如下所示:

XMind

Add more check points

在拆分需求时,可同时进行,基于以往的开发和测试经验,列举出所有在需求文档中未提及可能出现的场景(更多的是不同场景的组合或异常场景)。需求中已明确规定的异常处理,一般开发人员会相对注意,所以出问题的几率相对较小。反而是需求中未提到的异常,一旦在客户现场发生,程序处理极有可能出问题。这一步相对tricky,一方面,取决于与会人员的经验,要求我们不停的问what if并think wildly;另一方面,正常的分支可能只有有限的几种,但异常可能有无数种,因此要做一定的筛选。

建议此步以测试人员为主导,但凡遇到诸如绝对, 肯定, 100%等此类词语,需仔细分析。以前发生过开发人员说“绝对没有问题”的场景,后来客户真就出了问题的惨剧。这时测试人员一定要“打破沙锅问到底”,如开发不能明确说出所以然,也需要把相应场景列上去。

Prioritize

我将每个可测点从XMind上导入到Excel中,做了一些基础的分类,比如验证functionality的,验证exceptional的,验证feature inter-working的等等。

接下来需要做的就是制定可测点相应的优先级。但如果只是凭经验简单排序,恐怕无法保证优先级制定的合理性。因此,我为每一点都新增了属性ImportanceRiskDifficulty。每个属性取值为范围为1/2/3。

  • Importance: 从功能角度评估可测点的重要性。
    • 3: 非常重要。如果缺失该特性基本功能都无法运行;
    • 2: 中度重要。如果缺失一些高级功能或者扩展功能可能无法运行;
    • 1: 不太重要。一些锦上添花的功能,如对testability的增强等,实际客户无法感知;
  • Risk: 从代码改动复杂度以及场景复杂度来评估可测点的风险。
    • 3: 代码改动量很大,或相关场景以前出过大量问题等,风险很高;
    • 2: 代码改动量不太大,风险一般;
    • 1: 代码改动量很小,风险很小;
  • Difficulty: 由于通信设备测试环境非常复杂,如果场景特别复杂,测试难度很大,所需effort也可能非常高。因此我将这一项也设为其中一个属性。
    • 3: 场景简单,在已有测试环境/用例上简单更新即可测试,测试难度很小;
    • 2: 场景有一定的难度,但仍可参考/复用已有的测试环境/用例,测试难度适中;
    • 1: 场景复杂,没有可参考/复用的已有测试用例,构造环境需很长时间,测试难度很大;

我们将每个可测点的这三个属性值在会议上都评估出来。基于这三项属性的评估值,加上一定的权重,得到最后的优先级。权重是经验值,这里我们选择Importance权重40%,Risk权重40%,Difficulty权重20%。

Priority = Importance * 40% + Risk * 40% + Difficulty * 20%

最终的Priority取值范围为[1, 3],可带小数点。虽然这三项也是凭经验估计出来的,但细化后再排序总比直接排序更有说服力。基本上,越重要,风险越高,难度越小的可测点,值会相应越大,表示优先级也越高

checkpoints

Design test cases

这一步非常关键,因为期望输出就是每个测试用例的步骤和每一步的pass criteria,之后具体测试用例的设计会以此为guidance。这一步的主要原则是,在尽可能少的测试步骤中验证尽可能多的可测点,避免测试用例之间的重复或冗余

测试人员根据经验,结合User Stories的schedule,定制相应的测试用例。需要考虑几点:

  1. 高优先级的可测点必须全覆盖,低优先级的采样测试,比如多种低优先级的组合,可选测其中一种或几种;
  2. 测试用例的排布遵循先简单后困难的顺序。开发的US(User Story)通常也是先交付较简单的功能,接着交付高级功能,最后交付容量和性能相关的功能;
  3. 尽量保证可测点均匀分布,避免出现某些测试用例需要检查数十个可测点,另一些测试用例又只测了一两个点的情况;
  4. 在设计用例步骤时,需要考虑用最精简的路径串联起尽可能多的状态,并且最好不要出现重复的状态。这里可能需要一些图论的知识。
  5. 需要明确测试用例每一步的check points,确保可测性;

Analyze uncovered points

最后需要分析所有未被覆盖的场景,通常这些可测点都是优先级相对较低的。我们需要:

  1. 再过一遍那些可测点,确认是否在前期测试阶段如UT/MT中覆盖;
  2. 检查是否有必要在UT/MT中没有覆盖的可测点中,挑选部分补充调整到测试用例中;
  3. 对于决定彻底drop的可测点,可以列到risks中(当然是low risk了),以备后期回顾查阅;

Known issues

这次新特性的测试规划做下来,觉得还存在一些问题:

  • 部分与会人员对需求和specification没有前期准备,讨论过程中浪费太多时间在解释需求上;
  • 整个过程下来,至少需要4~5次讨论,非常细致,花费effort较多,似乎有违scrum敏捷的原则;
  • 如果有系统构架师参与,应该效果会更好,争端更容易当场确认和解决;
  • 从头到尾我作为组织者,不仅主持了讨论,而且将材料及rough plan在每次讨论前都准备妥当。造成其他与会人员有一定的依赖性,而自己也累得够呛;

Summary

这次做下来收获还是颇丰的,对特性有了更深入的理解,消除了组件之间一些误解,并且在开发之前就发现并解决了部分潜在的问题。而对照最终的test plan,也可以很清楚的看到可测点的覆盖率,即使最终覆盖率没有达到100%,我们对风险一目了然,而且对输出的测试用例也能相对自信。在这个特性测试结束后,我会做一次retrospective,回顾这次实践的结果。

当然流程是人制定的,也是人去实施的。因此要想保证高质量的输出,每个讨论者都要keep quality in mind,积极参与其中,贡献自己的力量。

如果以后还有人问我“如何保证测试覆盖率”,我将用这篇文章来回答他。