自动化回归测试

注:本文假设你对CucumberSelenium-WebDriver有一定的了解。

什么是回归测试?

回归测试(regression test)是QA对程序功能问题的验证,通常我们的做法是:
QA 手工测试 -> 报告bug -> Dev 修bug -> 提交到代码库 -> 构建程序包 -> 部署-> QA手工测试 -> 报告bug, 如此反复…

试想随着版本需求范围的增加,回归测试的测试用例也会如滚雪球般越积越多,在实施回归测试的过程中,因此,手工测试对于QA来说是重复的、乏味的。

让我们来看一下回归测试的定义:

回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。
回归测试的目的是,通过了回归测试的软件,至少其基本功能是可用的。

回归测试应该覆盖哪些内容?

从上面的定义,我们就可以识别出哪些测试用例需要被包括到回归测试中:

  1. 与外部件集成的功能。
  2. 主干功能。
  3. 容易被break的测试。

自动化回归测试

既然测试范围已经确定了,接下来就要考虑如何减少这些重复的工作。我们很自然地想到了自动化。随之而来的问题是:

  1. 我们如何实施自动化?
  2. 我们的项目时间很紧,如何在不影响项目进度的前提下实现自动化的回归测试?
  3. 对于web项目,如何保证其在各个浏览器下面都能够正常工作?

对于第一、三个问题,有个成熟的方案,selenium-webdriver,其支持多种语言API, 包括Java,C#,Python,Perl,PHP,Ruby。也支持对多种浏览器的调用,可以模拟多种浏览器下对app的访问,并且支持对结果页面进行检查。 对于第二个问题,在经历过几次不是很成功的实践之后,我个人很反对对项目采用”休克疗法”,即完全停止目前的工作,采用另外一种看起来更好的方式来解决当前的问题,最具代表性的例子就是”打着重构的幌子进行重写”,在所谓的”重构”完成之前,项目其他成员的工作都是被阻塞住的,而且一旦”重构”失败,也难以恢复到”重构”前的状态。

在这里,我推荐一种循序渐进的方式逐步实现回归测试的自动化。
举个例子: 在确定了回归测试的范围后,我需要测试一个网上书店的从最新书籍列表进入书籍详情页面的功能,我们分别用普通的测试用例和DSL描述的用例:

普通的测试用例:
预置条件
数据库中有三本书,其信息如下:

          ISBN          书名                       作者            价格 
         111111    Head First Design Pattern      Somebody         12
         222222    Test Driven Development       Kent Beck         22
         333333         Refactor                 Martin Fowler     20

操作步骤:
进入书籍列表页面,点击书籍”Head First Design Pattern”的链接
预期结果:
进入书籍《Head First Design Pattern》详情页面,能够正确展示书籍名,价格,作者

使用Cucumber DSL描述的测试用例:

    Given There are books as follows : 
        | ISBN   | 书名                      | 作者          | 价格 |
        | 111111 | Head First Design Pattern | Somebody      | 12   |
        | 222222 | Test Driven Development   | Kent Beck     | 22   |
        | 333333 | Refactor                  | Martin Fowler | 20   |
     And I am on the book list page
     And I follow "Head First Design Pattern"
     Then I should be on book detail page
     And I should see "Head First Design Pattern" as title
     And I should see "12" as price
     And I should see "somebody" as author</span>

渐进式实现自动化回归测试

比较这两种形式的测试用例,我们排除语言实现的差异(中文和英文,而且Cucumber也是支持中文DSL的), 它们的共同点是,都是人类可以理解的语言,任何一个QA都能够编写上述两种形式的测试用例。不同点在于,使用Cucumber DSL描述的测试用例可以在以后的某个时间点很容易地转换成自动化测试用例.

于是,我们可以在项目中采取如下形式逐步把回归测试自动化:
阶段一: 确定回归测试覆盖功能点的范围, 使用cucumber DSL描述测试用例 手工执行这些用例 阶段二: 利用项目间歇期,把这些使用cucumber DSL描述的测试用例转换成自动化测试。 此时,项目中回归测试会存在自动化和手工测试两种形式,部分地节省了人力。 阶段三 : 所有的回归测试用例都被实现为自动化测试。 这些测试都是可重复的,可以大大节省QA手工执行测试用例的时间。 后期对回归测试用例的修改都相应地将其自动化。 就像重构一样,你可以在上面这三个阶段中的任何一个时刻停止,你也可以在停止之后继续。 如果你能够在你的项目里实践到阶段三,那么恭喜你,你们已经做到了让合适的人做合适的事情了。

    所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。这使得只要目标对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新的值, 和上次那次调用的是各自独立的

 分享了个关于closure的ppt在这里

