今天下午给同事就自动化验收测试做了一个简单的介绍,引起了大家的阵阵讨论。同时还有其他Team的人来分享各自的经验,他们也都做得相当不错。
测试包括很多种,单元测试、集成测试、功能测试、验收测试、数据库测试等等。撇开大家都熟悉的单元测试、功能测试不谈,为什么这里要单独拿验收测试来说自动化呢?
首先谈谈准备一次发布要做哪些事情。首先得验收这个迭代里面的所有Story,功能符合验收条件,没有bug。然后呢,需要对以往的所有迭代的Story都要进行回归测试,来验证这个迭代里面的修改没有破坏以前的功能。如果全部通过,或者核心功能工作完好,那么系统就能进行发布了。
由此看来,测试人员的工作量相当大,验收Story+回归所有功能。如果在一个发布很频繁的互联网项目上,两周甚至一周一个发布,要是这些活全部手工做的话,测试人员就得累得吐血了。即使这样,血吐光了都不一定能测得完。测不完?所有人都来做测试,所有人都得吐血了。“人肉测试”不仅仅是很难完成任务,而且还有以下几个问题:
1. 重复劳动。这样一件事情,每次都得做一遍,烦不烦?
2. 人工测试精确度不高,容易出错。人不是机器,没有百分百的准确性,某个时候肚子饿了,或者听说股市大跌,精神一紧张,手一抖,就测不准了。
3. 浪费人力,物力和时间。一大堆人花大量时间来测试,测试完还得花时间写书面报告,成本增长的那个快。
结论是人肉测试不行!能自动化的必须得自动化。因为有了自动化以后:
1. 系统质量得到保证。任何时候,只要测试通过,你就能勇敢的说:看!测试全过,功能肯定没问题。
2. 消除浪费。上面所提到的人力、物力和时间的浪费都能消除掉。
3. 加快了测试速度。人工测试需要两天的,自动化通常2个小时可以测完。累得吐血这种情况已经看不见了。而且自动化测试可以通过一系列的方法,比如使用更好的机器,将测试分布式运行等来加速。
4. 提高测试的效率和准确性。自动化测试是高效的,它一刻也不停的在运行。同时它也是准确的,程序代码帮你做验证,不会有任何偏差。
5. 勇气和反馈。开发人员有勇气去修改、维护或重构代码,有的时候即使单元测试通过,功能不一定正确。有双重保证的话,开发人员就有足够的勇气。项目所有人都有勇气说发布,不会像以前那样战战兢兢。
6. 可阅读的文档。验收测试的代码就是测试用例。看代码就是看用例,一步一步,清晰明确。不需要投入精力去维护一份测试用例文档,而且很有可能是过期不管用的。
自动化测试,不管是XP、精益还是DSDM都把它放在最重要的地位。如果还处在人工测试阶段上的朋友,请自动化它们吧。
如果写好测试,也是一门很高深的学问。不要认为写测试是测试人员的专利,不管白盒黑盒,写出漂亮的测试是开发人员的基本素质。先看看下面这个测试,以Watir+RSpec为例:
it “Scenario: create story with full information” do
@browser.open(…)
@browser.text_field(:id, ‘..’).set story_name
@browser.text_field(:id, ‘..’).set story_description
……
@browser.button(:id, ‘..’).click
@browser.div(:id, ‘..’).text.should contains(‘…’)
……
end
这里描述了一个场景,用户填写一些字段,然后保存提交来创建一个用户故事。细看每一行代码,会发现看不懂!不知道每一行代码是在做什么。无法领会写测试人员的意图。
这段代码最大的问题是其脆弱性。测试代码跟页面元素、结构紧紧耦合在一起,而UI元素是会经常被重构被改变的,一旦改变,很多测试用例就会失败,需要做出相应的修改。
要想同时解决这两个问题,可以引入Page Model模式(相信ThoughtWorks的人都会用)。上面的代码便可以写成这样:
it “Scenario: create story with full information” do
create_story_page = @navigator.goto_add_story_page
create_story_page.add_story :name=>’…’, :description=>’…’
……
view_story_page = page.save_and_view
view_story_page.information.text.should contains(‘…’)
……
end
下面再来一个复杂点的测试用例
it “Scenario: import all the stories from excel file”
import_stories_page = @navigator.goto_import_stories_page
import_stories_page.import_from excel_path
preview_page = import_stories_page.preview_import
preview_page.choose_import_stories :all
preview_page.to_next_page
preview_page.choose_import_stories :first
story_list_page = preview_page.confirm_import
story_list_page.stories.should contains(‘imported story1’)
……
end
看!代码是不是可读性很好,业务意图是不是很清晰。每一行代码都是一个业务操作,代表着测试用例的一个步骤,最后是一些断言,进行验收。断言也避免了assertEquals这种写法,采用RSpec的should来达到可读性最佳,最贴近人类语言的效果。如果你仔细看看这段代码,会发现它没有一点UI元素的操作在里面。UI再怎么重构,页面结构再怎么变化,测试用例代码都是稳定的。
因为Page Object封装了UI元素和结构,使得测试代码稳定,达到要修改只修改一处的效果。同时,Page Object也封装了业务行为及操作,使得测试代码更可读。Page Model还有几个重要的概念,Driver, Navigator, DataFixture,其实都是封装了不同的变化点,这里也不多做介绍。
验收测试的理想目标是全部自动化,测试代码即测试用例文档,易读易写。