许多刚毕业的工科同学,尤其是计算机专业的同学在选择自己的进入社会的第一份工作时,往往会面临着研发与测试职位(本文只讨论白箱测试)的选择,而对这两个比较相关的职位有着明显不同的想法,大家一般都比较倾向于研发,尤其是男生,觉得研发更有技术含量,更能发挥自己的聪明才智,创造性更强,甚至认为地位也比较高;而认为测试技术含量较低,重复性劳动较多,也比较繁琐,适合逻辑思维较弱的女生。这种粗粗的印象,其实也是有一定道理的,至少研发和测试在对人的要求上的偏重确实是不同的,但是对这两个职位,以及它们之间的关系的认识,绝不是那么几句话就能概括的,是需要较深入了解和理解的。以下我将与大家一起分享一下我的一些经历以及粗浅的心得(对经历不感兴趣的可以直接跳到最后的总结部分),主要内容放在测试和研发与测试关系的部分。
我07年工科硕士(非计算机)毕业,在年初的时候以实习生的身份进入一家企业,职位是(嵌入式)软件开发,然后毕业后就留在了该公司,从1月份到12月份一直做同一个项目,在我加入该项目时,预研部分已经完成,设计部分也基本上架构好了,而我负责的部分以移植为主,修改为辅,经过2个月的熟悉,便开始了新特性的开发,那时的我其实对研发经验很少,一方面借鉴了原有代码,另一方面也是无知者无畏,写出了一些比较弱智的代码,总之,在大约3个月后,开发工作基本结束,其他模块的也相应开发完毕,这个时候的我们,肯定是觉得松了一口气,觉得自己的模块没啥问题,呵呵,其实在随后的测试阶段,我们的自信将会受到一个又一个bug的冲击。
测试阶段长达半年,比开发的时间还要长。第一个测试阶段是单元测试和功能性测试(这里的功能性测试是代码级的,不同于黑箱测试中的功能测试),这里我们统称为离线测试(基本在PC机上完成,与后面的线上测试相对,线上测试是在真实板卡上进行),这个测试确实是应该由研发人员来做的,所以做研发不意味着就和测试一点关系都没有了,要对自己开发的东西负责,成为一个至少自身能经受得住基本考验的模块。
这个部分主要的特点是在时间允许的条件下测试要尽量做到充分和细致,因为以后到了集成测试阶段,我们将主要关注模块间的协作以及整体流程的运行,出现模块内部问题将变得相对较难定位,因为你的模块出现问题,表现出来却可能是其他模块表现不正常;另一方面,作为研发人员,对测试都有一定的抵触心理,往往不愿意做得太细致,所以在这个阶段,虽然我们压力较小(到了集成测试阶段,你出了问题,那可就是N双眼睛在盯着你了,压力可想而知),但是一定要注重这个阶段的工作,既是作为研发人员的责任,更是将很多bug消除于无形的重要阶段。对于我具体负责的模块来说,比较单纯,主要是自己与自己的内部逻辑,对测试环境不敏感,所以这个阶段对于像我这样的模块更为重要,设计出完备的测试用例(我们一般称为离线环境),不单单对于这个阶段的测试有意义,对以后集成测试阶段发现的模块自身的问题,也能在离线环境下方便的复现,从而定位问题,解决问题。这里我引用我们老大的一句话:测试线上是用来发现问题的,而解决问题主要还是要靠离线环境。
经过了1个月左右的单元测试和功能性测试后,bug自然是找出了一大堆,但是事后证明一个月的时间还是过少了,测试用例的完备性很难保证,就拿我的模块来说吧,功能性测试后(我这部分因为是移植的,不用做单元测试),我自信满满,以为bug已经消除的差不多了,实际上,拿到线上去,估计跑1分钟错误就满屏幕了。后来在集成测试阶段,我仍然继续做完备性的离线测试,这才渐渐地稳定下来,当然这是后话了。
接下来就是真刀真枪的线上测试了,线上测试使用了真实的板卡,是我们的产品日后真正运行的环境,线上测试分为预集成测试和集成测试,其实本质差不多,所以这里就合在一起讲了,统称为集成测试,即线上测试。
集成测试就是应该由测试人员完成的了,但是由于一些原因(主要是产品不成熟),所以还是由我们这些研发人员来完成。注意,这部分就是大家认为的(白箱)测试了。
集成测试采用的是交叉调试环境,这个就不多说了,有两种方式来进行调试工作,一种是接仿真器,利用断点进行跟踪;另一种是不接仿真器,不打断程序的运行,而通过计数器和查看内存的方式进行跟踪调试。前一种方案一般在前期使用,这个阶段存在的bug比较多,程序往往不能持续运行,接仿真器可以打断点单步调试,定位问题比较细致,缺点是效率比较低,每调试完一次便要重新启动系统重来,系统无法持续运行(因为这是个同步系统,一旦进入断点,同步关系便失效了);后一种方案使用于测试后期,这个时候细节方面已基本有了保证(若无保证,那么用这种方式就是自找苦吃),使用这种方式可以从整个系统的角度来观察是否达到了我们所需要的结果,且就算出现了异常,只要不死,就能持续运行,反复观察。呵呵,看出来了吧,测试还是有点技术含量吧。
从以上测试方式的选择我们可以看出,集成测试遵循着从细节到整体的规律,在前期,测试人员要规划出支撑整个系统运行的关键点(这可不简单哦,要对系统很熟悉,并正确地挑出哪些是关键点,当然关键点越多越保险,但是同时要考虑到时间成本),并对这些关键点进行排序,哪些需要先做,哪些后做,哪些可以一起做,然后针对每个关键点设计出较合理和完备的测试用例(当然,这个也不容易,需要有测试的基本素养),然后逐一地把这些用例过掉。测的过程中如果出现问题,那么要能初步定出可能的原因,然后找相关的研发人员过来定位,然后倒上杯咖啡,慢慢欣赏他们那紧张的表情,时不时地指点两句。不过对于我们这个项目,测试人员是我们,研发人员还是我们,精神分裂中。。。。。
当这些关键点一一打通,OK,我们将进行最后一个环节,集成测试的后期,不过先别高兴太早,这只是意味着我们的系统貌似可以运行起来,不会千疮百孔,错误百出。你会发现当我们冒充客户,做出一个最最最最基本的要求时,#^E$&^#^*&,怎么了????居然没有我们可爱的上帝需要的咚咚出来!!!请不要慌张,更不要悲伤,这就是现实,残酷的现实,所有细节的OK并不等于它们加起来是一个OK的东西。怎么办??还能怎么办,查吧,不然要这个阶段干吗!
一个好的测试人员,在这个阶段,他必须能够十分清楚系统的运行流程(这个系统是他一手缔造的就更好了,俺们老大就是这样的人哦),设计合理和完备的测试用例,敏锐地发现测试中出现的异常(不要像我一样异常了还在那傻呵呵的笑,或者一点风声鹤唳就大叫UFO来了),细致地分析现场哪些是线索,哪些是垃圾,甚至是错误的信息,初步定位会是哪些模块出现了异常,并逐一排查,这里重点说说排查。如前所述,在这个阶段采用的是第二种调试方法,所以我们在各个模块都加入了大量计数器等等,可以使我们在不打断系统运行的前提下,通过读取内存的方式,得到计数器的当前值,这些计数器记录了各个模块在运行中一些重要的信息,在排查阶段,一般情况下,这些计数器就能够提供足够的信息来定位出究竟是哪个模块不好好工作了,如果需要,那么继续在模块内添加相应计数器,进一步的定位,定位的差不多了,那么我们便可以在离线环境下复现出同样的场景(当然有时候如果问题对线上环境很敏感,那么只能是线上精确定位+代码走查了),从而将bug找出,消灭之。在整个排查过程中,测试人员要为研发人员提供足够和正确的线索,宏观上把握排查的方向,微观上提供思路。
经过了单元和功能性测试,集成测试后,我们的产品也算是梳梳洗洗可以见人了,不过要作为一个真正的商用产品,这还差着十万八千里呢,还要进白箱,黑箱,反正早着呢,我30岁的时候能商用就不错了,咳咳,这里就不多说了。流水账后,俺作为一个刚刚到门里转了一圈的门外汉加强版,稍微总结一下我对研发及测试的感受:
1,研发注重细节,对每个模块需要认识很深,这一点显而易见,因为这个模块的每一行代码都是你写的,在这个世界上,没有其他人比你更了解他,你对于你的模块有着最高的发言权,是不是很有成就感呢,呵呵。
2,测试注重整体,测试人员对于某模块的认识肯定没有研发人员细,但是他对这个模块该干什么,不该干什么,以及在整体中的作用与位置是非常了解的,研发人员就不一定知道这些,测试人员有一个整体把握的优势,在测试过程中往往还要指导研发人员的定位,是不是有点作领导的感觉呢?
3,研发中有测试,测试中有研发。研发人员也得做测试,也得有一定的测试能力和思想,这一点我正文中说了很多了,就不多说了;反过来,测试人员也得写代码啊,测试用例不就是代码吗?一个好的测试用例,也是具有创造性和艺术感的。
4,作为测试的上游工作,研发过程中,必须考虑到测试。最基本的,比如说内存的申请与释放,数组的越界,这些如果不在开发过程中就有意识的保证其正确性,那么到了测试阶段,一旦出问题,是相当难查的。而要求更高一点,就是要求我们的代码可测性要好,有意识的编写出容易测试的代码。
5,对于人的性格,研发要求沉稳、守规矩、考虑问题全面而周到、逻辑思维强;而测试要求细心、耐心、敏锐、思维活跃、有宏观意识。
6,工作环境方面,研发一般在自己工位上呆着就行了,测试得有一定比率的时间去测试线、实验室,感觉测试较辛苦,另外,测试可能需要反复测同一个用例,反复敲相同的命令,不断地观察同一些现象,相对枯燥乏味些。
好了,虽然码了不少字,其实我对研发和测试还只是一个很短时间的接触,这仅仅是处于个人职业生涯初级阶段的一点小心得,认识和理解都还非常的粗浅,甚至有些可能是错误的,还望见谅,呵呵~~