50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

使用 iTest2 重构自动化功能测试脚本

  • 2009-08-31
  • 本文字数:6008 字

    阅读完需:约 20 分钟

介绍

众所周知,自动测试脚本很难维护。随着敏捷方法学在企业软件项目中的广泛应用,其核心实践之一——自动化功能测试已经证明了它的价值,同时却也对项目提出了挑战。传统的“录制-回播”类型的测试工具也许能帮助测试人员很快地创建一系列的测试脚本,但这些测试代码最后却很难维护。原因就是:应用程序在不断变化。

在编程的世界中,“重构”(在不影响软件外在行为的前提下,改善软件内部结构的一种方法)已经成为程序员之间频繁使用的词汇。简而言之,通过重构,程序员让代码变得更易于理解、设计也更灵活。经验丰富的敏捷项目经理会给程序员分配一定的时间来重构代码,或者把重构作为完成用户故事的一部分。大部分的集成开发环境(IDE)已经对多种重构方式提供了内置支持。

开发或者维护自动测试脚本的测试人员就没有这份惬意了,虽然他们也有使自动测试脚本变得可读和可维护的要求。软件发布新版本,会伴随新特性、bug 修复和软件变更,要想跟踪与之对应的测试脚本,这很难(而且,测试脚本越多,这项工作就越困难)。

测试重构

