论测试用例的有效更新及杀虫剂悖论
在2014年,我们团队试图推动一件事情——把产品后端(客户、客服、生产制造等等)出现的问题,反向增补为测试用例,扩充到测试用例库中,避免后续重复的出现问题——早些年柳传志在创业类的节目问一个选手,作为老板,你每天第一件要处理什么事情。选手按照自己的优先级和重要性说了一堆。柳传志说:你应该优先处理反复出现的问题。
复盘论是联想的看家本领,这也仅借用一下这个意思。
尝试这么做了一段时间,把已经形成的反向增补测试用例,推广到相关测试用例库,然后在实际中执行和检查,一段时间大致有如下几种现象:
一、绝大部分,根本不执行。
二、小部分,有选择的执行。
三、小部分,重新编写,纳入到原有的测试用例中执行。
第一种现象的原因有很多种,光明正大的以及不那么光明正大的——我更愿意认为是下文会提到的原因。
对于第二、三种现象,我被反问的问题是:如果没有按照我们写好的格式,单独的拉取出来并有执行结果,那么就无法通过人工或者工具来统计这些新增的用例是否被执行过?数据拿不到,由此就不能判断大家在测试方面是否有优化和进步。
先暂时放下复杂的执行和检查的针对性问题,仅仅从测试本身——一个问题出现,是否要把这个问题出现的步骤、缺陷的场景,类似可能出现的逻辑,都写在测试用例中,在后续的项目中,反复的执行?
答案是不一定——测试设计是一个领域的高手才做的事情,而不是单纯的有一说一的死板描述。或者换个说法,测试用例是测试工作的核心,是充满创造力的事情,而不是可以有一个什么绝对正确的方法论,就可以一劳永逸搞定的。
列举一些不同的例子,来展示表象和本质之间的复杂关系:
一、问题产生的原因,它的频率是什么?
EX1——如果问题是因为开发人员错手把一段代码注释,或者因为各种笔误产生的缺陷,发现之后修改代码重新编译,问题解决。
那么这种问题的概率就是一次性的。这个缺陷修复后,再次出现的概率就非常小——除非这代码是别人留下来的,然后换个开发,又胆大的修改了一些老代码。然后自己的组长还没有代码审核,直接提交了。那么这问题才有可能重见天日。正常针对这种情况,是没有必要写上几条case,后续的项目每次都执行的。
EX2——有一个资源,多个模块都会调用,而且这几个模块业务逻辑耦合的较为紧密,而且联调一直做的不好,甚至因为解决缺陷还发生过多次扯皮到底是你的我的他的等破事儿。
那么这种问题应该是有概率出现的。这个缺陷修复后,不仅仅这条缺陷产生的操作后续要增补,甚至这几个模块调用资源的一些方法,之前没有太过注意,后续也要适当的加强测试设计。
二、问题涉及的组件、分支流、版本多少情况?
EX3——在嵌入式设备中,“兖”字无法显示,显示为“口”。问题的原因是在嵌入式设备内存较小时,可能字库采用的是一级字库,那么可能所有的二级字库的文字都会显示异常。
2.1、具有唯一性:
如果全公司使用的都是统一的font字库。那么只更新这个font,所有嵌入式设备的二级字库问题都会得到解决,这个缺陷一次性修复后,就不需要纳入到测试用例。
2.2、存在多分支:
有好多的外包项目,要显示不同字体、不同国家的语言,简而言之就是有好多的分支font存在。
2.A、如果有好的全面的缺陷分析和波及通知方式,大家各自修复,也不需要写到测试用例中,因为是一次性的行为。
2.B、如果有一定的缺陷知会方式,不同的分支流可以感知,但是时效性较差,那么这事儿就要固化在测试用例中,执行上一段时间。
2.C、如果没有一定高度的缺陷知会方式,大家基于一个流,后续各自开发维护,那么肯定要写在测试用例中,甚至要组织小的专项测试,来集中暴露不同版本的问题。
三、是否有强顺序依赖关系?
EX4——如果一个问题,和业务逻辑顺序强相关,需要经过必须的1、2、3、4、5等步骤,才会导致一个必然的bug。从测试人员的本职工作来说,能发现这样的bug(俗称神级bug),简直是自己对业务知识了如指掌的最好表现,甚至可以作为自己的江湖轶事不断的吹嘘下去。
但是,这种bug,回归测试之后,真心不用把它形成测试用例,让后面每一个项目,都去反复的执行——强业务顺序关系修复了,后续自然不会出现。至于是否有其他隐含的逻辑,是否需要进行其他的分支状态测试,那是另外一回事儿。
四、验证条件具不具备?
EX5——各种复杂的外厂商对通问题。
此类的bug,多是在现场,通过抓包分析、码流分析,然后不停的替换临时版本才能修复。如果是协议标准化方面,可以在测试环节加强,如果是各厂家飞速发展中产生的非标协议,谁也没办法,只能现场解决。
所以,你可以写一条,A设备,需要接入甲厂家的XXX产品/乙厂家的YYY产品,进行ZZZ功能测试。但是,这些测试用例,不具备可执行性。
对于此类的互联互通问题,最好的解决方案是,找到一个设备型号很多的客户,维系好客户关系,发布新产品的时候,自己带台设备过去,联调就搞定了。
这个例子需要的是此类问题的测试策略和方案,而不是生硬的补充无法执行的测试用例。
EX6——长时间运行后导致的问题,比如XX设备运行三年后,器件老化,或者版本、文件无故丢失。
这就分别涉及了可靠性和flash反复读取,碎片和黑天鹅事件等。
测试这类的问题,要在短时间内模拟三年的效果,只能是通过上测试设备量,以及通过公式推导大概的稳定性。写在测试用例里面,在日常的工作中,显然是无法实现的,还不如老老实实的做专项测试,集中人力、设备等等。把此类问题一次性搞定。
以上是由缺陷反向提炼测试用例的第一个概念——从问题中汲取经验,避免以后再犯同样的问题,思路和逻辑都是对的。但是绝对不意味着比着葫芦画瓢,有的问题可能就是一次性的,有的问题背后可能有更大的问题,有的问题你知道但是还只能看概率和投入产出比,或者尝试通过其他方法来解决。
第二个概念和团队和人有关系,一个团队真实的运作,往往只有内部人知道。同理,问题产生的真实原因,往往是一个团队内部被隐藏的,所以是否能写出精准的测试用例,也只有团队内部自己人才能搞的定。这就意味着如果测试用例更新不是自己部门内部主动触发,而是第三方部门(质量部门、流程部门)驱动的,那么就注定只会拿到一些样子货。
五、问题产生的真实原因,会让你写的case完全不一样。
举个例子:软件客户端解码无声音。
但是如果你增加一条测试用例:“软件安装/更新成功后,查看编解码状态是否正常,预期结果:图像、声音正常。”拿到这条用例的人会认为编写人秀逗了,这么基本的东西早就测烂了,还正儿八经的新增,最后的结果要么是不执行,要么就是无脑打钩通过。
但这个最基本的问题,会一次次的出现,背后自然有深层次的东西存在。
EX7:兼容性问题,某音频格式经过翻转,未考虑兼容性。
早期版本的音频码流发过来,解码失败,这种无声音就是标准的兼容性问题——所以增补测试用例,就要写成,和各产品各版本进行兼容性测试,看视音频是否正常。
看起来是不是抓到实际问题了?但是这种用例也是理论上的全面用例,实际也不可能会被执行(参考六、测试用例的可执行性)——历史产品可能有二十几个,历史版本可能也有二十几个。你动动嘴皮子互联互通,且不说是不是测试环境有这么多设备,就是在一切顺利的情况下,版本更换并测试一轮,也要个几个工作日。在测试资源、时间一贯紧张项目背景的下,这条case会被执行才怪。
结合上面的观点,看编解码组件的版本是否有变更,然后再决定是否执行编解码不同版本之间的兼容性测试,然后通过等价类,选取产品和版本,让测试执行在半个小时到一个小时可以被执行,才是正解。
EX8:DLL被覆盖的问题。系统先装了产品的的编解码插件,然后又装了其他的播放器(暴风影音、千千静听都出现过此问题),同名编解码插件被覆盖,解码失败。
此类的问题,排查过程可能比较纠结,但是排查清楚后,是否要写条测试用例,以后每次都纳入执行呢:“首先安装我司产品,然后安装暴风影音,进行编解码,看是否正常。预期结果:视音频正常。”这种用例是否可执行?
看解决问题的方案是什么:
8.1、如果解决方案是销售规避——服务器是独立安装的,所装软件都是有标准版本,不允许安装其他软件,那么这个问题根本就不需要解决。只需要卸载非允许软件,重新安装一次即可。
8.2如果解决方案是统一把dll的路径由system目录,修改到指定的目录,规避dll被覆盖的问题。那么这条case就需要执行一段时间,并且要明确检查,setup之后,查看XX路径下的XX文件,是否更新成功这条检查项。
8.3如果解决方案没有统一指定,每个软件团队都是自己指定目录,且dll的特性不一样,有多个版本在同时使用,那么必然会存在自己公司多款软件调用dll冲突的现象,或者毫不客气的说,部分人员连dll搜索路径“当前目录->system目录->windows目录->环境变量Path指定的目录”都没有考虑。那么这事儿如果要暴露,就要找几个人成立专项测试,甚至要周期性进行检查了——但是一旦恶劣到这种情况,就是各软件产品没有统一的规则,大家关起门来自己按照自己的想法设定,并单纯的认为客户只会安装一款 产品。如果是我,肯定罢工——系统部门坐下来定个规则,大家一起修改一下,就可以一劳永逸,分分钟的事儿。结果把问题甩给后端团队,找几个人费工费时,长期的去折腾。这是不拿别人当人看,也没有考虑项目整体成本,或者干脆就是没有尽到责任,凭什么让测试人员来背锅?
一个看起来相同的现象,因为产生原因的不同,可能采用的行动是截然不同的。如果你不在项目组里面,不对里面的原因了如指掌。只是单纯的督促某一个人员,这事儿是不是你的问题?这反而容易激发逆反情绪,对整体推进产品,会产生非常大的负能量。
六、测试用例的可执行性。
上文已经举了一个例子。凡是随意的写出穷率测试的测试用例,都是不负责任的。
A:我写了遍历所有的接口,所有的格式,清清楚楚,你怎么没有测到?
B:你算过你这一行实际要测试多少时间么?你写一句话,我要折腾一个礼拜。
出了问题,你说你想到了,是执行人员偷懒,但是这么紧张的测试时间,不可能给一个礼拜的时间去测试这么一个基本功能。测试优先级、测试等价类划分,甚至根据客户使用概率做带风险的暂不测试决定,不是测试设计该做的事儿么?
形成一张图表来阐述观点。
这张表的目的并不是死记硬背,而是当你思考“这个问题的产生,我们要不要写条case,然后一直去执行它?”的时候,能够根据自己产品的实际特点,做出正确的分析判断就可以。
所以回归一开始的问题,如果是按照冷冰冰的规范约定,所有的问题都必须纳入到缺陷进行管理。在面对复杂多变的种种现实情况时,落地的样式可能会多种多样(不需要、选择执行、长期例行覆盖、短期覆盖、专项重点解决)。
第三方部门的种种检查方法,可能并不能套用到一条条的用现有用例中,而趋利避害的本能,向不了解业务的人,讲解清楚的缘由和解决方案是非常麻烦的事情。所以往往实际的产品验证方,与其试图无谓的解释清楚一二三四,还不如干脆做表面文章,写几条看上去通俗易懂的测试用例,大家反而会过的舒服一些。
按照驱动力3.0的概念,胡萝卜大棒会扼杀别人的积极性和创造力,所以通过智力定位并且写出足够牛B的测试用例这种高大上的行为,通过标准化、检查化的方式,往往会被变成写水文的应付行为——这不是本文的重点,就稍微点到为止。
回归测试用例更新、优化本身。
除了由缺陷提炼出测试用例进行反向增补外,测试用例的基准库,也要定时评审修改更新的。
这就是测试用例中的杀虫剂悖论(pesticide paradox)——对软件进行越多的测试,那么该软件对软件测试人员的测试就越具有免疫力。或者字面理解,如果地里长期只打一种农药,那么虫子(bug)就会产生抗药性,导致效果越来越差,最后杀不死虫子。或者换个维度来描述:测试用例就是一种数据量化指标,你想考核什么,长此以往就必然会得到这种量化结果,但是对事情实际的提升,可能帮助不大。
可能一些测试用例在设计时是针对当时产品的一些薄弱环节。但是几个项目测试完成,甚至几年之后,这些测试用例的有效性就趋于为0。
1、可能是代码逻辑修复,此类问题再也不会出现;
2、可能是软硬件变更,原来的测试方案需要调整;
3、可能是功能点优先级变化导致的测试用例优先级调整等等。
举个例子,曾经在测试用例中,要求把版本放在发布服务器之后,需要重新下载后,进行一次安装测试,确认各模块的版本号信息。这在当时的条件下,是必然的一个测试步骤。原因一、曾经出现过用不同的解压软件和断点续传下载工具,导致文件字节数大小不一致的问题;二、原来版本是一个文件夹,其中有各种ini、exe、bin、setup文件,很容易出现测试版本和发布版本不一致的现象;所以重新进行安装后检查版本,是非常必要的行为。
但是过了几年之后,解压缩软件越来越多,兼容性越来越好。覆盖解压软件越来越不可能,还不如指定解压软件及版本号更现实;发布文件本身也不是一个文件夹,而是一个或几个Zip包,也避免了人工复制粘贴多个文件,人为混乱的风险;三、数据校验也做的比之前好多了,没必要采用土鳖的方法手动核实。
所以,这条用例,毫无疑问可以删除掉,毕竟下载、替换、看版本号,怎么说也要两、三个小时才能搞的定。
经年不变的测试用例,从工作性价比的角度来说,这就是无效的工作内容。就好像站在楼梯口的服务员,仅仅是因为传统而站在那里,而不知道一开始仅仅是为了提醒大家楼梯的油漆未干。
从测试职责和风险来讲,这就是推卸管理者的职责。无论怎么说,常年不变的测试用例,就意味着多年没有进步的测试团队,这是毋庸置疑的。