2008年12月3日星期三

真实!真实! 真实!!!

昨天Cruise的功能测试通过了,四十多天来的第一次,团队所有成员都欢呼雀跃,为了这此通过,无数随机失败的测试被找出来,一一修复。 为了让测试运行的速度更快,我们新建了开源的项目test-load-balancer,为了让测试可读性更好,我们扩展了harmcrast。为了应付在不同平台下的测试,我写了Prerequisite

有同事问我,让测试通过很难么? 很难,我们花了40天才得到了一次通过的构建, 我们花了3个月的时间,理清了功能测试的思路,将原来的测试一步步移植到了Twist上,我们花了超过1年的时间从之前失败的经验里汲取教训,学习如何进行测试。值得么? 值得!现在你能站在团队成员身边就能感受到满满的信心,很多信心。 开发变成了一种享受。

变化的起因是:真实!真实! 真实!!! 在尽量真实的环境下测试。

谈起单元测试似乎就离不开Mock,在Mock的环境中搭建的测试就好似沙上建塔,很漂亮,很快,却经不起真实环境的推敲。 Cruise需要跟不同的版本管理工具交互,最实用的方法就是在测试中启动一个版本管理工具的服务器,然后一一测试checkout, sync等功能。没错,它很慢,非常慢。我们有运行时间超过一分钟的单元测试。但是这些测试让我们学习到了WINDOWS, REDHAT, MACOS在某些操作上令人惊诧的不同。 更重要的是,当它们通过的时候,我很有信心,我相信Cruise在与这些版本工具集成方面不会有问题。

使用Mock的最佳实践就是不要使用Mock(李晓 不要把Mock当作你的设计利器), 改进设计才是王道。在我们几乎清理了项目中所有Mock的时候,本地构建时间已经超过了20分钟,全部构建时间在一个半小时左右。后果是我们不得不编写了test-load-balancer,因为一样的原因mingle团队诞生了dtr。 从另一个角度看,让我对Cruise(可以进行并行构建的持续集成工具)的未来充满信心,长时间的构建不可避免,Cruise可以提供成本最低的加快构建的解决方案。

再来说说功能测试,我们之前的方案是在测试环境下启动产品(调整产品的某些参数,调整产品的启动方式等),通过使用数据库设置测试必须的数据环境,然后通过Selenium进行页面操作和验证,测试结束后利用DBUNIT来清理环境。 这样的测试除了验证了测试环境的产品能正常工作外,什么也说明不了。这也是为什么以前虽然也有很多测试,我们却在发布前依然诚惶诚恐。

我承认,使用真实的产品进行测试很复杂,因为很多测试必须的数据设置和查询都难以在外部完成。但是这样单一的,封闭的使用软件的方式是我们希望的么?开放的产品(基于Open API + Restful的设计), 让测试变的更容易,同时也给用户带来了好处(界面进行的重复操作可以通过脚本自动化)。

我们现在的方案是直接在产品包上进行测试,数据的设置通过REST URL进行,同时利用公布出来的JSON来判断产品是否已经被设置为我们希望的状态,再利用selenium进行页面的操作。在这组测试完成时,我们可以对最终交付的软件的质量做到心中有数。

这样的测试方式对开发团队也提出了挑战,产品必须可已在在不同的平台下中通过命令行安装、启动、关闭、卸载,在linux的平台上这点非常容易做到,不管是aptitude还是pkg都通过命令行进行包管理,即便是通过shell启动也可以运行"KILL $PID"轻易的结束进程,而在WINDOWS平台就比较困难(但也是能做到的,只是比较丑陋),再就是如何确保测试可以在一个Repeatable的环境中运行,所谓Repeatable,是说对于每个测试运行前,产品可以被设置到确定的状态,当测试真实的产品时,这点是比较难于做到的,说到底是在两个进程间共享信息,能采取的手段比较有限(Open API + RESTFUL是个比较好的选择)

检验测试好坏的最好标准不是测试覆盖率,而是信心和安全感,如果你所在的团队对测试通过后发布软件信心满满,那么恭喜你,你们在作正确的事情。

没有评论: