最近半年一直在应各业务团队的需求优化自动化测试框架,简化case的编写和维护。随着用户对测试框架的使用不断深入,对测试框架提的需求也越来越有挑战性,也越来越“变态”。所以,也一直在思考,这些需求哪些是可以接受的?哪些可以变通接受?哪些应该直接拒绝呢?这就涉及测试框架的底线的问题。
我一直认为自动化测试中还是应该以人为主,工具和框架为辅,所以这里指的测试框架指的是辅助case编写的框架,类似JUnit之类(不过主要用于功能测试中),并不包括一些录制回放工具。
我们都知道,自动化测试的成本很大一部分都在维护成本上(倒觉得编写的成本是次要的)。一个case一旦写成,我们当然期望它一次又一次的运行,并且随着系统的演化而不断演化。要让case可以演化,就必须让后来人能够理解,能够看懂case。而要让case很好懂就必须很容易看出case的上下文环境。这就像我们理解一段话,如果单独从一篇文章中摘抄一段话让我们理解,我们可以觉得很费解,或者理解偏差,这就需要这段话所在的上下文来帮助我们理解。 case也是一样,一个case不管是测试一个接口,还是测试一个业务流程(完整的或片段),都应该能展现出case的全貌,能够清楚明白的倾诉它所在的上下文:这个业务场景分成几个大的步骤、在执行之前初始化了哪些数据等等。当然,这并不是说一个case要事无巨细,表达出所有细节。相反,应该在不影响理解的基础上,尽量隐藏一些细节。
打开一个case,要让阅读的人能感觉这个case在讲述一个完整的“故事”,更不应该产生case的运行与case的表现形式之间的gap。比如,这个case实际会运行三大步,但是因为测试框架提供某种magic tool,让我们在case里只能看到一步。这样虽然看起来case是简单了,但是却增加了理解的复杂性。就比如很多人写单元测试时会将一些初始化的公共工作提取到 Before里,这样表面上看起来每个case都变得更简单了,但是每个case却丢失了它一部分上下文,如果这个上下文对理解case非常重要的话就更不可取了。这个时候我们应该采取另一种重构手段:把本来应该放到Before里的公共部分抽取到一个公共方法中(并且给该方法起一个很好的名字),然后在每个case之前插入该方法。这样麻烦是麻烦了点,但是上下文立马就倾诉出来了。
所以,我一直坚守的一个原则是测试框架应该尽可能为用户提供抽象组合的可能性,只要用户能够想出的抽象,框架都应该能很好的支持,但要明确的是这些抽象都是用户构思出的。测试框架不能越雷池半步,不能自作主张的提供某种magic tool将某些细节隐藏于无形。比如测试框架可以提供关键字支持,将一些细粒度的操作抽象为关键字复用;提供DSL支持,将一些通用的业务操作封装成DSL;提供数据驱动的支持封装通用数据。
举个例子:比如我们有一个业务场景-生单。生单包括search, booking, order三个大步骤。这三个大步骤里又包含若干小步骤,要调用若干个接口来实现。那测试框架可以干的就是提供一种抽象手段,让用户可以将这些小步骤封装出这三个大步骤,而测试框架不能干的是阅读 case时只看见一个步骤-order,但实际执行的时候search和booking却都执行了(作为前置步骤)。这是底线一。
框架的第二个底线应该是不断地引导用户向简单case这个方向走,而不是随着功能增多,让用户可以写出更多更复杂的case。如果你认为提供这个功能后,用户会利用这个case写出更复杂的case,那就应该思考我是不是应该提供这个功能。功能自动化测试是一把双刃剑,它的收益也是一个抛物现状,如果 case的复杂度超过了一个度,那么收益曲线很容易就越过抛物线的顶峰,急剧下降,然后成本很容易超过收益。所以如期让用户写出更复杂的case,倒不如干脆不提供该功能,让用户要麻烦十倍百倍来实现那个case,或许聪明的用户就会寻求别的测试方式,比如单元测试。
额......动笔之前觉得好多想说的,动笔之后发现没什么好说的,但又好想吐下槽(不吐难受啊),所以写的烂糟糟的,欢迎指教。
原文转自:http://blog.sina.com.cn/s/blog_6ee6888a0101l5fu.html