满打满算,步入测试行业已经 5 个年头,期间做过 web 测试、大数据测试、性能测试、App 测试、微服务测试等,但是软件测试的核心原则本质是一样的,只是不同测试的类型侧重点不同而已。web 测试侧重于黑盒测试,大数据测试侧重于数据准确性,性能测试侧重于软件产品的非功能性,app 测试侧重于专项测试,微服务测试侧重于接口测试、契约测试。
刚入行的时候也看过软件测试一些原则(感兴趣的同学参见《软件测试的艺术》一书),但是当时受限于测试经验的不足,不能深入理解一些原则的含义。最近重读这些原则,对其中的一些原则有种豁然开朗的感觉,但对有些原则持 “质疑” 观点,同时也有自己的一些见解, 下面我就结合我的测试经验和大家一起重温下这些原则的含义。
《系统之美》一书中有个观点我非常认同,就是任何一个系统都有一个最关键因素决定其行为。
对于测试用例来说,预期结果就是最重要的。大家可以思考下,如果某个测试用例的预期结果没有定义,由于 “所见即所想” 现象的存在,某个似是而非、实际上是错误的结果可能会被解释成正确的。换句话说,尽管 “软件测试是破坏性” 的定义是合理的,但人们在潜意识中仍然渴望看到正确的结果。
在设计测试用例时如何定义用例的预期结果?
1.避免出现模棱两可的词汇
2.避免将预期结果描述为是或否
3.尽可能详细描述预期结果
第一个用例设计准则的原因上文阐述过了,我这里说明下后面两个准则。不可否认,第 2 个准则要求避免的问题我刚入职那会也是犯过的。将预期结果描述为是否无外乎两个原因:1. 项目时间比较紧;2. 觉得用例是自己执行的,预期结果已经在脑壳中记到了,无须再写出来了。
但是我们负责的项目并非一个迭代就完事了,通常要多个迭代,期间会出现人员离职、人事转岗项目交接的情况,那么你写的东西别人就可能看不懂,这样会带来重复劳动。所以眼光要放长远些,就像开发同学开发代码一样,你写的用例可能会在若干年后被别人用到,编写高质量的用例也是对别人负责。
就以登录功能预期为例,预期结果至少要包含 登录成功与否、页面是否跳转、登录成功要包含接口返回结果、登录失败要包含失败信息、错误码信息。
不同于测试同学以 “破坏性” 的眼光测试产品,开发同学更多以 “建设性” 的眼光写代码,因此他们往往会忽视一些潜在的问题。
不可否认,开发同学测试自己的代码效果不好,但是更不建议开发不测试自己的代码,例如不写单元测试。当然,大多数开发工程师还是比较看重自己的代码质量的,通常会做基本的单元测试,毕竟这关乎自己的脸面和绩效。
我想此原则作者并非建议开发不做测试,而是更多想突出 开发工程师开发的代码是不可靠的这个观点。因此代码质量的保障是离不开测试同学的。
即便错误的内容在输出结果中可以清楚地看到,但还是没有找出那些错误来。换言之,在后续测试中发现的错误,往往是前面的测试遗漏掉的。
上述问题相比大家都深有感触,如果你的团队有交叉测试的环节,可能对这个问题更有感触了,为什么同样的用例执行过后,你能发现你同事没发现的问题或者你同事发现了你没发现的问题。其实归根到底还是 测试过程检查测试执行结果不仔细导致的。因此建议初入行的测试同学,一定要认真检查执行结果中的内容是否与预期结果中的完全一致。
当然,针对这个原则我还想多深入一些,聊聊自动化测试的断言颗粒度问题。
还记得我刚做自动化测试时候是利用 JMeter 测试工具,当时断言接口响应成功与否取决于接口的返回的个别字段,比如接口返回字段 isSuccess=true/false。其实这样的断言是非常不可靠的。大家要清楚请求一个接口后,是否有数据库表数据的变化。通常事务性的接口断言需要精细到表数据变化、查询类的接口也要断言到重要返回信息的正确性。此外,有些接口的断言甚至要精细到日志打印的准确性。
这个原则换句话说就是,我们在测试的时候不能仅考虑正向的用例,还要考虑异常的业务场景。
正向用例通过只能说明产品功能被正常开发,但是这不能说明产品功能就是鲁棒的、容错的,而异常测试能弥补这个不足。
有大量的实践证实,在软件产品中暴露出来的许多问题是当程序以某些未预料到的方式运行时发现的。因此,针对未预料到的和无效输入情况的测试用例,比针对有效输入情况的那些用例更能发现问题。
这条原则是上条原则的必然结果。必须检查程序是否有我们不希望的负作用。比如,某个工资管理程序即便可以生成正确的工资单,但是如果也为非雇员生成工资单或者它覆盖掉了人员文件的第一条记录,这样的程序仍然是不正确的程序。
一个最极端的例子就是,如果登录需要验证 6 位手机验证码,而你输入了 4 位验证码就点击登录,结果导致了 APP 崩溃,显然触发这个异常场景 APP 就 crash 是用户不能容忍的。
其实这个原则要解决的问题,我在原则一中也有表述。
测试同学匆忙地编写测试用例,然后测试过程执行这些用例。项目结束后就把这些用例当作垃圾一样扔掉了。一旦软件需要重新测试(例如,fix 了某个缺陷或做了某种功能改进后),又必须重新设计这些测试用例。
如果对程序的更改引起了程序某个先前可以正常运行的功能发生了故障,这个故障往往是不会被发现的。保留测试用例,当程序功能发生改动后重新执行,这就是我们所谓的 “回归测试”。
当然,我所在的项目测试团队都有意识到这个问题,我们的解法从最开始的将用例按项目迭代存档,到将用例的开发、维护等其用例的整个生命依托于到测试用例管理平台,就大大地提升了用例的复用率,降低了重复劳动。
这个原则和原则 4、5 想表述的问题本质是一样的,测试不是为了验证产品功能的正确性,测试是发现错误而执行程序的过程。
这幅图看似没有什么意义,但很多程序都存在这种现象。例如,假如某个程序由两个模块、类或子程序 A 和 B 组成,模块 A 中已经发现了五个错误,而模块 B 中仅仅找到了一处错误。如果模块 A 所经过的测试并不是故意设计得更为严格,那么该原则告诉我们,模块 A 与模块 B 相比,存在更多错误的可能性要大。
原则的另一个说法是,错误总是倾向于聚集存在,而在一个具体的程序中,某些部分要比其他部分更容易存在错误,尽管没有人能够对这种现象给出很好的解释。
当然这个原则可以用咱们的一句谚语概括之:一粒老鼠屎坏一锅粥。
这个原则没什么可说的,毕竟测试岗位本身就要求测试人员具备较强的逻辑性以及耐心的。不可否为,测试入门门槛比开发低,但是测试岗位的天花板是比开发要高的,测试想做出成绩,一定要具备开发能力的。