测试时找到边界情况是发现bug的好方法。一个常见的测试场景如下:
某一个加法计算器,有两个输入框,当输入两个数字,点击“Add”按钮,计算器会显示两个数相加的结果(如下)。
如果我们不输入任何数字,点击确定会发生什么呢?这就是一个典型的边界情况。如果该加法计算器的代码如下,则会发生错误。(精简后的示例代码)
...
public int sum(int a, int b) {
return a + b;
}
...
代码中并没有处理为空的情况,程序遇到没有输入的值时,就会不知所措。
上面的例子很简单,我们可以轻易的找到一个边界,然而,随着软件的快速发展,软件所解决的问题越来越复杂,软件本身的复杂度也越来愈大,远超个人的理解能力。那么在这样的情形下,我们如何在测试时找到边界情况呢?
个人遇到的有三种边界:
业务边界
业务边界是大多数测试人员广泛使用的一种边界。上面的计算器的例子,就是一种业务边界。从业务的角度来看,用户是有可能不输入而直接去点击确定按钮的,而这里常见的业务需求是提示用户输入数字。业务边界的特点在于需要测试人员充分的理解业务,从而找到除了软件开发需求说明(文档)之外的一些非常规业务场景。
在之前的项目中我曾遇到过一个相对复杂案例,测试对象是一个保险公司的在线车险理赔网站。
典型的业务如下:需要理赔的车主,通过在网站上填写汽车出险的信息,系统会自动计算出相应的赔偿金额,车主在线支付后,车主就可以在线选择修理厂进行修理。
由于该保险公司的客户主要居住在沿海地区,容易发生极端天气,如暴雨,台风等。一旦发生了暴雨,往往在某一小区域内,会有大量的汽车遭遇水淹事故,由于都是同样的问题导致的故障,维修的方式也是类似的,保险公司为了减少自己的理赔成本,于是把同样问题的汽车打包指定到某固定的修理厂,通过这种打包,保险公司获得了优惠的价格,从而降低成本。而对于出险的车主也无需在线支付,简化了理赔过程,提升了用户体验。于是保险公司就给他的网站增加了一个功能,将某次极端天气袭击的发生的时间,地点,极端天气种类,车辆发生的问题,这四个因素组成一个组合,当这四个同时满足时,系统就会把这些车制定到某一特定修理厂。
业务人员在写业务需求时,主要经历放到了描述上面的场景上,如何而没有描述如果四个条件之一不满足的情况。恰恰就是这种遗漏,导致了出错。发生缺陷场景为当时间,发生的问题,极端天气的种类3条满足时,无论地点在哪里,都会将车主导向同一个修理厂。
以上是一种业务上的边界,业务文档中并没有说明需要考虑不满足的情况,但是作为专业的测试人员,我们应该从业务的角度测试这种场景。
系统构架边界
对于该在线报险系统,从业务的边界上,我们确实找到了问题。然而业务上的问题很可能是隐藏的系统架构问题。当我们深入调查之后,发现,原来上面的问题发生的根源在于后台有两个理赔规则计算的入口,开发和业务人员只考虑了第一个入口忽略了第二个。导致,第二个入口的规则覆盖了第一种规则。而这就是系统架构上的种边界。若要找到系统架构上的边界,通过遍历业务确实是可以找到的,但是,如果能对系统架构层面有充分的理解,我们找到边界,发现问题的方式将更加高效。
大家应该熟悉在线查看历史订单的场景。我们在淘宝京东购物,都会遇到。而在线的保险网站同样也可以提供查看自己保单历史的功能,前提是你能用正确的用户名密码登陆自己的保单查询页面。
我曾参与了一个这样的在线保单查询与相关信息修改的网站项目。项目的内容是使用Angularjs重构老的JSP页面。在时间有限的情况下,需求方为了稳妥起见,希望开发团队使用逐步重构的方式,渐进式替换原有的老的JSP页面。第一期目标是替换登陆,保单列表等页面,其他详细信息页面仍旧使用JSP页面。
团队开发进度一致很快,节奏也很平稳。于是大家决定定期的讲讲系统的架构。我们其中一位开发同事主动提出要为我们介绍整个系统新老页面集成的架构部分。当一个整体结构呈现到我的面前时,直觉告诉我,当两套系统互相跳转时,如何相互认证是个问题。当这位同事讲出他的相互认证方是通过URL带着用户ID和session进行相互认证时,我问到,如果我是一个用户,在成功登录保单列表页面后,如果session不过期,那不是说我可以通过修改ID进入其他用户的JSP页面了吗?我立刻现场实验了一番,果不其然,这样做确实存在这个问题,这意味着,只要某一个用户有恶意,只需要通过遍历用户ID,就可以轻松将系统所有的其他用户信息全部窃取,而这对于一个知名的保险公司来说,必然是一个重大的事故。
回顾这个事件,通过分析系统的架构,我们就可以找到架构设计上的边界,从而找到业务测试中难以找到的问题。
编程语言与环境边界
上面讲到了业务与软件架构的边界,软件测试中还存在着一类对于业务测试而言更加隐蔽的边界,那就是编程语言与运行环境本身的特性而存在的边界。
在一个android项目中,我们实现了一个录音机。在测试时,我们发现了一个缺陷,常常会录到一些音频是空的,大小 0KB。经过了反复的研究之后,发现当录音时间比较短的时候会出现这个问题,通常是小于4秒就会为空,然而,再次验证后,又有时6秒才不会为空。算是一直不能稳定的找到缺陷路径。然后自己拿到代码进行了调试,经过了多次实验之后,发现,每当需要写入的录音文件大小小于8KB的时候,该文件最终写到磁盘上的时候就成了0 KB。
8KB这个数字有如此神奇吗?一番Google + stackoverflow之后,终于发现了问题的本质,原来Java 的BufferedWriter有一个默认的大小,它就是8KB,当需要写的文件大小小于8KB时,默认是不会写入目标文件的。要解决这个问题,在文件写入操作只有调用close或者flush才会真正的关闭文件,完成写入。果不其然,我们的录音机程序调用了另一位同事封装的文件读写库,该库里的文件读写方法,恰好忘了调用close方法。添加close方法后,缺陷被修复。
以上是一个程序语言特性导致的边界场景,另外还有自己曾经短暂做过嵌入式开发的我,在ARM平台与Windows X86 平台通信时,发生了大小端转换的问题,这个是一个典型的运行环境导致的边界。
要找到编程语言和环境的边界,对测试人员提出了更高的要求,需要测试人员深入理解编程语言的各项特性与操作系统的各项知识。
总结
作为测试人员,在保证产品正常功能完善的情况下,再做一些边界测试时,可以通过上面的3个纬度来寻找测试中的边界。
而上述的3个纬度要求测试人员需要对业务,系统架构,编程语言和操作系统等知识有深入的学习与理解。
所以,做一个好的测试人员,需要的技能要求是非常高的。而自己一直在朝着这个目标努力着,希望有朝一日自己能达到这样的程度。
后记
虽然我上面讲到找到这些边界,需要很强的技术能力,但是,测试中有专门的测试技术可以忽略上面的背景知识,来发现缺陷。在后面的系列里,我会讲到。然而,记得史亮的软件测试实战中提到,“测试人员的工作效率取决于他对软件和项目的理解,而不是他掌握的测试技术”,对于这一点,我非常赞同,对软件和项目的理解,包括了业务,架构和技术栈本身的理解,测试人员理解的越深入,那么越可以更加高效的发现有价值的信息,协助团队做好质量工作。