东拉西扯

公开课

最近看了两个比较有名的公开课:《公平》和《幸福课》 《公平》告诉了我:在很多时候,人们不得不做选择,这些选择没有对错,只是代表了每个人不同的价值观。 正是这门课程,颠覆了我从小就被教育的非对即错,非黑即白的观念,那些被我误解了的人们,在这里跟你们说声对不起,原谅我当年的无知与鲁莽。 《幸福课》的老师刚出场的时候,他身上的那种气场就感染了我,即使是通过电脑屏幕,你依然能够从他的动作,他的眼神、他的语气上感受到他身上的那份宁静,心如止水,宠辱不惊。记得有这么一个笑话:”幸福是什么?幸福就是,我能吃着,你看着,我坐着,你站着”。这是最简单的物质的幸福,试想:当你从那个"看着"、"站着"的人变成"吃着"、”坐着”的人的以后,你得到幸福了,然后⋯⋯ 你的幸福感会持续多久?现在我还记得自己得到第一个手机、第一个笔记本电脑、第一个MP3播放器、第一个智能手机、第一个iPod的那些时刻,那种幸福或许会持续一天、一周或者再久一些,但是终归会消逝于平淡,这种通过获取物质而得到的幸福感是短暂的,不可延续的。只有获得了精神上的宁静,幸福才是长久的、延续的。

团队

再来说说团队里的事情,前几天跟一个一起入职的同事聊天,都谈到了一种共同的感觉: 因为事情比较少而感到了一种不安全感,其实就是姜鹏的这篇博客 里提到的存在感,一个人在团队里有了存在感,这种不安全感就会随之消失。

自己

再来说说自己,综观自己这短短二十多年里遇到的各式各样的人,有一种人看起来总是眉头紧皱,脸色阴沉、有着说不完的抱怨, 你能明确地感受到他/她不快乐。然而也有另外一种人,你总是在某个时候听到他/她爽朗的笑声,你很少能够听到他们的抱怨, 他/她有很多兴趣爱好,总是有说不完的话题,也很乐于分享。 不幸的是,我更接近于第一种,而在ThoughtWorks,你会发现大多数人都更接近于第二种,同样,放下轻车熟路的java进入ruby on rails 的世界也是。都会让我感受到一些压力。 一开始,是有点不适应,在读了《哪来的天才》这本书之后,对于这种压力有了新的认识,对于我来说,是从”舒适区”进入了”学习区”。这才是进入了提高的通道。

同事

最近公司邮件组里发起了关于如何在国内开展Social Impact Project的大讨论,其中有个比较好的点子是: 帮助留守儿童与他们外出打工的父母保持联系,以弥补空间带来的情感上的隔离。 我才意识到原来这帮人不仅仅是在为自己讨生活,他们确确实实在考虑着如何通过自己的一点努力来帮助那些需要帮助的人们, 能与这样一群人一起工作,真是一件很幸福的事情。

ThoughtWorks入职一月

     在最近的两周里,和一个同事一起搭建一个持续交付的demo工程,到目前为止,已经基本接近尾声。不过, 这个demo之前是根据一个ruby应用搭建的,但是考虑到国内目前的软件行业的现状,ruby仍然是一门小众语言,因此考虑通过一个java应用搭建一个CD Demo,这就是后面一周的工作重点啦。

成果:

  • 设计了一个包含了Go master、Go agent、puppet master,target nodes, Dev Env演示环境的部署策略:
  • 使用shell编写了一个简陋的rails应用远程部署脚本。
  • 对于puppet有个基本的认识,使用puppet搭建了上述的环境的基础设施。

