Web 应用程序的验收测试常常涉及一些手工任务,例如打开一个浏览器,并执行一个测试用例中所描述的操作。但是手工执行的任务容易出现操作人员人为的错误,也比较费时间。因此,尽可能将这些任务自动化,以消除人为因素,这是一种很好的做法。于是 Selenium 之类的测试工具就有了用武之地。Selenium 帮助您自动化验收测试,从而可以构建经过更严格测试、因而更为可靠也更易于维护的软件。

验收测试也称黑盒测试和功能测试,是测试和检验应用程序是否能按照涉众(stakeholder)的功能性需求、非功能性需求和其他重要需求来运行的一种方法。验收测试是单元测试和组合测试的补充,后两者通常是使用 xUnit 框架编写的。验收测试也可以使用编程语言来编写,但是 Selenium 和其他类似的工具,例如 Fitnesse,也支持用特定于工具的文档格式编写测试。

验收测试与单元测试和组合测试有以下不同之处:

  • 应用程序是作为一个完整的端到端实体来测试的,而不是像单元测试和组合测试那样,只是测试一个类或一组类。
  • 验收测试是在用户界面(例如一个浏览器)上执行的,而不是在 Web 应用程序界面上执行的。
  • 编写测试用例的人不一定知道应用程序的内部结构,因此也被称作黑盒测试。非技术性用户也可以编写验收测试。

背景知识

在讨论 Selenium 之前,我要介绍关于以下三个话题的一些背景知识,因为这些话题虽然不是本文的主题,但是和本文密切相关:

  • 持续集成
  • Ajax
  • Ruby/Ruby on Rails

持续集成

持续集成的目标是自动化构建和测试过程,以便每天自动运行一次或多次这些过程,而不是每个月手动地运行一次。使用持续集成的最大好处是,代码的更改会定期地自动被集成。如果系统受损,没有构建成功,Apache Continuum 和 Luntbuild 之类的持续集成工具可以自动通过发送电子邮件通知团队(见 参考资料)。

Ajax

Ajax 是 Asynchronous JavaScript and XML 的缩写,这是为相当老的技术新创造的一个术语。Ajax 背后的主要思想是,由于只需更新部分页面而不是整个页面,所以 Web 应用程序可以更快地对用户操作做出响应。

Ajax 将更多的复杂性引入到 Web 应用程序中,这一点也反映在测试中。这是因为 Ajax 就像它的名称所表明的那样,使用 JavaScript 和异步 HTTP 请求来更新页面内容。每个浏览器在实现中与其他浏览器相比有一些小小的不同。Selenium 是测试和检测这些差异的很好的工具,因为它在大多数流行的浏览器中都能够运行。

Ruby/Ruby on Rails

Ruby 是一种开放源码的解释型脚本语言,用于快捷、容易地进行面向对象程序设计。它提供了大量的库,而且简单易用,还具有可扩展性和可移植性。该语言是由 Yukihiro “Matz” Matsumoto 创造的,目的是让程序员将更多的注意力放在手头的任务上,摆脱语法的烦恼。

Rails 是由 David Heinemeier Hansson 创造的一种全栈的(full-stack)、开放源码的 Ruby Web 框架。Rails 的目标是使现实中的应用程序编写起来需要的代码更少,并且比 J2EE 和 XML 之类的语言更容易。所有层都能够无缝地一起工作,因此可以使用一种语言编写从模板到控制流乃至业务逻辑的各种东西。Rails 使用 YAML 而不是 XML 配置文件以及注释形式的反射和运行时扩展。这里不存在编译阶段 —— 程序修改后将直接运行。

什么是 Selenium?

Selenium 是 ThoughtWorks 专门为 Web 应用程序编写的一个验收测试工具。据 Selenium 主页所说,与其他测试工具相比,使用 Selenium 的最大好处是:

