项目背景介绍:
最近在给一个团队做测试用例的重构,重构最主要的原因是由于不同team的测试执行了相同的操作,但是却有不同的检查点。偏偏这些操作又是非常耗时的,所以从整个部门的角度看,这些测试的效率就是不高的。然后我们又在做持续集成,自然对测试的效率有极高的要求,所以就决定做测试用例的重构,PO分配了一些有经验的测试工程师来做这个事情,我帮助他们解决自动化测试上的问题。
问题介绍:
涉及到的软件模块有一个是大型电信分布式系统的管理模块,主要管理系统的告警,故障恢复,启动时序等功能。模块的功能基本上可以理解为各种各样的policy的制定。(这个模块有点类似政府部门,呵呵)
比较有意思的一点软件功能的边界有很多模糊的地方,用通俗的话说就是“水很深”。有点像中国的政策,按原则是一套,但是实际执行的时候考虑到一些系统自身的限制,又做了很多妥协,所以就造成了系统中的诸多灰色地带。
对测试人员的影响是碰到问题以后,和开发人员沟通,往往最后的结论是诸如“一般情况下应该是这样的,但是现在出了不一样的情况也是正常”的等等。(作为一个测试人员,你会不会郁闷?呵呵)
对于手工测试人员来说,经过“打磨”一段时间后,这样的“弹性”是可能去建立的,但自动化测试却不同,往往是自动化测试的期望结果是简单一刀切,结果失败了并不是软件真的需要改的Bug。限制地松了,则可能任何问题都发现不了。所以测试人员挺郁闷的,都开始反感自动化测试了!
问题原因的分析:
我们分析认为,以上问题的原因是对于该模块,一条基本的需求可能就会存在多个分支的期望结果,要把这些不同情况下的需求的细微不同描述清楚,纯粹通过Robot原生态提供的关键字,还真有点勉为其难。
当然我们可以扩展Robot的语法,但是这会丧失了Robot表格化编程,以类似自然语言写测试用例的精神。我们也做过这方面的尝试,效果不是十分的好。
在以前的自动化测试用例中,对于这些灰色地带,其实是检查地没有这么严格的。但这样的风险是,我们对于系统的边界在哪里,其实并没有一个清晰的认知。有一些设计上的概念,可能并没有自动化测试有效的保护,也没有被严格地验证过。对于一个高可靠性的电信软件系统,这是难以接受的!
而且 在持续集成中,我们不希望碰到非软件原因引起的测试失败,所以这也是一个必须解决的问题。
为了解决这个问题,我们采用了以下的手段:
1. 在Robot里面只描述非常high level的需求,而细节的需求大量地用python来精确描述不同情况下期望结果的差别,但又是在系统功能的允许范围之内。python的语言特性让我们在处理一些分支情况的时候非常得心应手,又能够保证很好的可读性(其实在实践的时候我们采用了基于模型的测试方法,把测试需求通过模型化的方式来描述,然后动态生成Robot Case,但这个不在这篇文章的讨论要点中)
2.内部重新封装了Robot的run_keyword方法,基本上可以控制哪些方法执行了以后作为测试步骤的一部分显示在最终的日志上,哪些则只是一些分支控制语句,在最终的报告上不会生成。最终测试人员看到的报告,只会比他们纯粹的Robot Case的日志更加简洁,更加的具有可读性。
3.对于某些复杂的解析函数写UT,由于产品的可测性不是很好,在解析产品输出的时候,不得已做了很多妥协,引进了一些复杂的正则表达式。实践表明,对这种函数写UT还是很有必要的。 不过或许是我们语言功底太差,呵呵
4. clean code思想对我们很有帮助。经验表明,把代码写复杂很容易,但是把代码写得简单并不容易。测试人员的代码功底毕竟还不是很好,所以需要有经验的人在旁边加以指点。在指引他们写清晰的代码的时候,一开始不用马上引入一些新的名词,比如SRP(Single Responsibility Principle) ,面向对象的设计等等,而是通过pair work的方式,让大家看到实际的例子,看起来很复杂的代码如何可以变得简单…
鼓励大家把函数写得尽可能短一些,函数和变量的名字尽可能有意义一些,一个函数的名字不要挂羊头卖狗肉,言行不一
一个函数应该只有一个抽象层次……
大家的悟性都还不错,当看到自己写的比较乱的代码原来可以整理的很干净的时候,自然而然会希望朝这个方向靠拢。
5.充分利用python动态语言的特性。比如python的decorator功能,可以对函数做一些动态行为的修饰,可以使代码写得更加简洁,去掉不必要的冗余代码。再如我们内部开发的keyword wrapper的功能,可以实现一个函数名词映射到多个不同的实现,然后在运行的时候去寻找适合当前版本的实现,可以使我们的测试代码灵活兼容不同的版本,等等。
6. 对代码书写必要的注释,但重点在Why to Do 而不是What to Do
效果:
我们用较短的时间,除了重用了一些以前身经百战的测试函数库外, 基本上相当于重写了之前的很多测试用例,改进了之前测试的coverage, 而且发现一些系统行为和我们预期的行为不同,相当于对被测系统有更深的认识。
发现了一些疑似或真正的软件Bug。
自动化测试用例的稳定性比以前改进不少,测试人员对于自动化测试的结果更有信心了。
对于我们重构过的测试,能否真正做到不碰到非软件原因引起的失败,还得继续观察,估计也没那么完美,呵呵,但我们应该可以做到发现问题,随时改进
我们得到的体会:
1. 测试代码也需要不断的重构。似乎有位大师是这么说的,“对代码要无情的重构”,“无情"这两个字说得好。对于你感觉不好的东西,比如重复的或者可读性性很差的代码,不要因为自己写的,或者花了很多时间就心慈手软,应该是”挥泪斩马谡“,毫不犹豫的把它们改掉。
2. 测试代码其实比一般的产品代码更需要clean code,因为测试代码是写给更多的人看的,是描述需求的边界的,一旦需求理解错误,其实比产品的实现理解错误更糟糕,呵呵。这点不知道大家是否认同?
后记:
我们的经验可能和我们采用了基于模型的测试(MBT) 这样的实践有一定关系,MBT要求我们的high level Keyword,或者也叫Test Action,要能表达特别多的信息量,所以才迫不得已把很多需求信息放在python中来描述 。 毕竟这也会失去一些Robot Case的优势, 如果你的实践不同,能否借鉴还需要具体问题具体分析。
注:
Robot是一个关键字驱动的ATDD的自动化测试框架,详情请见http://code.google.com/p/robotframework/