自动化测试实践:自动化测试的思考总结

目前所在的团队一直主要负责部门产品的自动化测试方案。团队在自动化测试方面已经有了几年的探索,但直到最近才真正发挥作用大大提升了产品的测试效率,在这个过程中走了很多的弯路。
以下是我这一个阶段以来做自动化测试的思考及总结,有一些大的方向上的讨论,也有一些细节的探讨,希望给大家参考。

关于要不要做

相信还有很多人对自动化测试有疑问,或者因为种种原因不愿意引入自动化测试。以下总结了一些反对自动化测试的论点:

  • 自动化实现较难
    一般领域内都有可用的自动化测试框架,是这个领域内大家在自动化测试领域探索后的经验总结。在使用正确框架的基础上,自动化测试的实现并不复杂。
  • 自动化成本太高
    下文中将会详细解释,如果设计合理,并有正确的预期,自动化的收益远大于成本。
  • 项目规模较小,没必要做自动化测试
    以我们自动化项目的经验,越早做自动化测试,越容易实现,成功率越高。因此可以说成本越低,受益越高。
  • 会限制产品的灵活性
    需求的频繁变更确实是自动化的灾难。但一般如果需求需要频繁变更,往往预示着其它问题。

手工测试的局限

  • 大量机械、重复性的工作效率较低、测试结果可能不准确
    总结一个词:boring
  • 很难满足需要模拟大量数据、大量并发量的测试场景
    比如无法实现系统可靠性测试,如对关键业务进行持续数十天的7*24小时测试
  • 很难适合敏捷开发过程,无法在短时间内完成大量的测试用例的回归
    对于测试标准严格的大型软件产品,测试用例的数量可能是以“万”为单位的,全部手工回归一遍需要几人月的工作量!

自动化测试的引入

自动化测试就是使用软件来控制测试用例的执行,将实际测试结果与预期结果进行比较,并提供测试预置条件设置、测试逻辑控制以及测试报告生成等重要功能。
软件甚至计算机、机器的出现,是为提高人的效率。既然机械、重复性的劳动较多,为何不将这部分工作软件化、自动化?
自动化测试可以极大的提升回归测试的工作效率,并简化稳定性测试、兼容性测试等负载类测试的难度,在保障产品质量和持续构建等方面起到举足轻重的作用。

自动化测试的好处

简单说几点自动化能带来的好处:

  • 快速接收;
  • 持续回归;
  • 节约人力成本;
  • 完成人力难以实现的测试,如扩展到性能及稳定性测试。

当然有些场景下自动化测试是不适合的。

自动化测试的代价

  • 开发维护成本
    特别是从零开始的阶段,自动化会有些挑战。其次是提高测试代码的稳定性,需要付出很大经历。
  • 对开发流程的挑战、对开发规范的检验
    自动化测试非常依赖标准的流程,如果没有较好的开发规范,开发人员随意变更代码、测试用例维护不完善,都会导致自动化的推动困难重重。

对自动化测试的正确预期

很多自动化项目失败的原因是对于自动化没有合理的预期,大多期望太高。

  • 自动化测试无法取代手工测试
    只能取代其中机械重复的部分,“无脑工作量”,越高的智能需要越大的投入。单为测试来说,难以实现且不太值得。
  • 自动化代码需要持续的维护
    提高自动化的覆盖度、提高稳定性、与需求变更保持同步,所有这些都需要持续投入进行开发维护。
  • 但自动化测试确实会带来很大的好处
    成功的自动化项目能为项目节省至少50%的人力成本,且可以实现一些手工难以实现的功能。

关于如何做

接下来探讨一下如何做的一些问题。

自动化测试的前提

一个项目、产品,可不可以、值不值得做自动化测试,主要有一下几个前提需要考虑:

  • 需求变动不频繁
    如果需求,特别是测试的接口(如界面、输出的结果展现)变化频繁,自动化代码的维护无法赶上产品迭代的速度,这种情况下不建议引入自动化测试,而应该等产品稳定、需求收敛后再考虑自动化。否则会导致自动化成本过高,而实际未来得及使用就已经失效了。
    但同时需要注意的是,如果在项目开始后还频繁变更需求,那往往是项目遇到了比较大的问题。此时应该慎重的考虑整个项目,而不只是仅考虑自动化是否该引入了。

  • 项目周期长,并且有迭代
    项目周期长,才有足够的时间开发自动化测试代码,并确保能真正用上。如果自动化未完成,项目已经结束,那自动化就多余了。
    而且最好项目有持续迭代。否则如果只需要测试一次或几次,自动化带来的收益太小了。

  • 模拟可以实现并且是有效的
    自动化的过程就是模拟人使用软件的过程。对于无法用软件模拟的动作(如验证码的测试),是无法自动化的。
    并且模拟必须是有效有价值的。朋友分享的案例是app测试时的定位问题:app中用到了定位,为了方便测试,编写了修改定位的脚本对app进行测试。但实际测试后发现无法测试全面,很多问题只有在实际场景中才会出现。