Selenium 测试直接在浏览器中运行,就像真实用户所做的一样。Selenium 测试可以在 Windows、Linux 和 MacintoshAnd 上的 Internet Explorer、Mozilla 和 Firefox 中运行。其他测试工具都不能覆盖如此多的平台。

使用 Selenium 和在浏览器中运行测试还有很多其他好处。下面是主要的两大好处:

  • 通过编写模仿用户操作的 Selenium 测试脚本,可以从终端用户的角度来测试应用程序。
  • 通过在不同浏览器中运行测试,更容易发现浏览器的不兼容性。

Selenium 的核心,也称 browser bot,是用 JavaScript 编写的。这使得测试脚本可以在受支持的浏览器中运行。browser bot 负责执行从测试脚本接收到的命令,测试脚本要么是用 HTML 的表布局编写的,要么是使用一种受支持的编程语言编写的。

Selenium 适用于以下浏览器:

   Internet Explorer Mozilla Firefox Safari
Windows XP 6.0 1.6+, 1.7+ 0.8+, 0.9+, 1.0   
Red Hat Linux    1.6+, 1.7+ 0.8+, 0.9+, 1.0+   
Mac OS X 10.3 不支持 1.6+, 1.7+ 0.8+, 0.9+, 1.0+ 1.3+
 

Selenium 命令

通过 Selenium 命令,脚本编写者可以描述 browser bot 在浏览器中所执行的操作。可以将这些命令分成两类 —— 操作(action) 和断言(assertion)

  • 操作模拟用户与 Web 应用程序的交互。例如,单击一个按钮和填写一个表单,这些都是常见的用户操作,可以用 Selenium 命令来自动化这些操作。
  • 断言验证一个命令的预期结果。常见的断言包括验证页面内容或当前位置是否正确。

在 Selenium 网站上可以找到可用命令的完整列表(见 参考资料)。

 

Selenium 模式

可以按两种模式来使用 Selenium:test runner 和 driven。这两种模式在复杂性和编写方式方面有所不同。driven 测试脚本编写起来往往要更复杂一些,因为它们是用编程语言编写的。但是如果使用 Python 或 Ruby 之类的高级动态编程语言,那么这种复杂性方面的差异就很小。

两种模式之间最大的不同点在于,如果使用 driven 脚本,测试有一部分在浏览器之外运行,而如果使用 test runner 脚本的话,测试是完全在浏览器中运行的。

不管是 test runner 还是 driven 测试用例,都可以与持续集成工具集成。

 

test runner 模式

Selenium test runner 脚本,也称测试用例(test case),是用 HTML 语言通过一个简单的表布局编写的,如 清单 1 所示。

清单 1. Selenium 测试用例的结构
<table border="1">
<tr>
<td>First command</td>
<td>Target</td>
<td>Value</td>
</tr>
<tr>
<td>Second command</td>
<td>Target</td>
<td>Value</td>
</tr>
</table>

test runner 脚本通常与所测试的应用程序(AUT)部署在同一个服务器上。这是因为 browser bot 使用 JavaScript 来模拟用户操作。这些脚本在一个受限制的沙箱环境中运行。如果需要绕过这些限制,可以使用一个代理。

test runner 脚本使用与 xUnit 框架相同的测试套件(test suite)和测试用例概念。测试用例和命令按照它们在测试套件和测试用例中出现的顺序依次执行。在 清单 1 中:

  • 第一列包含命令 或断言
  • 第二列包含命令或断言的目标(target)。这里可以用多种受支持的组件定位符中的一种来指定目标。通常使用的是组件的 ID 或名称,但 XPath 和 DOM 定位符也是受支持的。
  • 第三列包含用于为命令或断言指定参数的。例如,当使用 type 命令时,这一列可能就是一个文本域所期望的值。

即使对于非技术人员来说,test runner 脚本也易于阅读和编写。当在一个浏览器中打开 清单 1 中的例子时,将得到类似这样的一个表:

First command Target Value
Second command Target Value

