写点什么

使用 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:563700
用户头像

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

关注

评论

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

GitHub狂揽6700 Star,Python进阶必备的案例、技巧与工程实践

我再BUG界嘎嘎乱杀

Python 后端 入门 开发语言 零基础

蓝易云 - 关于openfeign的http和rpc详解

百度搜索:蓝易云

Linux 运维 Web HTTP openfeign

win版EaseUS Todo PCTrans(数据迁移软件) v13.15 中文特别版

iMac小白

win版Icecream PDF Editor Pro(pdf编辑器) v3.24 激活版

iMac小白

服务高可用秘籍:高性能 - 葵花宝典

SFLYQ

Go 架构 高可用 后端 高性能

python项目 以docker形式打包部署全流程

我再BUG界嘎嘎乱杀

Python Docker

豆瓣评分9.4!堪称经典的Python入门圣经,你还没看过吗?

我再BUG界嘎嘎乱杀

Python 后端 入门 开发语言 零基础

SoftReference 到底在什么时候被回收 ? 如何量化内存不足 ?

bin的技术小屋

ZGC JVM java

win版Aiseesoft FoneTrans(ios设备管理软件) v9.3.58激活版

iMac小白

win版Perfectly Clear Workbench(图片编辑软件) v4.6.1.2668 特别版

iMac小白

码住!详解时序数据库不同分类与性能对比

Apache IoTDB

这个vue3的后台管理系统虽然简洁但不简单

大师兄

JavaScript Vue3 vue后台管理系统

科技手段助力服务升级,新加坡旅游局成为马蜂窝首个境外目的地AI伙伴

技术研究院

I/O虚拟化之软件模拟

Linux内核拾遗

Linux qemu kvm 软件模拟 IO虚拟化

spring boot优雅下线

智慧源点

微服务的优雅上下线

win版中望3D 2025(ZW3D2025CAD/CAM软件) v2025中文版

iMac小白

蓝易云 - Redisson集成SpringBoot详解

百度搜索:蓝易云

redis spring 运维 服务器 redisson

ERC404开发:探索代币化资产的新维度

dappweb

区块链开发 ERC404

win版Gihosoft TubeGet Pro(YouTube 视频下载工具) v9.3.88 专业激活版

iMac小白

XMind for mac (XMind思维导图) v24.01中文版

Mac相关知识分享

思维导图 办公软件 Mac软件 软件下载 mac下载

蓝易云 - Ubuntu22.04交叉编译gcc9.5 for arm

百度搜索:蓝易云

Linux ubuntu 云服务器 arm GCC

学习笔记:快速成长的几点方法

老张

成长 学习笔记 成长路线

码住!详解时序数据库不同分类与性能对比

Apache IoTDB

win版Nitro PDF Pro(全面的PDF工具) v14.26.0.17便携版

iMac小白

win版MediaMonkey Gold(媒体管理工具) v2024.0.0.3031 中文注册版

iMac小白

mac电脑pdf增强插件:Enfocus PitStop Pro 2022激活版

你的猪会飞吗

Mac软件 苹果软件资源 Mac软件推荐

win版威力导演 2024 (CyberLink PowerDirector Ultimate) v22.5.3003.0 特别版

iMac小白

30 天 52% 回报:GPT-4o 量化交易机器人

俞凡

人工智能 交易

Mac苹果3D摄影棚布光软件:Set A Light 3D Studio 汉化版

你的猪会飞吗

Mac 软件 苹果电脑软件下载

昆仑万维颜水成受邀出任2024北京智源大会程序主席及视觉大模型论坛主席

新消费日报

win版IDimager Photo Supreme 2024(图片管理软件) v2024.2.0激活版

iMac小白

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