Selenium 是 ThoughtWorks 专门为 Web 应用而开发的自动化测试工具,适合进行功能测试、验收测试,其最大的优势有几点:
- 可直接运行在浏览器之上,所见即所得,就像真实用户所做的一样。Selenium 的核心,也称 browser bot,是用 JavaScript 编写的。这使得测试脚本可以在受支持的浏览器中运行。browser bot 负责执行从测试脚本接收到的命令
- 支持多操作系统(Windows, Mac OS和Linux)和各种浏览器Internet Explorer、Mozilla 和 Firefox,更容易发现浏览器的不兼容性
- 支持两种开发脚本的模式test runner (HTML文件)和 driven(脚本语言编写),其语言包括Java, .NET, Perl, Python 和 Ruby. 使用 driven 脚本,测试有一部分在浏览器之外运行,而如果使用 test runner 脚本的话,测试是完全在浏览器中运行的。
但是Selenium是轻量的测试框架, 脚本所处理的测试用例构成简单,其实质就是通过HTTP协议,发送请求(request)来完成测试用例,所以很困难处理业务逻辑关系强的测试用例。
更多的讨论: http://forums.openqa.org/index.jspa
Selenium 命令Selenium 命令分成两类 —— 操作(action) 和断言(assertion):
- 操作模拟用户与 Web 应用程序的交互。例如,单击一个按钮和填写一个表单,这些都是常见的用户操作,可以用 Selenium 命令来自动化这些操作。
- 断言验证一个命令的预期结果。常见的断言包括验证页面内容或当前位置是否正确。
在 Selenium 网站上可以找到可用命令的完整列表。通过 Selenium 命令,脚本编写者可以描述 browser bot 在浏览器中所执行的操作
组成- Selenium IDE:一个firefox的plug-in,可以录制和回放并保存一些test cases, 可以生成一些简单的基于rc 模式的简单code. (相当于Jmeter的gui模式和jmeter脚本的生成-badboy)
- Selenium Core. 整个测试机制的核心部分,即有assertion(断言) 机制的test suite runner。它由一些纯js代码组成, 可以运行在windows/linux的不同browser上 (相当于Jmeter 的runner 跟 Assertion)
- Selenium Remote Control:一个代理与控制端, 可代替Selenium core/ Selenium IDE的client端(相当于通过编程来实现一切),是支持多语言的. (相当于Jmeter的client/server模式,但Selenium Remote Control更强一些)
- Windows:
- Internet Explorer 6.0
- Firefox 0.8 to 1.5, Mozilla Suite 1.6+, 1.7+
- Seamonkey 1.0, Opera 8
- Mac OS X:
- Safari 1.3+
- Firefox 0.8 to 1.5, Mozilla Suite 1.6+, 1.7+
- Seamonkey 1.0, Camino 1.0a1
- Linux:
- Firefox 0.8 to 1.5, Mozilla Suite 1.6+, 1.7+
- Konqueror
下载地方:http://www.openqa.org/selenium/
selenium目录下的内容:
devtests:试验性功能 dom-images: 查看DOM用图片
dom-styles: 查看DOM用样式表
html-xpath: Xpath库
jsmock: javascript mock library
jsunit: javascript unit test library
tests: samples(以这个为基础开发测试用例)
核心js文件和html文件
如果想要测试自己开发的发布在服务器端的页面,需要把selenium配置在同一个服务器下:
Apache :直接将selenium目录拷贝至htdocs(Apache的确省根目录)目录下,然后启动Apache,用地址http://server:8080/selenium/TestRunner.html访问例子。
Tomcat :直接将selenium目录拷贝至webapps目录下,启动Tomcat,用地址http://server:8080/selenium/TestRunner.html访问例子。:
IIS:建立一个虚拟目录selenium,将该虚拟目录直接指向实际的selenium目录,用地址http://server/selenium/TestRunner.html访问例子
Test runner 脚本开发模式Selenium test runner 脚本,就是测试用例(test case),是用 HTML 语言通过一个简单的表布局编写的,即使对于非技术人员来说,test runner 脚本也易于阅读和编写。如 清单 1 所示。
清单 1. Selenium 测试用例的结构 (HTML格式)
Command1/Assertion1 | Target1 | Value1 |
Command2 Assertion1 | Target2 | Value2 |
test runner 脚本使用与 xUnit 框架相同的测试套件(test suite)和测试用例概念。测试用例和命令按照它们在测试套件和测试用例中出现的顺序依次执行。在 清单 1 中:
- 第一列包含命令 或断言。
- 第二列包含命令或断言的目标(target)。可以用多种受支持的组件定位符中的一种来指定目标。通常使用的是组件的 ID 或名称,但 XPath 和 DOM 定位符也是受支持的。
- 第三列包含用于为命令或断言指定参数的值。例如,当使用 type 命令时,这一列可能就是一个文本域所期望的值。
Test runner 脚本通常与所测试的应用程序(AUT)部署在同一个服务器上。这是因为 browser bot 使用 JavaScript来模拟用户操作。这些脚本在一个受限制的沙箱环境中运行。如果需要绕过这些限制,可以使用一个代理。
driven 脚本开发模式driven Selenium 脚本是用多种受支持的编程语言(Java, .NET, Perl, Python 和 Ruby)中的一种编写的。这些脚本在浏览器之外的一个单独的进程中运行。驱动程序的任务是执行测试脚本,并通过与运行在浏览器中的 browser bot 进行通信来驱动浏览器。驱动程序与 browser bot 之间的通信使用一种简单的特定于 Selenium 的连接语言 Selenese。
driven 脚本比 test runner 脚本更强大、更灵活,可以将它们与 xUnit 框架集成。driven 脚本编写和部署更复杂些,它必须执行以下任务:
- 启动服务器。
- 部署所测试的应用程序(AUT)。
- 部署测试脚本。
- 启动浏览器。
- 发送命令到 browser bot。
- 验证 browser bot 执行的命令的结果。
driven 脚本更依赖于应用程序运行时环境。例如,Java 驱动程序使用一个嵌入式 Jetty 或 Tomcat 实例来部署所测试的应用程序,如将 Selenium 集成到 Ruby on Rails 中。
开发测试用例测试用例开发涉及四类文件
主文件: TestRunner.html/TestRunner.hta(.hta文件是html application,windows平台特有);
Test suite和Test case文件:需要编写的由一个表格组成的html文件;
引擎库js文件:位于selenium根目录下的核心文件,其中html-xpath目录下的那个文件,也是必须的库文件;
user-extensions.js:用来扩展selenium的文件;用户自己编写的函数和扩展的命令都应该放在这个文件中;
这四类文件中,除了引擎库以外,其他三类文件都是可以根据具体情况去修改的。selenium 部署完毕后,可以打开浏览器来通过url来访问TestRunner.html文件。初始的时候,TestRunner.html文件中的 TestSuite是链接到tests目录下的TestSuite.html文件,TestCase的frame(上部中间)中打开了 TestSuite.html文件中的第一个Test Case “TestOpen.html”。
可以直接修改TestSuite.html文件,让其指向自己开发的Test case html文件。我们也可以建立另外一个目录,然后将自己的TestSuite文件和Test case文件都保存在这个目录中。如果使用后一种方式,那么在打开TestRunner.html的时候需要传递一个参数,例子如下:
http://localhost/selenium/TestRunner.html?test=/testDir/myTestSuite.html
下面就是开发测试用例——即编辑测试用例的表格。无论Test Suite还是Test Case,表格的第一行都是描述性文字,selenium的引擎是不会处理这一行的内容的。实际内容都是从第二行开始的。Test case的表格列数一定不能少于3列,否则Selenium会出错。而基本的三列组成是:
|command| Target| value|
清单 2就是四个测试用例的例子,将执行以下操作:
- 通过进入 /change_address_form.html 打开变更地址页面。
- 在 ID 为 address_field 的文本框中输入 Betelgeuse state prison。
- 单击名为 Submit 的输入区。注意,这里使用 XPath 找到 Submit 按钮,这导致表单数据被发送到服务器。
- 验证页面是否包含文本 Address change successful。
清单 2. 在测试用例中使用命令和断言的例子
command | Target | Value |
open | /change_address_form.html |
|
type | address_field | Betelgeuse state prison |
clickAndWait | //input[@name='Submit'] |
|
verifyTextPresent | Address change successful< |
|
测试套件
要达到对应用程序的完全测试覆盖,通常需要不止一个测试用例。这就是 Selenium 使用测试套件的原因。测试套件用于将具有类似功能的一些测试用例编成一组,以便让它们按顺序运行。
测试套件和测试用例一样,都是用简单的 HTML 表编写的。Selenium 执行的缺省测试套件的名称是 TestSuite.html。测试套件使用一个只包含一列的表,表中的每一行指向一个包含某个测试用例的文件。
对于一个有着多个功能模块、组件的web应用,编写的测试脚本html必然比较多。因此,应该建立一个合理的目录结构来组织这些脚本,一般按web服务、模块、功能来组织,形成层次性结构。问题1:当用户在网上商店购物时,一次完整的购买流程需要用户进行好几个步骤的操作(包括选择商品、填写订单信息、选择支付方式、确认订单等),涉及四到五个页面以及数十个类的协作。如何在开发过程中始终确保该流程能够正确无误、畅通无碍?
问题2:客户提出需求:在显示货物列表时,应该首先按货物名称排序,名称相同的货物再按照价格排序。我们已经实现了这一功能,并且有单元测试作为保障,但如何让客户看到我们的成果?
问题3:美工在制作页面时,一不小心把一个<form>的id属性删掉了。几天之后,另一个页面上的JavaScript莫名其妙地失效,我们花了很多时间才发现这个问题。应该如何避免类似的情况再次发生?
这三个问题对于做惯了web应用的读者来说一定不陌生——实际上,我们的每个项目都或多或少地遇到类似的问题。说穿了,这三个问题都是关于同一件事情:如何验证一个东西是正确的,以及如何便利而自动地重复这一验证过程。在代码层面上,xUnit单元测试工具(对于J2EE项目,就是Junit)给了我们帮助。但是,当问题涉及到web界面和用户交互时,JUnit就显得有些力不从心了,这也是很多采用测试驱动开发(TDD)方法的团队只能把TDD贯彻到web controller层面的原因。
单元测试 vs. 功能测试 正如它的名字所揭示的,JUnit是一个单元测试工具。而我们在前面提出的三个问题,实际上已经属于功能测试(functional test)或者验收测试(acceptance test)的范畴。尽管单元测试能够保证各个单元的正确,却无法确保将这些单元组合起来之后的效果。需要依靠功能测试工具,我们才能继续TDD的脚步。 另一方面,功能测试在很多时候应该由客户——或者是具有一定技术背景的客户代表(可能是项目的需求分析师)——来编写的(这也是“验收测试”这个名称的由来:通过这些测试就代表工作通过验收),因此编写这些测试不应该要求太高的编程能力。在这一点上,JUnit也是令人望而生畏的。 |
由ThoughtWorks员工开发并维护的Selenium(http://selenium.thoughtworks.com)正是帮助我们解决上述问题的得力工具。简单地说,Selenium是一个自动化的web应用功能测试工具——我知道,这个短语不足以让读者了解它所描述的对象。所以,在进一步介绍之前,我想先请读者来看一个活生生的例子。请打开你的浏览器,访问下列URL地址:
http://www.openqa.org/selenium/demo1/TestRunner.html
你将会看到Selenium的主操作界面(如图1)。可以看到,整个页面被分成四个部分。左上角的“Test Suite”区域显示出当前运行的测试套件包含哪些测试用例;中间上部的“Current Test”区域显示出当前执行的测试用例;右上角的“Control Panel”区域是给用户操作的区域。至于下面的一大片空间,它会在执行测试的过程中起到重要的作用,我们稍后就会看到。
图1:Selenium主界面
点击“Control Panel”区域中的“All”按钮,读者会——或许有点惊讶地——发现,屏幕上的文字和颜色开始飞快地发生变化。如果你还没有明白这是怎么回事,可以先把按钮上方的单选按钮放在“walk”上,然后再点击按钮。这时你会清楚地看到,原来Selenium正在逐个运行套件中的测试用例:执行测试用例中指定的操作,并进行指定的条件判断。至于屏幕下方的大块空白区域,此刻正在模拟着实际的用户操作。而那些淡绿色的横条,熟悉JUnit的你应该不难猜到,正是测试通过的象征——看到这些绿色横条让你感到心情愉悦,不是吗?
图2:测试套件执行完毕
读者可以看到,“Control Panel”区域中还显示着本次测试的相关信息:耗时10秒,执行3个测试用例,共有10个判断条件,所有测试都通过,没有失败或未完成的测试。此外,如果点选一个测试用例,再点击“Selected”按钮,就可以单独运行这一个测试用例;如果选中“Step”选项,就可以进行单步跟踪运行。这些功能,相信聪明的读者只需要稍微尝试一下就会全部掌握了。
经过几分钟的探索,读者应该能够明白这个Demo的奥妙所在了。没错,Selenium采用JavaScript来管理整个测试过程,包括读入测试套件、执行测试和记录测试结果——这在很大程度上得益于强大的JavaScript单元测试工具JSUnit(http://www.edwardh.com/jsunit/),正是有它的帮助,Selenium才能够模拟真实的用户操作,包括浏览页面、点击链接、输入文字、提交表单等等,并且能够对结果页面进行种种验证。也就是说,只要在测试用例中把预期的用户行为与结果都描述出来,我们就得到了一个可以自动运行的功能测试套件。而且,我们习惯的测试驱动开发方法也可以延伸到web表现层:我们可以先写测试、运行测试并看到它失败、然后编写功能代码让测试通过。
现在,如果你已经对Selenium产生了兴趣,我将带领你开始真正的Selenium测试之旅。首先,请到以下地址下载最新版本的Selenium(当然,作为一个J2EE开发者,我假设你已经安装了JDK和servlet容器譬如Tomcat):
http://www.openqa.org/selenium/download.action
Selenium的故事 在等待下载的过程中,不妨先听我讲讲和Selenium有关的故事。正如我在前面提到过的,Selenium是ThoughtWorks员工在业余时间开发并维护的开源项目,并且在ThoughtWorks的项目中被广泛应用。不过,真正有趣的是它名字的来历:在Selenium出现之前,最著名的web应用功能测试工具当属Mercury Quanlity Center(http://www.mercury.com/us/products/quality-center/),但那是一个商业工具,功能强大却也价格不菲,常常让开发者们又爱又恨。所以,自己动手开发开源功能测试工具的ThoughtWorker们把这个工具叫做Selenium——“mercury”有“水银”的意思,而“selenium”(硒元素)恰好是专解汞中毒的特效药。 |
把下载的压缩包解压之后,你会得到两个目录:doc和selenium。只要把后者复制到你的web服务器根目录(对于Tomcat,就是webapps目录)下,就算是完成Selenium的安装了。安装好之后,可以启动web服务器,然后试着访问下列URL地址(假设你也像我一样,把Tomcat开在8080端口上):
http://localhost:8080/selenium/TestRunner.html
在这里,你应该又会看到那个熟悉的主操作界面(如图3)。不妨试着运行一下这些测试,看看它们是否能够在你本地的机器上正常运行。确认一切正常之后,我们再来编写自己的测试。
图3:在本地运行Selenium
缺省情况下,Selenium会从tests目录下的TestSuite.html文件加载测试套件,但我们也可以指定从别的文件加载。首先,我们在tests目录下创建一个MyTestSuite.html文件,然后在其中定义我们的测试套件:
<html>
<head>
<title>My First Test Suite</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<td><b>Test Suite</b>
<td>
<a href="./MyTests/TestHello.html">Test Say Hello
</body>
</html>
然后,在tests/MyTests目录下创建TestHello.html文件,在其中描述我们要做的动作和期望得到的结果:
<html>
<head>
<title>Test Hello</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<td rowspan="1" colspan="3">Test Say Hello To World
<td>open
<td>/sample/hello.jsp
<td>
<td>verifyTextPresent
<td>Hello, World!
<td>
</body>
</html>
测试套件和测试用例的写法都是一目了然的。在测试用例中,我们首先访问/sample/hello.jsp这个地址(open命令),然后验证页面上有“Hello, World!”字样存在(verifyTextPresent命令)。在Selenium的doc目录下,你可以找到完整的命令帮助列表。现在访问http://localhost:8080/selenium/TestRunner.html?test=tests/MyTestSuite.html这个地址,应该就可以看到我们的测试套件,当然现在运行它会看到红色的失败信息。
图4:运行我们的第一个测试,失败了
熟悉TDD过程的读者现在不仅不会失望,反而会感到兴奋,因为这个失败的测试为我们指出了目标。为了让测试通过,我们可以创建一个名为sample的web应用,在其中放上hello.jsp这个文件,让它向世界问好。然后,我们就可以享受成功的喜悦了。
图5:我们的第一个Selenium测试通过了
继续这个过程:编写测试-红-编写功能代码-绿……随着我们一步步前进,测试用例也会逐渐增加,最终构成一张庞大而严密的安全网。不仅是Java程序,在Selenium的帮助之下,界面的开发工作同样可以用TDD的方式来进行。而且,由于是架设在JavaScript的基础上,Selenium并不仅限于J2EE web应用的测试,实际上各种web应用都有它的用武之地。
作为一篇简介,本文只能帮助读者对Selenium建立一个最基本的了解,更多的知识与技巧还有待读者去探索。譬如说,在持续集成的环境下,可以用Ant来驱动Selenium测试,并将测试结果汇报给CruiseControl,从而实现更加严格的集成管理。在下次的文章中,我将向读者介绍ThoughtWorks公司采用测试驱动开发的一些技巧与实践(当然也包括Selenium的使用心得)。现在,让我们先说再见吧,希望你编程愉快。