接下来,我将描述如何使用命令和断言编写一个简单但是完整的测试用例。

测试用例实例

执行 清单 2 中的测试脚本时,它将执行以下操作:

  1. 通过进入 /change_address_form.html 打开变更地址页面。
  2. 在 ID 为 address_field 的文本框中输入 Betelgeuse state prison
  3. 单击名为 Submit 的输入区。注意,这里使用 XPath 找到 Submit 按钮,这导致表单数据被发送到服务器。
  4. 验证页面是否包含文本 Address change successful
清单 2. 在测试用例中使用命令和断言的例子
  <table>
<tr>
<td>open</td>
<td>/change_address_form.html</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>address_field</td>
<td>Betelgeuse state prison</td>
</tr>
<tr>
<td>clickAndWait</td>
<td>//input[@name='Submit']</td>
<td></td>
</tr>
<tr>
<td>verifyTextPresent</td>
<td>Address change successful</td>
<td></td>
</tr>
</table>

测试套件

要达到对应用程序的完全测试覆盖,通常需要不止一个测试用例。这就是 Selenium 使用测试套件的原因。测试套件用于将具有类似功能的一些测试用例编成一组,以便让它们按顺序运行。

测试套件和测试用例一样,都是用简单的 HTML 表编写的。Selenium 执行的缺省测试套件的名称是 TestSuite.html。清单 3 展示了一个测试套件,该套件像通常的用户一样测试应用程序。注意,测试套件使用一个只包含一列的表,表中的每一行指向一个包含某个测试用例的文件。

清单 3. 测试套件示例
 <table>
<tr>
<td>Test suite for the whole application</td>
</tr>
<tr>
<td><a href="test_main_page.html">Access main page</a></td>
</tr>
<tr>
<td><a href="test_login.html">Login to application</a></td>
</tr>
<tr>
<td><a href="test_address_change.html">Change address</a></td>
</tr>
<tr>
<td><a href="test_logout.html">Logout from application</a></td>
</tr>
</table>

接下来我将把目光转移到 driven 测试脚本。

driven 模式

driven Selenium 脚本是用多种受支持的编程语言中的一种编写的 —— 目前可用的有 Java、Ruby 和 Python 驱动程序。这些脚本在浏览器之外的一个单独的进程中运行。驱动程序的任务是执行测试脚本,并通过与运行在浏览器中的 browser bot 进行通信来驱动浏览器。驱动程序与 browser bot 之间的通信使用一种简单的特定于 Selenium 的连接语言 Selenese。

driven 脚本比 test runner 脚本更强大、更灵活,可以将它们与 xUnit 框架集成。driven 脚本的缺点(与 test runner 脚本相比)是,这种脚本编写和部署起来更复杂。这是因为驱动程序必须执行以下任务:

  • 启动服务器。
  • 部署所测试的应用程序(AUT)。
  • 部署测试脚本。
  • 启动浏览器。
  • 发送命令到 browser bot。
  • 验证 browser bot 执行的命令的结果。

driven 脚本更依赖于应用程序运行时环境。例如,Java 驱动程序使用一个嵌入式 Jetty 或 Tomcat 实例来部署所测试的应用程序。目前,已经有人在致力于将 Selenium 集成到 Ruby on Rails 中,但是在我撰写本文之际,这个集成版本还没有被发布。

清单 4 摘自一个使用 Ruby 驱动程序的 driven 测试脚本。注意,我省略了用于启动服务器和浏览器的步骤,这个测试脚本代码几乎和 test runner 脚本一样简单。

清单 4. 使用 Ruby 驱动程序的例子
    puts selenium.open('/logout.html')
puts selenium.verify_location('/index.html')

现实中的需求

