从这篇文章开始,我会选择一些重要的质量策略进行详细讨论,而今天就先来聊聊测试用例。
测试用例是每位测试人员都绕不开的话题,也是大家习以为常的事情。几乎所有测试相关的公众号、博客、专栏,都会提及测试用例,由此可见它的重要性。但是,有许多从业者,对测试用例的设计仍然依靠经验积累,即使知道用例要从功能、性能、安全等多方面考虑,也仅限于对字面的理解,未形成体系化的整理。所以,今天我更多会从系统的角度,来和大家一起看看用例设计背后的底层逻辑。
01. 万物皆可测
曾经有一位小伙伴问我:接口要怎么测?当时他已经是一位很熟练的功能测试人员了,之所以在更换一种场景之后不知如何应对,还是因为未能掌握用例设计的通用逻辑。类似的问题还有:一个服务要怎么测、SDK 要怎么测等等。
首先,我要说一点:万物皆可测。在开始测试之前,我们需要深入去了解我们的测试对象,用互联网的话说,就是“业务分析”。通常,我们会根据 PRD 去构建测试用例,但这里面的问题是,业务的质量特性包含显性特性和隐性特性,而 PRD 往往只有最表层的显性特性(这很考验产品人员的能力,有些甚至连显性特性都不齐全)。一般而言,能通过各类文档中直接生成的用例,不到完整用例集的十分之一。所以这就需要我们通过其他的方法,来分析出更加全面的特性。
举个例子,老的测试面经里有一个经典问题:要怎么测试一个水杯(不知道现在是否还有面试官问这个问题)。它的 “PRD” 只有一句:这是个水杯。这个问题考验的就是我们对测试对象的分析能力。我这里给出一条万能公式:交互分析->等价划分->边界测试->用例组合。接下来就以水杯为例,来具体看下如何运用这个公式。
我们要知道,所有的测试对象,都是可以被交互的。我们的测试用例设计,其实就是在寻找“交互点”,而后转化为输入域和输出域。比如,我们和水杯的交互点有:
纯看着:外观
拿起来:功能/效率/安全/易用
装东西:功能/效率/兼容/安全/易用/可靠
喝东西:功能/效率/兼容/安全/易用/可靠
放起来:效率/兼容/安全/可维护/可移植
通常我们说的“要从功能、性能、安全等多方面考虑”,其实指的就是单个交互输入域的完整性。比如,对于水杯“装东西”这个交互:
从功能上考虑,要能够正常盛放被装物品(正确性),要能够装入开水、凉水、茶、碳酸饮料(完备性),并且容量足够(适合性)。
从安全上考虑,如果我装的是开水,杯体是否会烫手。
从可靠上考虑,长时间盛装液体,是否会漏水。
…
当我们面临一个不熟悉的场景时,难免会无从下手。如果我们有一套方法论,就可以帮助我们提供更全面的思考,得到更完整的输入域。我引用一个我认为比较好的一种描述法,即产品质量的八个特性:
功能:是否满足用户需求,核心三要素:正确性、完备性、适合性。
效率:在软件中可以理解为性能,包括时间特性、资源占用。
兼容:与环境的共存、交互对象的互操作。
易用:用户理解、操作需要付出多少努力。
可靠:能够正常维持产品特性的程度或概率。
安全:输入输出安全、内容安全、交互安全。
可维护:因环境变化时需要做出调整的难易度。
可移植:从一个环境移到另一个环境的难易度。
通过复用这套方法论,我们就可以在面对不同的测试对象时,尽量找到更多的“隐性特性”(显性特性已经明明白白写给你啦)。此外,我们能够识别的“隐性特性”,还取决于我们对业务的深度理解。比如水杯的容量是否足够,要考虑水杯设计时面向的用户群体。茶杯、白酒杯、红酒杯、啤酒杯,它们的容量要求都是不一样的,我们不会希望一个白酒杯的容量能有 500ml。所以,在列举输入域时,一定要结合业务特性和用户群体。
回到之前的问题,接口要怎么测试?我们同样从与接口的交互考虑:我们可以发起接口调用,检查它返回内容的正确性;可以以相同参数反复请求,检查它的幂等性;可以高频次的发起,检查它的性能;可以尝试提交未经授权的请求,检查它的安全性等等。
02. 用例的本质
在做完交互分析之后,会面临一个问题:在绝大部分(99.999…%)的情况下,输入域是无限的。比如,水杯中盛装的物品,可能是水、酒、可乐、饼干、草莓、浓酼酸… (谁说只能装可饮用的液体?)我们穷尽一生,也不可能去尝试所有的输入。因此,业内才有一句话叫做:完美的测试是不可能的。那是不是代表测试就是在碰运气呢?并不然。
之所以测试领域会有这么多的用例设计方法论,原因就是我们需要把无限的输入域,变成有限的、有代表性的输入集。测试的意义,不是找到全部的缺陷,而是找到对业务有价值的缺陷。比如一款定位是面向下沉市场的水杯,杯子不够耐摔,就是有价值的缺陷,而杯体不够透明,就是无价值(或低价值)的缺陷。对任何一款产品或一块业务而言,我们都得把握好质量本身的“度”。
好的用例设计,是能够以尽可能少的用例,找到尽可能多的有价值问题。因此,用例的本质,是对问题的抽象,而测试人员最核心的能力,是对问题的定义和抽象能力。我们再来回顾这个公式:交互分析->等价划分->边界测试->用例组合,它其实就是一个问题定义->问题抽象->问题简化->问题处理的过程。
1)等价划分是将输入域按某个维度进行的有效归并。比如可乐、雪碧、芬达,它们对于常规的水杯而言,都是同类物体,穷举测试并没有意义,我们可以把它们归为一类“碳酸饮料”。再比如,一个塑料杯,对于温度 10 度的可乐和 28 度的水,也没有分别,我们也可以把它们归为一类“常温液体”。注意这里在归类之后,会有输入重叠的现象,比如常温下的碳酸饮料,这个稍后很快会讲到。
2)边界测试是提取单个输入域分类的有效代表值。对于单个输入域分类而言,可选的输入值仍然是无限多的,所以我们需要找到能够代表其他输入值的单值。而边界值往往就极具代表性。比如,对于水杯能够承受的液体温度,会有一个规定的上下限,而我们需要验证它是否能够真正达到这个限制(我们不会希望看到倒入开水之后杯子就爆开吧)。除了边界值外,还可以在非边界范围内随机挑选几个值做为代表。
3)用例组合则是对这些代表值按分类做交叉考虑。像判定表、因果图、正交实验等,就是告诉我们如何做交叉考虑的方法论。前面提到的输入域重叠,即是不同分类的交叉点。比如,常温下的碳酸饮料、常温下的水、冰冻的碳酸饮料、冰冻的水、沸腾的碳酸饮料、沸腾的水…
很多测试教程,将等价划分和因果图等做为同一个维度的测试方法放在一块介绍,对测试新手会造成很大的干扰,以为它们相互并行,这其实是不正确的理解。这点我们已经解释过,前者是对问题的抽象,后者是对问题的处理。还有一个常见误区是,等价划分、边界测试仅用于黑盒测试,覆盖率分析仅用于白盒测试,这也是由于没有真正理解测试方法论造成的。比如对于如下代码:
if (value >= 1 && value <= 10) {System.out.println("I have " + value + " cards." );}
等价划分和边界测试在这里仍然有用:20 和 30 是同一类,-5 和 -10 是同一类;0、1、2、9、10、11 就是它们的边界。同样,对于黑盒测试,也可以应用覆盖率分析,这就涉及到接下来的内容:业务建模和图论。
测试用例可以讲的内容实在是太多,由于时间关系(工作太忙,见谅),我拆成上下两篇文章来讲解,剩下的内容,我们下周再见。
现在我邀请你进入我们的软件测试学习交流群:【746506216
】,备注“入群”, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路。
喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!