作者TonyQ (自立而後立人。)
看板Soft_Job
标题[闲聊] 笑谈软体测试的几个阶段(四)
时间Mon Mar 26 01:41:21 2012
前情提要,这是写给软体开发者的测试历程,
主要是以我自己进入测试的经验说明如何进入测试门槛,
以及我身为软体开发者进行软体测试时需要知道的事情跟可能的心路历程。
所有的内容都只是个人经验仅供参考,欢迎提供意见分享跟挑战。
-----------------------------------
另外这里所指的软体测试都只是主要针对者自己对软体的测试,
而与专案测试(目的为增加专案品质)的目的并不直接相关。
也就是说,这里讲的主要目的,是开发者如何可以在开发过程,
快速验证自己的修改是否有误跟常见的一些问题。
-----------------------------------
本文开始
-----------------------------------
上一篇我们谈到程式码的可测试性,然後你会发现,
我们这系列到现在还没谈到 unit test 的 test case 要怎麽写,
也没有讲到什麽神奇的自动化测试,更没有提到测试可以帮你什麽。
我只说了,
1.测资:你要知道要测什麽,不然也没什麽好测的
2.测试路径:你要知道你现在测试要怎麽测试
3.可测试性:你的程式码经得起测试吗?
-----------------------------------
为什麽?
-----------------------------------
答案很简单: There's no magic.
单论测试而言,你我等每个会写 code 的开发者,
可能都会从软体开发过程中累积无数次,成千上百甚至上万次的测试经验。
如果看也不看这些测试经验一眼,那他们也不过就只是虚度的光阴,
这些经验需要被组织,被思考,被强化,
所有的测试方法论大抵上我认为都不出这个环节。
所以不谈开发方法,不谈进入开发过程我们会碰到的事情,
要怎麽单刀切入测试呢?
即使我有个写测试很快的工具,但我想不到测资也只是完蛋,
当然这些工具会有其神奇的地方,但是在那之前我们还有事情要知道。
所有的工具都是试图帮你简化流程,
但是这些工具简化了哪些流程,便是我正试图说明的事情。
-----------------------------------
这一篇我们要继续举实例说明可测试性的几个样貌,
然後开始批判这样作的缺点。
-----------------------------------
先举低可测试性的案例,
以前我专案中有个设计是跟其他网站合作,使用他们 avatar 的共享服务。
也就使用者在我这边上传使用者图片,
我需要使用合作夥伴的 api 更新他们的 avatar 资料,
因为对方服务不太稳,所以我也在使用者登入时去 sync avatar ,
把图片存在自己家跟自己显示避免对方服务挂掉害到我自己。
因为加入额外的操作,所以我登入跟上传图片时,
要考虑的事情变多了,我甚至要连我登入缓慢时,
是不是对方网站速度变慢,导致挡到我的路都要考虑进去。
-----------------------------------
考虑到可测试性,这样的程式码是不行的,
所以後来改采用背景处理之类的方案,
使用者上传跟登入都只是在 db 注计状态,由独立程式去进行动作。
如果独立程式有 exception ,那就表示是对方服务出包,
我也不用在我的测试周期中担心他。
这类的事情也常发生在「寄 email 」上,
因为 smtp 一样是有可能会喷 exception 的服务,
我後来习惯上也都是采取一样的作法开一张 mail table 去操作。
-----------------------------------
再来其他测试性的案例, 7-8 年前 ORM 跟资料库工具还没这麽流行时,
开 db connection 一堆 sample 都是要用时写 ip account/password。
Sample 不会教你什麽模组化...
开 db 写个表格什麽的都在对应程式码里面写一次帐密,
新手傻傻的照着写就会发现换个测试用 db 就整死自己。
解法就是把这些包起来,做出 Connection 类别,统一管理。
在一条测试路径上,需要修改的重复代码太多,也会降低可测试性。
-----------------------------------
以上我们说的,都还是「开发过程」中带来的测试困扰,
而不是「为了写测试」而发现的测试困扰。
这系列的文章设定是即使你不写 unit test ,不搞自动化测试,
只有手动化测试它还是该要对你有帮助。
然後你可能会发现,这些事情也就是开发者所谓的「程式好不好写」,
本来就写程式而言,测试跟开发就是密不可分的,
因为你总要验证你写的 code 是不是会动。
(注:是不是所有情境会动是另一回事,但总有一个情境要会动吧。)
-----------------------------------
前面的可测试性可能有点抽象,我们继续谈些可测试性的案例,
@相依过多的「状态」会导致可测试性下降,
通常相依一个状态就会有一层条件式判断,而且每多一个就会让复杂度上升。
举个例子,假设你今天论坛网站有做网站分级制度,
你设定登入两百次、发文两百次以上的是黄金会员,
登入一百次、发文一百次以上的的是银会员,其他的是一般会员。
(为解说方便,假设会员等级永远就只有这三种。)
假设会员是 user 好了,比起在程式码中判断
if( user.getPosts() > 200 && user.getLogins() > 200 ){ //gold
//作黄金会员该做的事情
}else if(user.getPosts() > 100 && user.getLogins() > 100){ //silver
//作银会员该做的事情
}else{ //normal
//作一般会员该做的事情
}
你会发现,虽然你是要测试黄金会员跟银会员的执行路径,
但是你测试时却需要发文数(posts) 跟登入数( logins ) 的资讯。
如果我今天程式码是这样,我可以更针对我想测试的目标去测试,
我也不见得要烦恼说 posts 跟 logins 是不是对的,
if(user.isGold()){
}else if(user.isSilver()){
}else{ //normal
}
以 Java 而言,要测 Gold user,
可以让测资复写 User 让 isGold 回传 true,就不需多知道文章数等资讯,
至於文章数判断,那可以切给另一个地方测试。
更不用烦恼我今天万一改了 posts 跟 logins 的条件,
要怎麽让系统中的资料一致。
当然也可能会考虑视情况选用策略模式甚至是状态模式,
我要强调的是测试对象状态如果可以减少,测试性会提高。
不过因为增加函式也有增加函式的维护成本,
所以事实上你该怎麽作,还是因时地而异。
这里只是再次说明,程式的架构会大幅影响可测试性。
(有没有很熟悉,其实我们还是在谈模组化程式开发。)
-----------------------------------
接下来我们谈测试经验管理
-----------------------------------
到这里我们谈论的都还是直接改 code 测试,
一般而言,因为改 code 测试不太方便保存,
有一种作法是把测试码注解,需要的时候在把注解拿掉回来测试,
但是会让程式变得有点肥。:x
如果以 Java 来讲,
也有人会对 util class 写 main method 放测试 code ,
反正平常不会有人无聊去执行非主要的 main method 。
或者是给一个特定系统变数,如 debug = true ,满足时就跑特定测试 code。
有点像 Java 的 system properties 、
php 的 if(isset($_GLOBAL["debug"])) 或诸如此类的东西。
-----------------------------------
测试案例保存的好,
以後要测试可以事倍功半,是目前至今有讨论过的测试案例,
几乎都要异动现有程式码把测试程式加进去,所以要谈谈这样作法的许多缺点。
这样当然显然是有缺点的:
1.如果没有保存测试案例
你每次做完测试之後就只有那次有用,下次就要再重来一次了,
基本上你只有在不断练打字跟温习系统,有蛮多时间在「回想」。
2.如果你在注解保存测试案例
你程式码环境引入复杂的测试资讯,而你程式码总是会改的,
你无法保证你的测试码一定跟的上。
再者,因为注解通常不视为程式内容,
你无法善用 IDE 或语言除错工具给你的帮助,
如果类别改名字之类的,维护上会很麻烦,
常常你移除注解还要修 test case 才会动。
3.如果你利用特别 method (ex. java 的 main) 或 flag 保存测试案例
a.机制可能会太复杂、麻烦。
(main 应该是还好,但是万一碰到真的有 main 的 class 你就没得测了)
b.插 flag 的要担心效能问题,另外通常都一个参数对应所有程式,
要怎麽只针对想测试的去设定,不要因为这样跑出太多无关的讯息,
这还蛮麻烦的。
不是插一群的,会像是 java 的 logger 的这种工具
虽然可以针对特定类别设定 log level 跟判断,
但是设定档真的是很龟毛。-_-
c.可能会影响到 production 。
这是最糟糕的,一个不小心 ,
test case 就可能变 bug 或使用者可以钻的漏洞了。
所以我们通常都不会太想把测试保存在专案里面,
那期待中最理想的方法是什麽?
1.放在测试专属的资料夹或 package 底下,
并且正式 production 必须不引入测试,也要这样还能运作。
2.开启独立的测试专案来放这些测试码
3.让测试简单到不需要保存
但根据目前我们提到的测试情境,我们只是写了一堆逻辑,
并且试着在整合测试的环境,藉由修改程式码来达到每次修 bug 的测试,
所以我们现在事实上还没有能力作到把测试独立出来。
光是要满足整合测试的所有条件你大概就等於自己重写一份了,
到时候大概你反而要测试你的测试案例。(大笑)
我们会将在之後的章节继续介绍怎麽继续往下走得路。
--
因为现在有点想睡,写的可能比较复杂些,
有论述不清的地方可以再推文讨论。:)
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 223.143.225.151
※ 编辑: TonyQ 来自: 223.143.225.151 (03/26 01:42)
※ 编辑: TonyQ 来自: 223.143.225.151 (03/26 01:44)
※ 编辑: TonyQ 来自: 223.143.225.151 (03/26 01:45)
※ 编辑: TonyQ 来自: 223.143.225.151 (03/26 01:49)
1F:推 SickKid:这系列的文章 好!!!!!!!! 03/26 21:32