在接下来的两节(现实中的需求 和 现实中的用例)中,我将描述如何在现实场景中使用 Selenium,并针对用 Ruby on Rails 和一点儿 Ajax 技术编写的一个简单的股票报价查看器应用程序编写 Selenium 测试用例。虽然这个应用程序是用 Ruby on Rails 编写的,但是也可以将这个例子应用于任何 Web 应用程序,因为测试脚本是按 test runner 模式以 HTML 编写的。这个示例应用程序是用 Ruby 1.8.3 和 Ruby on Rails 0.14.2 测试的,但是它也可能可以使用更旧的或更新的版本。

如果有 Linux,那么发行版中通常已经包括了 Ruby。在命令提示符下运行 ruby -v,检查您所拥有的版本。对于大多数平台,都可以在http://www.ruby-lang.org/ 上找到一个 Ruby 发行版。

接下来的步骤是通过 RubyGems 打包系统安装 Ruby on Rails。为此,只需执行 gem install rails --include-dependencies。在某些平台上,必须执行一些额外的步骤,所以请访问 Ruby on Rails 网站,以获得更多细节。

在我撰写本文之际,目前可用的 Selenium 版本是 0.6。我已经将它集成在示例应用程序中(见 下载 小节),我的做法是从http://selenium.thoughtworks.com/ 下载 Selenium Core 包,然后将名为 selenium 的文件夹复制到用于静态内容的文件夹。在 Ruby on Rails 应用程序中,这个文件夹的名称是 public。在 J2EE Web 应用程序中,可以将 selenium 文件夹放在 Web 应用程序的根目录或 WAR 归档文件中。

最后一步是下载示例应用程序。从 下载 小节中获得这个包。解压应用程序,并打开一个命令提示符。然后转入应用程序被解压到的那个目录。为了启动应用程序,运行 ruby script/server。应该看到 Rails 成功启动了,如 图 1 所示。

图 1. 从命令提示符下运行 Ruby on Rails

 

现实中的用例

在本节中,我将列出示例应用程序的用例。通过这些简化的用例,可以编写模拟用户所执行步骤的验收测试,并验证这些步骤的结果是否与预期相符。股票报价应用程序实现了以下四个用例:

  • 登录
  • 查看股票
  • 查看股票细节
  • 退出

实现这些用例的代码已经编写好了;可以在 app 目录中找到该代码,测试用例在 public/selenium/tests 文件夹中。

登录用例

大多数人都知道登录页面是如何工作的 —— 输入用户名和密码,然后将数据提交到服务器。如果凭证有效,就可以成功登录,并看到受安全保护的资源。在示例应用程序中,这个测试用例包含以下用户操作和断言,必须将它转换成一个 Selenium 测试用例:

  1. 单击登录链接。
  2. 验证系统是否要求用户进行登录。
  3. 输入用户名。
  4. 输入密码。
  5. 按下登录按钮。
  6. 验证是否登录成功。

图 2 展示了用于这些需求的 Selenium 测试用例。注意,我是在运行测试之后截取屏幕快照的。绿色箭头表示成功地通过验证的断言。

图 2. 登录和查看股票测试用例

查看股票测试用例

查看股票页面显示一个公司列表。用于这个页面的测试用例非常简单,所以被包括在前一个测试用例的后面。该测试用例验证当前位置是否为 /main/list_stocks,以及页面是否包含文本 Click on a company name to view details

查看股票细节用例

查看股票细节用例是在查看股票页面上触发的。用户在一个公司名称上单击鼠标时,就触发了到服务器的一个 Ajax 请求。服务器的响应包括该公司的详细信息,这些信息将插入到当前页面中,而不必重新装载完整的页面。用于这个用例的测试用例执行以下用户操作:

  1. 单击公司名称 Acme Oil
  2. 验证页面上是否显示该公司的详细信息。
  3. 单击公司名称 Acme Automotive
  4. 验证页面上是否显示该公司的详细信息。