收获:

   1)  对bash 有个基础的了解,知道了interactive shell和non-interactive shell、登陆shell与非登陆shell之间的区别(他们启动的startup不一样)

   2)  更加体会到了tasking的好处,尤其在考虑上述5个部件之间的交互关系时,采用这种方式能够帮助我理清思路,找出合理的解决办法。

   3)  对于SSH有了基本的了解,知道了可以通过发布公钥在多个节点之间建立信任关系,用shell脚本实现了通过ssh远程登陆目标机器完成相应操作。

 

计划:   

  • 了解open stack,目前仅知道这是一个开源的云计算平台软件集合
  • 搭建一个open stack环境。
  • 搭建一个java项目的持续构建、自动部署平台。

遗留问题:

  1) 对于rvm加载的机制尚不太清楚,需继续研究

  2) 对于maven还是不够了解,需要继续学习,通过maven对该java demo项目进行构建

好的编程习惯

 

昨天郑大晔校的主题是 “如何养成好的编程习惯”,只有养成好的编程习惯,当你在时间紧任务重的情况下仍然能够写出风格、质量良好的代码。

分组讨论后,每个小组都给出了自己认为好的编程实践,我把大家的想法总结了一下,并且根据这些实践的侧重点,分成如下五个方面:

任务规划:  Tasking,TimeBox

     关于Tasking,有两个大家最关注的问题

          ——如何确定tasking的粒度 ?

          结论:task的粒度是由开发者的能力确定的,

                   基本的评判标准是:如果你能够用一个测试用例描述这个task即将实现的功能,那么这个task的粒度就是合适的。

         ——如何划分task?

          结论:不需要一次性把所有task划分清楚,可以从最简单的task入手,在实现简单task的过程中,你会逐渐识别出新的task,

          把这些新的task加入task list中,待第一个task 完成后,再从task list中获取下一个task,如此持续下去直到所有需求都完成。

          推荐阅读《Test Driven Development》这本书,书中很好地演示了“Tasking”的实践。

     关于TimeBox,有同事推荐了一个比较有名的实践 tomato time,以15~30分钟为一个tomato time,有计划,有节奏地工作。

软件设计:  简单设计,先实现再重构,小步前进

     简单设计: 在软件设计上,一个程序员最容易犯两个错误——没有设计过度设计,两个极端。

                    对于一个刚入行的程序员,他/她最容易犯的错误是没有设计,比如,在一个类、一个方法内完成所有的功能。

                    对于一个刚入行并且阅读过设计模式相关书籍的程序员,他/她很容易犯一个错误——过度设计。比起没有设计,程序员更加难以意识到自己已经过度设计了。关于过度设计我有个深刻的体会,这里拿出来跟大家分享一下:

          在某个产品中,为了实现一个从远程部件获取数据,我想当然地为了提高效率,对从远端获取的数据进行了缓存,并且,为了进一步提高效率,在程序内部多个地方做了数据缓存,这种设计导致的问题是,在程序发布之后,当发现获取的数据是错误的时候,花了大量时间来定位错误的数据出现在了哪个环节。痛定思痛之后,我移除了这些缓存,把处理这些业务数据的spring bean改成无状态的,这样业务逻辑大大     简化,而且也没有发现性能低下的问题。 由此看来,这些缓存就是过度设计的思想引入的。

 

     先实现在重构:这个行为就是建立在简单设计的理念上的,其目的就是为了避免过度设计,完成刚刚好的功能。

效率提升:  熟练使用类库,熟悉IDE快捷键

     关于这两点,只有通过自己的实践慢慢积累,熟能生巧啦。

 

工程实践:  频繁提交, push前完成本地构建 ,  和pair及时沟通  

     频繁提交:频繁提交的目的在于及时地把自己开发出来的代码提交到代码仓库上去进行持续集成,验证自己的代码是否破坏了原有的功能。其本质思想在于快速得到反馈。

 

     push代码到代码仓库前完成本地构建:这个工程实践的目的在于,提前发现可能引入持续集成失败的问题,降低修复持续集成失败的成本。

     和pair及时沟通:在ThoughtWorks工作,大家都是以pair的形式进行的,这个实践的目的在于,及时从pair处得到反馈并获得成长。

 

代码风格:  代码要是自注释的,格式规范, 不提倡(switch, if else, 深度嵌套, 大函数,重复)

     关于代码风格方面,大家的观点基本是一致的:代码是给机器写的,但是是给人看的,所以要写人类能够看得懂的代码。