回顾与展望

今天是2013年第一个工作日。

打开代码统计工具,发现12年总共提交了200多点change list,添加、修改或删除的代码大概30000行(不包括移目录)。就数量而言,作为一个头上顶着test头衔的工程师,应该还算不错,跟10,11两年总和相当;就效果而言,也实现了去年年初的愿望。现在负责测试的几个产品的单元、集成测试覆盖率,持续集成、测试水平,快速发布能力都较年初有了不少提高。两个测试工具(一独立开发,一辅助开发),也能够实际的发挥作用。这些只是平平淡淡的工作,更不是耀眼的成绩。当然中间自己也曾为这种”平平淡淡“苦闷过,但毕竟还是踏踏实实的认真做了这些工作。

如果说在这平平淡淡中个人能力有什么提升,那我想应该是对自动化功能测试的理解。之前两年做功能测试,更多的都是从模拟用户的角度来做。那时想的是如果能把手工测试的case给直接映射称自动化的测试了,那测试效率一定会有很大的提高。于是就用了一些黑盒测试,例如webdriver,selenium,PyWinAuto,Robotium(这个倒是可黑可白)的工具来模拟用户操作。这样确实是容易上手,因为不需要对产品的内部实现有丝毫了解,只需要站在一个纯粹用户的角度理解产品的功能即可。事实上,不需要一个月的时间就可以把大部分功能测试用例都自动化。当以为大功告成之际,却发现噩梦才刚刚开始。之后是不断的维护测试代码,不断的跟flaky作斗争,不断的跟复杂的不透明的测试工具造成debug困难较劲。然后是越来越没有信心,以至放弃所有的自动测试代码。测试代码也是一种技术债,此言非虚。00,01两年的几万行代码,最后也都不幸带上了deprecated的帽子。

我不确认是因为测试工具不好导致了这种黑盒自动测试的不稳定,还是让自动测试代码模拟用户操作以提高生产率这种思路本身就是错误的。不管这种纯理论的争论,我实际做的,是探索单元测试和end-to-end测试之间的灰色地带。传统上,单元测试是典型的白盒测试,而端对端测试则是模拟用户的黑盒测试,也就是之前我试图自动化的测试。在二者之间,则是可黑可白的灰色地带。
先讨论纯白的单元测试。何为单元?大部分书会认为单元是类或者方法。那测试这个单元,其依赖的类或者方法是要用真实的吗?如果是真实的,那是不是已经是某种程度的集成测试?如果是针对main函数,或者对外api的接口的测试,是不是已经是针对整个程序的集成测试?如果是这样,那应该反思白盒测试和黑盒测试的区别又是什么?简单的传统的答案是,白盒能看到被测试的代码,黑盒看不到。那什么情况能看到,什么情况看不到呢?我觉得如果测试代码和被测试代码在同一个进程内,那二者就很容易共享代码;如果不是,那就只能通过被测试代码(SUT)开放的一些接口来控制。
所以对于那些可黑可白的地带,如果只是限定于一个进程内的,本来我会使用黑盒工具,我现在可能更倾向于使用白盒工具,或者说普通的xUnit单元测试框架。那我为何倾向于白盒测试呢?因为如果直接访问SUT的代码,那我可以更好分析容易出错的地方和不容易出错的地方。对于测试重点,一定会使用真实的代码;对于非重点,也倾向于使用真实的代码;但如果非重点可能导致测试不稳定,我会用一些测试替身来替代掉真实的代码。我虽然损失了部分的真实性,但这样保证了测试的稳定性。白盒测试,测试代码对SUT有近乎无限的控制能力,所以测试代码也能够写的非常稳定。甚至,如果SUT的testability,可以很容易的重构SUT以适应测试。另一个很重要的好处是这些代码一般都是开发人员来维护的,对于测试人员很有限的公司,这点至关重要。
一个技术细节是多线程的SUT。可能SUT本身就是多线程的,也可能SUT和测试代码分属两个线程。此时要考虑线程间的同步。异步总是自动测试的大敌,所以无论如何,要有很好的同步机制以避免测试的不稳定。

于是以上面这种思路,开始增加对单个程序(或进程,组件)的测试。这类测试都是白盒的。例如对页面中的javascript,会以javascript单元测试工具通过发送browser UI事件来驱动UI的变化,此时的server都是用javascript来fake的;会以普通的android insrumentation这种单元测试工具写针对android ui的操作,虽然SUT和测试代码分属两个线程,但instrumentation已经作了很好的同步控制;对于server,可以对每个sevelet做JUnit的进程内测试,而不是跨进程的发送http request;甚至对于兼容性测试,也可以先分析导致兼容性bug的原因,然后针对这些原因以类似单元测试的方式来写测试代码。如此等等。大部分我们平时使用黑盒方法的测试,都可以用一种更稳定的白盒的方式来解决,而这并不需要“强大的”黑盒测试工具的支持,只需要简单的xUnit框架,对SUT的理解以及提高SUT的可测试性。符合这三项需求的,也自然是开发人员。
如果再往外跨一步,到了跨进程或者跨模块的测试的范围,我想可以写很少的测试来验证各个模块之间的通信正常。可以把他们放在hermetic server中以增强稳定性。甚至我觉得,这部分手工测试已经足够了,或者说,性价比更高。

该展望了。在写这类类似单元测试的功能测试和为自己的代码写单元测试的过程中,我发现自己甚至很多开发人员都欠缺把单元测试写好的能力。所以我想在完成领导安排的工作和对应产品需要我做的工作的同时,会花更大精力学习如何把单元测试写好。具体而言,要严格要求自己给自己的代码写好单元测试,认真通过study group来学习《xunit test patterns》以及别的单元测试书,帮负责产品的开发人员提高单元测试的质量,如果可能,能够写一个单元测试领域的工具。另一方面,今年想多编码,希望能到5万行,在过程中提升自己的编码和设计能力,在其中注重代码的可测试性。二者合二为一,我现在的兴趣是通过注重代码的可测试性来提高代码质量,进而提高产品质量。这里的可测试性限于白盒测试,表示产品代码有易读易重构且稳定的单元测试,也容易写出这样的测试。当然这一切,都是以完成具体安排的工作任务为基础的。

经常会在路上发现小狗追着尾巴玩。三十多岁的人,现在自己也像这样的小狗,没有多大的理想,只是盯着这根小尾巴,自娱自乐。

你可能感兴趣的:(回顾)