由于使用了 Ajax,请求是异步发生的。在一般的 Web 应用程序中,所有东西通常都是同步的,因此这一点提出了一种不同的挑战。可以像测试其他功能一样来测试 Ajax 功能。惟一的不同是,必须让 Selenium 暂停,等待 Ajax 命令完成。为此,可以使用 pause 命令来等待 Ajax 命令的完成。另外,Joseph Moore 在他最近的 blog 贴中提到,还可以使用 waitForValue 和 waitForCondition 命令代替 pause 命令(见 参考资料)。

图 3 展示了被转换成 Selenium 用例的需求。

图 3. 查看股票细节测试用例

注意 pause 命令:必须使用这些命令,以便等待异步请求完成和更新页面内容。如果没有 500 毫秒的暂停,测试将失败(如 图 4 所示)。

图 4. 失败的查看股票细节测试用例

pause 命令还测试 Ajax 功能的非功能性需求。500 毫秒对于 pause 命令是一个很好的值,因为 Ajax 请求应该快速地执行和完成。可以试着去掉pause 命令,看看结果如何。如果测试在您的机器上失败,那么试着将这个值增加到 1000 毫秒。

退出用例

退出用例很容易实现,简单来说只有以下两步:

  1. 单击退出链接。
  2. 验证是否成功退出。

图 5 展示了最后这个测试用例。

图 5. 退出用例

所有测试都被添加到 图 6 左侧显示的缺省测试套件中。

图 6. 示例应用程序的测试套件

执行测试套件

最后要做的是在 Mozilla Firefox 和 Microsoft Internet Explorer 中执行测试套件。为此,在浏览器中打开http://localhost:3000/selenium/TestRunner.html,然后单击 图 6 中所示的 All 按钮。失败的测试用例和断言将被标记为红色,但是这里,在两个浏览器中所有用例都应该可以成功完成(同样见 图 6)。注意,我使用的是 Mozilla Firefox 1.0.7 和 Internet Explorer 6.0。

还可以单步调试测试套件,这意味着 Selenium 将很慢地执行测试套件,这样当测试套件在浏览器中执行时,就可以看到它的每一步。

结束语

Selenium 是软件工程师、设计人员和测试人员的工具箱中又一个有用且重要的工具。通过将该工具与持续集成工具相结合,团队就可以将验收测试自动化,并构建更好的软件,因为他们可以更容易、更早、更频繁地发现 bug。Selenium 的另一个优点是可以节省时间,使开发人员和测试人员不必将时间花在本可以(也应该)自动化的手工任务上,从而让团队将精力放在更有价值的活动上。