从简单做起

二八定律同样适合于自动化测试:提高自动化覆盖度的过程中你会发现80%的工作量集中在20%的测试用例上。然而这也意味着,我们往往只需要投入20%的工作量,即可实现80%的自动化。
因此自动化的过程要从实现的简单到困难,开始阶段往往收益比例是最大的。举例我接手的一个自动化项目,测试输入是5000+网站,其中80%只有打开首页这一个动作,10%还需要一个登陆动作,剩下只有不到10%才涉及发帖子等其它复杂的、个性化操作。如果按照从易到难的顺序实现,很容易就可以自动化80%,甚至90%,剩下的10%仍然采用手工测试都会大大减少人工测试的成本。

按照标准项目来做

绝大多数自动化项目最终失败的原因是没有把自动化作为一个正式的产品项目来看。
自动化测试的项目也是一个标准的软件项目,也应该遵循一些标准的开发流程。如前期也需要确定需求,也需要考虑敏捷、持续迭代,也需要做好自己的测试。
实现中容易陷入一些困惑,其实是因为我们忽略了某些流程。典型的如没有做好前期需求收集的识别,完全按使用者说的去做,结果做出来一个并不实用的傻瓜式的自动化方案。再比如没有做好测试,导致测试脚本不稳定。这些都可能导致项目流产。

要不要做成傻瓜式

自动化的一个比较大的陷阱就是试图做成傻瓜式,特别是没有专业的产品经理为自动化提取需求的情况下。以下是一个典型的傻瓜式自动化测试需求:测试人员只做测试设计,再做一些配置或工具录制,自动化的测试用例就生成了。之后所有的测试工作交由自动化测试完成。
事实证明,这种方案的代价远远大于收益,缺点如下:

  • 傻瓜化往往需要开发额外的工具
    越智能的工具,带来越高的开发成本。而且这些工具往往不通用。
  • 自动化脚本质量不高,且维护成本太高
    比如以录制来说,自动生成的脚本往往有各种问题,效率不高,需要手动调整;而且一旦交互或需求变化,可能很多用例都需要重新录制。而且可能会生成一些复杂难以被人理解的代码,遇见问题时调试困难。

  • 所需的培训成本并不低
    这些工具往往需要使用特定的规则。比如之前看到一个自动化方案,通过一些规定的词汇指定各步骤,然后解析自动生成测试脚本。任何一个词汇不满足要求就生成失败,还要智能的去解析变量,几乎就是重造了一个编译器。
    但其实回头想一下有没有必要创建一套特定的不通用的规则、重造一门语言?为什么不简单的去要求使用人员去学习一套通用的规则、学习一门语言,如python、tcl?只需要最简单的入门即可。

关于UI测试

如前所讲,自动化要从易到难,UI测试相比其它自动化测试(典型的如CLI测试),就属于比较难的阶段。
但若使用类似于selenium的UI自动化框架,实现并不是很复杂。但UI测试的表现不太稳定,需要付出很多的努力使得用例稳定。典型的如打开网页点击按钮这个简单的动作,实际测试发现总会有一定概率下,这个按钮不出现,而且重现调查也很难找到原因。
在这种情况下,建议先demo实验下,可行后从易到难实现。

测试阶段

哪些测试阶段应该自动化测试?严格说起来,只分为三个阶段:

  • 单元测试应该做,但更多应该开发人员,或者更详细一些,代码的编写者维护单元测试用例。
  • 集成测试和系统测试等开发迭代过程中的测试,应该自动化,并且是自动化的主要使用场景。
  • 发散测试/探索性测试,不应该自动化,因为要发挥人的灵活性,弥补自动化测试的不足。

关于实现细节

让我们深入细节,探讨一些深入的点。

工具或框架的选择

尽量选择开源的工具或框架:

  • 方便定制化开发
  • 非开源软件很多内部逻辑不透明,可能难以大规模扩展
  • 技术积累、沉淀,使用开源软件能使团队得到更多的技术积累
  • 节省费用,很多收费的自动化测试工具很贵,使用开源软件可积累一大笔费用

UI的自动化测试,推荐使用开源的selenium。在我们的项目中,使用selenium实现了绝大多数UI测试需要的功能,而且很容易扩展。
CLI的自动化测试选择要多一些,可以简单的使用shell脚本,或python、tcl等更加高级、功能更强大的语言。
针对某些特定框架、语言,如js,框架本身可能提供了一些自动化测试框架,因为它可能会用到语言或框架的一些特性,推荐优先使用。