对功能测试的重构目标和流程与代码重构一样,但有自己的特点:

  • 目标受众
    测试工具的最终用户包括测试人员、业务分析师,甚至还有客户。事实是测试人员、业务分析师和客户一般都不掌握编程技能,整个范式因此而改变。
  • 脚本语法
    代码重构主要是在编译型语言(比如 Java 和 C#)上得到支持。函数式测试脚本,可能是 XML、厂商专有脚本、编译型语言或者脚本语言(比如 Ruby)。根据测试框架不同,重构的使用形式也不同。
  • 功能测试专属重构
    很多通用的代码重构技巧,比如“重命名”,可以用在功能测试脚本里面,它们特定于测试意图,比如“Move the scripts to run each test case”。

iTest2 IDE

iTest2 IDE 是一款新的功能测试工具,专为测试人员设计,让他们能够很轻松地开发和维护自动测试脚本。iTest2 完全致力于 web 测试的自动化,它支持的测试框架是使用 RSpec 语法的 rWebUnit(是广为流行的 Watir 的一款开源插件)。

iTest2 背后的哲学是:容易、简单。试用显示:没有编程经验的测试人员在指导下,平均只需要少于 10 分钟的时间就能编写他们第一个自动化测试脚本。借助于 iTest2,测试人员可以开发、维护和验证功能需求的测试脚本;开发人员可以验证特性可用;业务分析师 / 客户通过查看测试运行结果(在真实的浏览器下,比如 IE 或者 Firefox)来验证功能需求。

由 iTest2 创建的测试脚本可以从命令行运行,也能集成在持续构建服务器上。

演练

事实胜于雄辩。下面我们就来看看如何使用 iTest2 提供的重构工具创建两个测试用例,使它们变得更易理解和维护。

测试计划

为了练习,我们给 Mecury’s NewTour 网站开发了一些典型但是简单的 web 测试脚本。

站点 URL http://newtours.demoaut.com 测试数据: 用户登录:agileway / agileway 测试用例 001: 一个注册客户可以选择单程航行方式,从纽约前往悉尼。 测试用例 002: 一个注册客户可以选择往返方式,从纽约前往悉尼。 自动化测试 测试脚本框架: rWebUnit(开源的 Watir 扩展) 测试执行方法: 通过命令行或 iTest2 IDE 测试编辑器 / 工具: iTest2 IDE ### 创建测试用例 001

1. 创建项目

首先,我们创建一个 iTest2 项目,指定网站 URL。一个简单的测试脚本文件就会被创建出来,如下所示:

复制代码
load File.dirname(__FILE__) + '/test_helper.rb'
test_suite "TODO" do
include TestHelper
before(:all) do
open_browser "http://newtours.demoaut.com"
end
test "your test case name" do
# add your test scripts here
end
end

2. 使用 iTest2Recorder 录制测试用例 001 的测试脚本

我们使用 iTest2Recorder,这是 Firefox 的一个插件,能录制用户在 Firefox 浏览器中的操作,并记录为可执行的测试脚本。

复制代码
enter_text("userName", "agileway")
enter_text("password", "agileway")
click_button_with_image("btn_signin.gif")
click_radio_option("tripType", "oneway")
select_option("fromPort", "New York")
select_option("toPort", "Sydney")
click_button_with_image("continue.gif")
assert_text_present("New York to Sydney")

3. 把录好的测试脚本贴到一个测试脚本文件里面,运行

复制代码
# ...
test "[001] one way trip" do
enter_text("userName", "agileway")
enter_text("password", "agileway")
click_button_with_image("btn_signin.gif")
click_radio_option("tripType", "oneway")
select_option("fromPort", "New York")
select_option("toPort", "Sydney")
click_button_with_image("continue.gif")
assert_text_present("New York to Sydney")
end

现在运行测试用例(右键单击,然后选择“Run [001] one way trip”),它通过了!

使用 Page 对象进行重构

上面的测试脚本可以工作,而且 rWebUnit 语法也非常易读。有人可能对重构的要求提出质疑,也许还会问“使用 Page”是怎么回事?

首先,以现在的格式来看,测试脚本并不易于维护。假设我们已经有了数百个自动测试脚本,而新发布的软件修改了用户认证方式,使用客户邮箱作为用户名登录,这意味着我们需要在测试脚本里面使用‘email’,而不再是‘userName’。在数百个文件里面查找替换,那可不是个好主意。况且,项目成员也喜欢使用项目里面的通用词汇,有一个很美妙的名字来称呼它们:领域专属语言(DSL)。在测试脚本里面也使用这些词汇就太美妙了。

使用 Page 对象能很好地做到这一点。一个我们所说的 Page 对象代表了一个逻辑上的 web 页面,它包含了最终用户在该页面上可以执行的操作。举例来说,在我们例子里面的主页就包含了三个操作:“输入用户名”、“输入密码”和“点击登录按钮”。“使用 Page 对象进行重构”是指把操作抽取到特定 Page 对象的过程,而 iTest2 提供了对这样的重构支持,你可以很容易做到这一点。

1. 抽取到 HomePage 对象

登录功能是发生在主页上面,我们把这事交给 HomePage。用户登录是一个很常见的功能,我们用了三行语句(输入用户名、输入密码和点击登录按钮)完成这个操作。选中这三行代码,然后在“Refactoring”菜单下单击“Extract Page…”(快捷键是 Ctrl+Alt+G)。

图 1. “Refactor”菜单——“Extract Page”

如下图所示,这样会弹出一个窗口,让你输入 Page 对象的名字和功能名。这里,我们分别输入“HomePage”和“login”。

图 2. “Extract Page”对话框

选中的 3 行代码就被替换成:

复制代码
home_page = expect_page HomePage
home_page.login
{1}

这将会自动创建一个新文件“pages\home_page.rb”,其内容如下:

复制代码
class HomePage < RWebUnit::AbstractWebPage
def initialize(browser)
super(browser, "") # TODO: add identity text (in quotes)
end
def login
enter_text("userName", "agileway")
enter_text("password", "agileway")
click_button_with_image("btn_signin.gif")
end
end

再次运行测试用例,它应该还是可以通过。

注意:正如 Martin Fowler 指出,重构的节奏:测试、小的改动、测试、小的改动。正是这种节奏保证了重构的迅速和安全。

2. 抽取 SelectFlightPage

登录成功之后,顾客进入了航班选择页面。与登录页面不同,这里的每个操作很可能被不同的开发人员修改,所以我们把每个操作都抽取为一个函数。把光标移到这一行

click_radio_option("tripType", "oneway")

再次执行“Extract to Page...”重构命令(Ctrl+Alt+G),给新的 Page 对象和函数名输入“SelectFlightPage”和“select_trip_oneway”。

复制代码
select_flight_page = expect_page SelectFlightPage
select_flight_page.select_trip_oneway
{1}

3. 继续抽取更多的操作到 SelectFlightPage 对象

继续把“SelectFlightPage”上的操作重构成函数:“select_from_new_york”、“select_to_sydney”和“click_continue”。

复制代码
test "[1] one way trip" do
home_page = expect_page HomePage
home_page.login
select_flight_page = expect_page SelectFlightPage
select_flight_page.select_trip_oneway
select_flight_page.select_from_new_york
select_flight_page.select_to_sydney
select_flight_page.click_continue
assert_text_present("New York to Sydney")
end

跟往常一样,我们再一次运行测试用例。

编写测试用例 002

在重构完测试用例 001 之后,我们现在有了 2 个 Page 对象(“HomePage”和“SelectFlightPage”),因此(通过重用它们)编写测试用例 002 会容易很多

1. 使用已有的 HomePage

iTest2 IDE 内置支持 Page 对象,输入“ep”再敲“Tab”制表键(称为“snippets”),就能自动补全为“expect_page”并且弹出所有已知的 Page 对象以供选择。

图 3. 自动补全 Page 对象

我们就能得到

expect_page HomePage

为了使用 HomePage,我们需要持有它的句柄(在编程世界中,也被称为‘变量’)。执行“Introduce Page Variable”重构动作(Ctrl+Alt+V)创建一个新变量。

图 4. ‘Refactor’菜单 - “Introduce Page Variable”菜单项

home_page = expect_page HomePage

现在在新行中输入“home_page.”,会自动提示这个 Page 对象中定义的函数供你选择。

图 5. Page 对象函数查找

2. 添加测试用例 2 需要的方法

测试用例 002 跟测试用例 001 很像,区别只在于旅行类型的选择和断言。借助于 Recorder,我们可以定义出新的函数:

click_radio_option("tripType", "roundtrip")

把它重构成 SelectFlightPage 的一个新功能

select_flight_page.select_trip_round

就变成了

复制代码
test "[2] round trip" do
home_page = expect_page HomePage
home_page.login
select_flight_page = expect_page SelectFlightPage
select_flight_page.select_trip_round
select_flight_page.select_from_new_york
select_flight_page.select_to_sydney
select_flight_page.click_continue
assert_text_present("New York to Sydney")
assert_text_present("Sydney to New York")
end

运行测试用例 2 的测试脚本(在测试用例 2 的任意一行之上单击右键,选择“Run …”),测试也通过了!

把应用复原为原始状态

但是等一等,我们还没有完成。测试用例 1 通过了,测试用例 2 也通过了,但是当把它们一起运行的时候,测试用例 2 却失败了,为什么?

我们没有把 web 应用复原回初始状态,在运行完测试用例 001 之后用户还是保持登录的状态。为了让测试之间互相保持独立,我们要确保每次运行测试都要以登录开始,以退出结束,有始有终。

复制代码
test "[001] one way trip" do
home_page = expect_page HomePage
home_page.login
# . . .
click_link("SIGN-OFF")
goto_page("/")
end
test "[002] round trip" do
home_page = expect_page HomePage
home_page.login
# . . .
click_link("SIGN-OFF")
goto_page("/")
end

删除重复代码

测试脚本存在着明显的重复。RSpec 框架允许用户在每个测试用例运行之前或之后执行某些操作。

选中首部两行(登录功能),按下“Shift + F7”以执行“Move Code”重构。

图 6. 重构菜单“Move code”

选择“2 Move to before(:each)”,把这部分操作移到

复制代码
before(:each) do
home_page = expect_page HomePage
home_page.login
end

正如名字所示,这两步操作会在每个测试用例运行之前执行,所以测试用例 002 里面的前面两行也就没有存在的必要了。我们还可以执行相似的重构,完成“after(:each)”的相关部分。

复制代码
after(:each) do<p>click_link("SIGN-OFF")</p><p>goto_page("/")</p><p>end</p>

最终版本

以下是测试用例 001 和 002 的完整的(经过充分重构的)测试脚本。

复制代码
load File.dirname(__FILE__) + '/test_helper.rb'
test_suite "Complete Test Script" do
include TestHelper
before(:all) do
open_browser "http://newtours.demoaut.com"
end
before(:each) do
home_page = expect_page HomePage
home_page.login
end
after(:each) do
click_link("SIGN-OFF")
goto_page("/")
end
test "[001] one way trip" do
select_flight_page = expect_page SelectFlightPage
select_flight_page.select_trip_oneway
select_flight_page.select_from_new_york
select_flight_page.select_to_sydney
select_flight_page.click_continue
assert_text_present("New York to Sydney")
end
test "[002] round trip" do
select_flight_page = expect_page SelectFlightPage
select_flight_page.select_trip_round
select_flight_page.select_from_new_york
select_flight_page.select_to_sydney
select_flight_page.click_continue
assert_text_present("New York to Sydney")
assert_text_present("Sydney to New York")
end
end

适应变化

我们的世界并不完美。在软件开发行业,事物频繁发生变更。幸运的是,以上的工作使得测试脚本不仅仅更易读,而且也更容易适应变化。

1. 客户修改了术语

众所周知,项目使用同一套语言是一个好的实践,即使在测试脚本里面也是如此。举例来说,客户现在更倾向于使用“Return Trip”这个名词,而不再是“Round Trip”。借助于重构测试脚本,这很容易做到。

把光标移到“SelectFlightPage”类(pages\select_flight_page.rb)的“select_trip_round”函数,在“Refactoring”菜单下选择“Rename …”项(Shift+F6)

图 7. “Refactor”菜单-“Rename”

然后输入新的函数名字“select_return_trip”。

图 8. “Rename Function”对话框

测试脚本其他引用“select_trip_round”的地方就都更改为

select_flight_page.select_return_trip

2. 应用程序的修改

应用程序(来自程序员)的修改就更普遍了。举例来说,程序员基于某些原因修改了航班选择页面,导致 HTML 页面上出发城市的属性从

复制代码
<select name="fromPort">

改成

复制代码
<select name="departurePort">

虽然用户不会察觉到任何变化,测试脚本(任何访问这个页面的测试用例)现在却会失败。如果你直接用录制的脚本文件作为测试脚本,修改的操作将会非常乏味,而且易于引入错误。

定位到“SelectFlightPage”的“select_from_new_york”方法(使用快捷键 Ctrl+T 选中“select_flight_page”,再输入快捷键 Ctrl+F12 选择“select_from_xx”),把“fromPort”改成“departurePort”。

复制代码
def select_from_new_york
select_option("departurePort", "New York") # from 'fromPort'
end

看上去还不赖!

结论

本文我们介绍了在自动化功能测试中使用 Page 对象,以使测试脚本易于理解和维护。通过一个使用 iTest2 IDE 改善测试脚本过程的实际例子,我们演示了其提供的丰富的重构功能。

引用文献

Fowler, Martin, et al. Refactoring: Improving the design of existing code, Reading, Mass.: Addison-Wesley, 1999


感谢郑柯对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2009-08-31 00:564273
用户头像

发布了 76 篇内容, 共 28.2 次阅读, 收获喜欢 3 次。

关注

评论

发布
暂无评论
发现更多内容

软件测试 | 开发接口

测吧(北京)科技有限公司

测试

10个提高工作效率的Cinema 4D小技巧

Finovy Cloud

C4D

开源赋能 普惠未来|浪潮集团寄语2023开放原子全球开源峰会

开放原子开源基金会

领先芯片供应商u-blox通过Perforce Helix Core加强协作,实现基于组件的开发

龙智—DevSecOps解决方案

组件化 芯片设计

“全球金牌课程”6月17-18日 · CSM认证在线周末班【提前报名特惠】CST导师亲授

ShineScrum

Scrum 敏捷

软件测试 | 接口测试

测吧(北京)科技有限公司

测试

C语言编程—变量的构成

芯动大师

融云 WICC 2023 定档!「出海嘉年华」穂城来袭!

融云 RongCloud

通信 社交 融云 出海 wicc

图数据库 NebulaGraph 的内存管理实践之 Memory Tracker

NebulaGraph

数据库 内存管理 图数据库

广西高等教育学会高校教育技术委员会莅临瑞云科技考察交流

3DCAT实时渲染

虚拟仿真 元宇宙 实时渲染云

亚马逊云科技 一周回顾 – 2022 年 7 月 18 日

亚马逊云科技 (Amazon Web Services)

Amazon

一文看懂OpenStack SR-IOV

统信软件

Django笔记三十之log日志记录详解

Hunter熊

Python django 日志 log

LLMs 诸神之战:LangChain ,以【奥德赛】之名

Zilliz

Milvus AIGC LLM langchain

Golden Gate (GGX) 启动公测,下一代创新DeFi和跨链 dApps 征程开始

股市老人

Neuron 提供免费无限时试用:完整体验数十种工业协议连接

EMQ映云科技

工业物联网 网关软件 工业协议

【论文分享|SIGMOD'22】WeTune 自动发现和验证重写规则

Databend

国产操作系统如何构建自己的生态

Onegun

国产化 国产操作系统

2023 年度中国 DevOps 现状调查|有奖问卷

CODING DevOps

DevOps 云端IDE cloudstudio

开源赋能 普惠未来|360集团寄语2023开放原子全球开源峰会

开放原子开源基金会

2023年汽车软件行业趋势分析:安全性是汽车软件开发的重大挑战2023年汽车软件开发

龙智—DevSecOps解决方案

汽车软件安全 电动汽车软件 汽车软件开发

ControlNet: 控制扩散模型的魔法

Zilliz

AIGC Towhee Stable Diffustion controlnet

软件测试/测试开发丨学习笔记之列表、元组、集合

测试人

Python 软件测试 自动化测试 列表 测试开发

“伙伴+华为”体系,数字时代的新航标

脑极体

伙伴 体系

软件测试 | spyne开发接口

测吧(北京)科技有限公司

测试

生产环境质量保障的重要性

老张

质量保障 稳定性保障

4大特性看Huawei Cloud EulerOS为开发者带来平滑迁移体验

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

小程序6大开发框架对比分析

Onegun

小程序 小程序框架 小程序容器

MobTech ShareSDK|助力预热618

MobTech袤博科技

“敏捷教练必修课程”7月22-23日 ·A-CSM认证在线周末班【提前报名特惠】CST导师亲授

ShineScrum

Scrum 敏捷 敏捷精髓 敏捷实践 A-CSM

使用iTest2重构自动化功能测试脚本_研发效能_Zhimin Zhan_InfoQ精选文章