Selenium 自动化验收测试的更多相关文章

  1. Selenium自动化脚本开发总结

    Selenium Selenium 是ThoughtWorks专门为Web应用程序编写的一个验收测试工具. Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE.Mo ...

  2. Python2.6.6执行selenium自动化

    系统类型: [root@bogon home]# uname -aLinux bogon 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 20 ...

  3. Selenium自动化中DOM,XPATH,CSS定位Web页面对象的优劣性分析

    加速IE浏览器自动化执行效率:Selenium自动化中DOM,XPATH,CSS定位Web页面对象的优劣性分析 1.技术背景       在Web应用中,用户通过键盘在输入框中输入值和鼠标点击按钮,链 ...

  4. Selenium2学习-036-WebUI自动化实战实例-034-JavaScript 在 Selenium 自动化中的应用实例之六(获取 JS 执行结果返回值)

    Selenium 获取 JavaScript 返回值非常简单,只需要在 js 脚本中将需要返回的数据 return 就可以,然后通过方法返回 js 的执行结果,方法源码如下所示: /** * Get ...

  5. Selenium2学习-032-WebUI自动化实战实例-030-JavaScript 在 Selenium 自动化中的应用实例之五(高亮标示元素)

    在自动化脚本编写过程中,操作元素之前,需要对元素进行高亮显示(通过修改元素的边框样式),同时进行截图,以记录操作的元素对象.在实际应用中较为少见,通常用于演示,或者发生错误时的屏幕截图捕捉,用于错误报 ...

  6. Selenium2学习-031-WebUI自动化实战实例-029-JavaScript 在 Selenium 自动化中的应用实例之四(获取元素位置和大小)

    通过 JS 或 JQuery 获取到元素后,通过 offsetLeft.offsetTop.offsetWidth.offsetHeight 即可获得元素的位置和大小,非常的简单,直接上源码了,敬请参 ...

  7. Selenium2学习-027-WebUI自动化实战实例-025-JavaScript 在 Selenium 自动化中的应用实例之三(页面滚屏,模拟鼠标拖动滚动条)

    日常的 Web UI 自动化测试过程中,get 或 navigate 到指定的页面后,若想截图的元素或者指定区域范围不在浏览器的显示区域内,则通过截屏则无法获取相应的信息,反而浪费了无畏的图片服务器资 ...

  8. Selenium2学习-022-WebUI自动化实战实例-020-JavaScript 在 Selenium 自动化中的应用实例之二(获取浏览器显示区域大小)

    前几篇文章中简略概述了,如何获取.设置浏览器窗口大小,那么我们该如何获取浏览器显示区域的大小呢?此文讲对此进行简略概述,敬请各位小主参阅.若有不足之处,敬请各位大神指正,不胜感激! 获取浏览器显示区域 ...

  9. Selenium2学习-011-WebUI自动化实战实例-009-JavaScript 在 Selenium 自动化中的应用实例之一(赋值)

    通常在编写 Selenium 的 WebUI 自动化脚本时,有些元素不易定位元素,或有些元素为隐藏的(此时用 WebElement.getText() 获取其值的时候,返回的结果为空),对日常的 UI ...

随机推荐

  1. 在C#中使用InputBox

    以前用VB编程常用InputBox,现在学了C#,竟然找不到它了--后来到网上查到了,现在贡献给大家:1.首先要添加引用Microsoft.VisualBasic2.命名空间 Using Micros ...

  2. [React + Mobx] Mobx and React intro: syncing the UI with the app state using observable and observer

    Applications are driven by state. Many things, like the user interface, should always be consistent ...

  3. android 15 activity跳转

    从一个屏幕跳到另一个屏幕,一个activity跳转到另一个activity,Intent类用于组件之间传递数据和跳转,组件包括不仅activity. package com.sxt.day04_01; ...

  4. Linux之TCPIP内核参数优化

    /proc/sys/net目录 所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),例如下面这 ...

  5. CentOS+Nginx+PHP+MySQL详细配置(图解)

    原文地址: http://www.jb51.net/article/26597.htm CentOS+Nginx+PHP+MySQL详细配置(带有图解),需要的朋友可以参考下.   一.安装MySQL ...

  6. Stream类

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

  7. hadoop2集群中的datanode启动以后自动关闭的问题

    今天在启动前几天搭建成功的hadoop集群的时候,出现了datanode启动之后自动关闭的情况,经过查询之后发现问题产生的原因是:在第一次格式化dfs后,启动并使用了hadoop,后来又重新执行了格式 ...

  8. KineticJS教程(1-2)

    1.基本结构 KineticJS首先是要绑定到HTML页面上的一个DOM容器元素上,比如最常用的<div>标签.KineticJS在此容器中创建一个称之为舞台(stage)的结构,这个舞台 ...

  9. @ManyToMany中间表附加字段设计

    在使用@ManyToMany时,若中间表只有相应的外键字段可以直接建立两个对应的Entity 设置ManyToMany @ManyToMany 两个表多对多关联 但若是中间表有自己的附加字段,这需要为 ...

  10. img标签块状与内联的博弈

    新手,请前辈们不吝赐教 说到html中img标签是内联还是块状元素,我们首先要知道什么是内联(inline),什么又是块状(block)? 我也在网上查看了一些别人分享的经验,有一个讲到了文档流的概念 ...