测试环境

测试环境的自动部署也是很重要的。虽然不是必须,比如可以在手工搭建好的环境上进行测试。但是对于复杂的环境,或者测试的扩展是很重要的。
基础设施部分建议使用虚拟机+pxe实现,软件建议使用配置管理工具实现。

函数库封装

如前文所说,我们应该将自动化测试作为一个标准项目来开发。因此也应该进行分层设计,核心的库与具体业务解耦。

  • 核心库封装各基础功能,建议由自动化团队负责维护;
  • 具体用例通过调用核心库实现,由测试人员维护。

这样如果产品接口发生的变更(如登录发生了变化),只需要简单的更新核心库及部分用例即可。如果每个脚本单独实现,此时就需要更新所有依赖此接口的用例。
不要单独编写每个测试用例,简单到容易的过程中容易犯这个错误。
并且用例之间尽量保持独立,否则不容易扩展,逻辑太复杂。如果一个用例的过程被多次使用,那说明应该封装了。

sleep的使用

等待某些条件达成时,最简单的方式就是使用sleep。
但sleep的时间设置多少?如果过少,可能条件还未达成;如果过多,会导致执行变慢。而且不同的环境,需要的等待时间不同。
所有这些会导致测试的不稳定。因此因减少sleep的使用,而应该实时的去检测条件。这样会增加代码量,但会减少不稳定导致的错误。

关于弥补不足

自动化是通过代码模拟人的动作,存在天然的不足。如果能做到足够的精确和高效,那它所具有的人工智能的价值已经超越被测试软件的价值了。
幸运的是,我们可以通过一些不太高成本的方式弥补其不足。

不稳定如何解决

因为代码的实现及环境因素等,自动化代码难免会出现不稳定的情况,可采取如下方式:

  • 多次测试,多次测试一排除偶然性的影响,但也不能忽略偶然性bug出现的可能
  • 专人排查,对所有出错的用例进行排查,定位是产品问题、测试代码问题,还是环境问题。这就要求自动化测试过程中记录足够的日志信息
  • 持续维护,持续维护代码,保持与主干项目同步,并增强稳定性
  • 代码自测,做好测试代码的自测,提高测试代码的质量

加强探测性测试

自动化尽可能的模仿人,但相似度越高,代价越大;
探测性测试可以用较小的成本获得大的受益,同时可检测自动化测试是否有效。

如何跟踪软件迭代的进度

自动化的价值就是能及时的对产品进行测试,因此对开发进度有比较高的要求,特别是在项目持续迭代时:

  • 尽早介入,最好能在详细设计阶段开始自动化开发,但这需要与产品开发人员约定好接口
  • 模块化的封装,封装后可减少修改
  • 变更太快的项目,会导致自动化测试无法维护,只能等版本稳定

团队实际经验

最后说下团队实际的经验,大体分为3个阶段:

  1. 录制阶段
    此阶段团队的大部分精力用来开发一个录制的工具:开启录制后,只需要测试人员执行一遍测试,工具即可记录并自动生成自动化代码。同上所述,这个工具的开发本身是不太容易的,而且实际也很难用于别的项目,录制出的脚本也需要再去修改。遇到需求变更,一般都需要重新录制。
    开始的两年团队一直在重复这个困局:项目开始,团队开始开发维护录制工具;项目集成测试,团队开始录制维护脚本;项目接近释放,录制勉强完成,用来做一次验收测试;开始下一轮循环。
    此阶段自动化几乎无法实际使用。此阶段的成果在于通过虚拟机+PXE的方式,产品的自动部署流程基本打通。
  2. 脚本阶段
    经过2年的开发,团队发现录制工具并不实用,而且开发维护的成本较大,自动化进度很慢。因此决定同时采用录制、脚本编写两种方式,开始从易到难的自动化测试用例,并编写模板培训测试人员使用。
    因为很多用例步骤都是重复的,通过复制粘贴的形式,自动化了很多测试用例,自动化进度大大加快。
    这个阶段自动化覆盖度大大提高,但是代码质量不高,维护成本很高。
  3. 封装自动化库阶段
    最终终于将自动化作为一个标准的项目进行开发:产品经理协助提取分析需求;代码解耦,封装核心库,并持续维护提高稳定性,逐渐扩展到UI测试;持续培训测试人员实现自动化,并review其代码。
    到这个阶段,前期花费巨大精力的录制工具几乎已经被抛弃。
    经过这个阶段,自动化覆盖度达到90%,因不稳定原因导致的用例失败率降低到10%。

总结

综上,自动化测试绝大多数情况下是很有价值的。
但是需要遵循一些规则,建议将自动化测试项目规范的作为一个产品去开发,并尽可能将敏捷的思想应用其中。