7.2 Selenium
7.2.1 什么是 Selenium
Selenium9 是实现 Web 应用程序的功能测试以及集成测试自动化的浏览器驱动测试工具群,具备以 Web 应用程序的测试自动化为特点的各类功能。
和使用系统的用户相同,在浏览器上进行的鼠标操作、在表单中输入文字或者打开页面时,Selenium 能够检查页面的内容是否正确显示、检查表单的值以及验证页面内执行的 JavaScript 等。因此借助 Selenium 能够重复实施至今为止手动操作的测试。
7.2.2 Selenium 的优点
下面介绍一下 Selenium 的主要特点。
●…… 自动化测试用例制作简单
首先是测试用例的制作非常简单。Selenium 提供了名为 Selenium IDE10 的工具。该工具具备记录(capture)键盘或鼠标的操作并播放(replay)的“capture·replay”功能,能够非常简单地制作测试用例。
10 还有名为 Selenium Builder 的 Selenium IDE 的后续工具。
因为能直接根据浏览器上的操作生成测试用例,所以不善于编程的工程师也能简单地制作自动测试用例。
●…… 支持多种浏览器及 OS
其次是测试可使用的浏览器种类多样。现在支持的浏览器有 Internet Explorer(简称 IE)、FireFox、Chrome、Safari、Opera、iPhone 标准的浏览器(Safari)、Android 标准的浏览器,可以说是支持了所有主要的浏览器。并且还支持 Windows、OS X、Linux、Solaris 等多种 OS。
无论对功能进行如何详尽的测试,如果在 FireFox 上正常运行,在 IE 上无法运行的话,这样是没有意义的。因此 Selenium 支持在各种浏览器上执行同样的测试用例。
在系统支持的所有浏览器上手动执行同样的测试是一件非常无聊且麻烦的工作。这样繁琐的作业就应该实现自动化。
7.2.3 Selenium 的组件
如上所述,Selenium 作为自动测试 Web 应用程序的工具群,是由多个组件(工具)构成的。根据运行方式的不同,所使用的组件也有所差异。这里简单地介绍一下具有代表性的 Selenium IDE、Selenium Remote Control 和 Selenium WebDriver。
●…… Selenium IDE
Selenium IDE11 是 Selenium 测试用的 IDE,是一款支持从测试用例的制作到执行、调试的 Firefox 的插件(图 7.4)。
11 http://www.seleniumhq.org/projects/ide/
图 7.4 Selenium IDE
Selenium IDE 能够通过录制浏览器的操作来制作测试用例,编辑录制的测试用例时还支持自动补足(autocomplete),因此非常方便。并且制作的测试用例能够当场立即执行,还能够通过在测试用例中设置断点来确认执行暂停时的状态,可以说是高效地制作测试用例必不可少的工具。
没有使用过 Selenium 的人,可以从使用 Selenium IDE 开始,来熟悉 Selenium 的测试用例。
●…… Selenium Remote Control(Selenium RC)
Selenium Remote Control12 (以下简称 Selenium RC 或 Selenium1)和 Selenium IDE 一样也是自动测试 Web 应用程序的工具(图 7.5)。但是,不同于 Selenium IDE 自身就能制作测试用例并执行测试,Selenium RC 需要在运行浏览器的机器上启动名为 Selenium Server13 的服务器,该服务器用于中继测试脚本(测试用例)和浏览器,并通过该服务器来执行测试。
12 http://www.seleniumhq.org/projects/remote-control/
13 Selenium Server 就是 Selenium WebDriver 发布之前的称为“Selenium RC Server”的中继服务器,和 WebDriver 合并成为 Selenium2 后开始改名为“Selenium Server”。
图 7.5 使用 Selenium RC 的构造
Selenium RC 可以启动、终止测试用的浏览器、执行测试脚本上的浏览器操作命令等,还能够起到类似于 HTTP 代理的功能,因此可以在 Firefox 以外的多种浏览器上执行测试。并且 Selenium RC 还提供了各种语言 14 能够调用的 API,因此可以使用各种编程语言制作测试用例。利用编程语言来制作测试用例,可以实现循环处理以及分支条件,还可以将共通处理做成库,因此可以制作出更为高效的测试用例 15 。
14 主要可使用的语言有 Java、Ruby、Python、Perl、PHP、.NET。
15 即将发布的 Selenium3 将不推荐使用 Selenium RC。虽然本书提到了较多关于 Selenium RC 的内容,但开始新的项目时还是推荐使用 Selenium WebDriver。http://seleniumhq.wordpress.com/2013/08/28/the-road-to-selenium-3/
●…… Selenium WebDriver
Selenium 是一款非常优秀的测试工具,但由于操作浏览器的发动机部分是基于 JavaScript 的,因此经常会受到浏览器的安全限制。随着 Ajax(Asynchronous JavaScript and XML)的普及,越来越多的 Web 应用程序开始使用 JavaScript,这个问题也变得越发严重。
另一方面,从 2006 年前后开始,为了解决 JavaScript 限制的问题,Google 的 Simon Stewart 开始开发名为 WebDriver 的项目(图 7.6),通过调用 OS 的原生接口来操作浏览器。
图 7.6 使用 Selenium2 的构造
WebDriver 项目和 Selenium 项目能够互补,为用户提供更好的测试框架,基于这个原因,两者被合并在一起,并发布了 Selenium WebDriver16 (以下称为 Selenium2)。Selenium2 发布后,原来的 Selenium RC 就被称为 Selenium1 了。
16 http://www.seleniumhq.org/projects/webdriver/
Selenium2 结合了 Selenium1 的“支持多种浏览器”“丰富的测试描述语言”的优点,以及 WebDriver 的“在 JavaScript 沙箱(sandbox)外部运行”“高速轻量且通用的浏览器仿真器”的优点 17 。
17 具体请参考 http://www.seleniumhq.org/docs/01_introducing_selenium.jsp#brief-history-of-the-selenium-project 。
7.2.4 测试用例的制作和执行
下面我们就来看一下如何制作 Selenium 的测试用例。
●…… Selemium IDE 的安装和运行
Selenium IDE 是 Firefox 的插件。用 Firefox 打开 http://www.seleniumhq.org/download/ ,点击最新版本的链接,即可进行安装。
启动 Selenium IDE,在浏览器上迁移画面、输入文字就可以直接将操作记录为命令,只需几分钟就可以制作好简单的测试用例。接着执行测试用例,就可以检验系统是否正常运行。
●…… Selenium 的测试用例
简单地说一下 Selenium 的测试用例。Selenium 的测试用例可以用多种编程语言来制作,这里说明的是用 Selenium IDE 制作的 HTML 形式的测试用例。
Selenium 的测试用例由多个测试用例和测试套件(test suite)构成。
测试套件由多个测试用例组合而成。通过组合成为测试套件,多个测试用例就能按照指定的顺序执行(图 7.7)。
图 7.7 使用测试套件的结构示例
在 Selenium IDE 中创建多个测试用例后,点击“New Test Suite”就能创建测试套件。用编辑器打开建好的测试套件,就可以看到连接到各个测试用例的链接,如代码清单 7.1 和代码清单 7.2 所示。
代码清单 7.1 测试套件的例子(testsuite01.html)
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict. dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta content="text/html; charset=UTF-8" http- equiv="content-type" /><title>Test Suite</title></head> <body> <table id="suiteTable" cellpadding="1" cellspacing="1" border="1" class="selenium"> <tbody><tr> <td><b>login</b></td> </tr> <tr> <td><a href="login_successful.html">login_successful</a></td> </tr> <tr> <td><a href="login_failed.html">login_failed</a></td> </tr> </tbody> </table> </body> </html>代码清单 7.2 测试用例的例子(login_successful.html)
<?x邮件列表 version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www. w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head profile="http://selenium-ide.openqa.org/profiles/test-case"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="selenium.base" href="http://example.com/" /> <title>login successful</title> </head> <body> <table cellpadding="1" cellspacing="1" border="1"> <thead> <tr><td rowspan="1" colspan="3">test</td></tr> </thead><tbody> <tr> <td>open</td> <td>/</td> <td></td> </tr> <tr> <td>waitForTextPresent</td> <td>ID、请输入密码</td> <td></td> </tr> <tr> <td>type</td> <td>id=loginId</td> <td>admin</td> </tr> <tr> <td>type</td> <td>id=loginPassword</td> <td>admin</td> </tr> <tr> <td>clickAndWait</td> <td>id=login</td> <td></td> </tr> <tr> <td>verifyTextPresent</td> <td>You are signed in to these accounts: </td> <td></td> </tr> </tbody></table> </body> </html>●…… 什么是好的测试用例
虽说测试越快完成越好,但据说“用 Selenium 测试非常费时”。如果测试要耗费几个小时的话,那么频繁地执行测试就比较困难了,只能将多个修改合在一起进行测试,但这样测试失败时就难以分析原因了。并且若提交数天后才发现有 bug,开发的节奏也会受到影响,无法高效地进行。
制作测试用例、执行测试、确认结果,然后再制作测试用例、执行测试、确认结果……为了让开发人员能够像这样频繁地进行测试,测试再长也应该在 30 分钟内结束。为了缩短测试时间,在开始制作测试用例时就需要考虑下面这几点。
单独执行各个测试套件也必须能够通过测试
1 个测试套件的执行时间不能超过所预期的全部测试的构建时间
各个测试套件必须能够独立执行,依赖于其他测试套件的就说不上是好的测试套件。这是因为,如果测试套件之间相互依赖的话,就必须按照固定的顺序执行,这样一来就难以实现并行执行,测试的时间也会逐渐拉长。
相反,如果测试套件之间相互独立,就有可能将运行所有测试的时间缩短到和最长的测试套件的执行时间相同。因此在制作测试用例时要时刻意识到上述两点。
●…… 用 Selenium Server 来运行测试
Selenium IDE 在制作测试用例方面是非常强大的开发工具,但并不适用于定期、持续地执行测试用例。也就是说,开始测试时必须在运行浏览器的机器上点击 Selenium IDE 的开始测试按钮。随着测试用例的逐渐增加,这样的测试手段可以说将变得越来越不现实。
为了解决上述问题,下面将介绍使用 Selenium Server 来执行用 Selenium IDE 制作的测试用例的方法。
下载 Selenium Server 后 18 ,通过下列命令运行 Selenium Server,就能够解析 HTML 格式的测试用例、启动作为测试对象的浏览器、执行测试并输出测试结果的报告。
18 可以从 http://www.seleniumhq.org/download/ 下载。
C:\>java -jar selenium-server-standalone-<version-number>.jar -htmlSuite "*firefox" "http://example.com" "C:\path\to\testsuite01.html" "C:\path\to\results.html"命令的选项由 -htmlSuite<browser><startURL><suiteFile><resultFile> 构成。可以通过选项 -h 来查看选项列表。
C:\>java -jar selenium-server-standalone-<version-number>.jar -h来看一下上述命令的例子中 -htmlSuite 选项的相关内容。
第 1 个参数“firefox”表示进行测试的浏览器的种类。修改为“iexplore”的话就可以使用 IE 进行测试。
第 2 个参数“http://example.com”是 Base URL。对系统的多个不同版本执行相同的测试用例的情况下使用该参数。测试用例的 open 命令所指定的 URL 的路径(相对路径)就是基于这个由 Base URL 指定的域名的,并作为绝对 URL 来执行。例如,在有开发团队 A 用的测试环境(http://a.example.com)和团队 B 用的测试环境(http://b.example.com)这样的情况下,可以使用这个参数。
第 3 个参数“C:\path\to\testsuite01.html”是测试套件文件的路径。请指定绝对路径。
第 4 个参数“C:\path\to\results.html”是测试结果报告的生成路径。同样要指定绝对路径。
这样通过使用 Selenium Server,就不必从 Selenium IDE 手动开始执行测试了,配合使用 Cron19 或 Jenkins 就能够定期地执行测试,并且还能够在 Linux 或 Mac OS 等多个操作系统上测试多种浏览器。
19 自动执行任务的守护程序。
至此,我们讲了使用 Selenium IDE 制作测试用例,利用 Selenium Server 执行测试的相关内容。这样就可以利用 Selenium 执行持续的集成 测试了。
7.2.5 Selenium 的实际应用
●…… 测试页面是否有改动
根据系统或功能所要求的品质的级别,存在不允许预期以外的画面变更的情况。另外有的公司必须保存并提交画面的截图作为实施测试的证据。这时就可以使用 Selenium 的画面截图功能,实现打开画面后截图这种麻烦的作业的自动化。这里将介绍画面截图的方法,以及将获取的截图和以前的截图进行比较、测试的方法。
Selenium 可以通过“captureEntirePageScreenshot”或“captureEntireP ageScreenshotAndWait”命令来截取当前浏览器的画面(图 7.8)。纵向较长的画面也能滚动后截取下来。在保存的文件名中输入“C:\path\to\img\capture1.png”这样的绝对路径来保存截图。不设定背景色的话会输出黑色的背景,所以可以预先设置好“background=#FFFFFF”等。
图 7.8 截取画面的例子
<tr> <td>captureEntirePageScreenshot</td> <td>C:\path\to\img\capture1.png</td> <td>background=#FFFFFF</td> </tr>接着说一下对保存的画面截图进行比较测试的方法。例如,将系统版本 1 时截取的画面和即将发布的版本 2 所截取的画面进行比较。对版本 1 和版本 2 执行相同的 Selenium 测试用例,各自输出同名的截图文件,分别存放在 C:\path\to\img\v1 和 C:\path\to\img\v2 这两个目录下。
图像的比较采用名为 ImageMagick20 的图像处理软件来进行。安装ImageMagick21 后执行下列命令。可以利用本书提供的脚本 22 来比较目录下的所有图像,并以 HTML 文件的形式输出结果。
20 http://www.imagemagick.org/
21 ImageMagick 的下载地址为 http://www.imagemagick.org/script/binary-releases.php 。
22 打开 http://www.ituring.com.cn/book/1495 ,点击“随书下载”。
C:\>composite.exe -compose difference C:\path\to\img\v1\capture1.png C:\path\to\img\v2\capture1.png C:\path\to\img\diff\capture1.png可以通过高光的图像来确认比较结果中存在差异的部分(图 7.9)。通过比较画面截图的方法,能够发现测试工程师无论如何测试都无法发现的问题,请一定要试一下。
图 7.9 附录脚本的执行结果文件
●…… 使 Selenium 测试稳定运行
Selenium 的自动测试并不是很稳定。经常听说系统明明正常运行,测试却失败了这样的事情。例如,点击链接,在画面加载完成前挂起下一步操作的 clickAndWait 命令执行时,点击链接迁移到下一个页面并试图点击页面内的按钮,但无法找到该按钮而造成测试失败。
上述情况下,因为 clickAndWait 命令执行成功,所以下一个页面应该已经加载完成了,那为什么无法点击本应正常显示的按钮而测试失败了呢?能够想到的原因有服务器内部的数据备份或执行的测试造成服务器的负载变高,对浏览器的响应速度变慢等。
但其实测试失败的本质问题在于服务器的响应速度慢,在页面内容还没有加载完毕的状态下 Selenium 就执行了下一条命令,从而导致测试失败。刚才的例子中,Selenium 虽然用 clickAndWait 命令等待画面加载,但没有等待该画面中接着要点击的按钮加载完毕就直接执行了下一条命令,所以造成了测试失败。
作为 Selenium 的创始人、公司的 CTO,Sauce Labs 在博客中这样叙述。
"Selenium tests often fail because they're too fast. Where a user might wait for a page to load for a few seconds and then click on a link, Selenium will interact with a page at the speed of code, before the page is ready. ”Selenium 的测试经常会因为速度过快而造成失败。人为操作的话会等待画面显示后再点击链接,Selenium 则是在画面完全显示之前以执行程序的速度实施测试的。
── How to Lose Races and Win at Selenium(http://sauceio.com/index.php/2011/04/how-to-lose-races-and-win-at-selenium/ )
通常,人为操作浏览器时会等待按钮显示或页面加载完后再进行之后的操作,而 Selenium 则是直接继续之后的操作。
该如何解决这个问题呢?解决方法之一就是在使用 clickAndWait 命令等进行画面迁移之后,暂停(pause)固定数秒钟的时间,通过延迟执行下一条命令,多少能够所有改善。但这个方法为了解决偶尔发生的问题不得不在测试用例的各处插入 pause 命令,会造成测试执行时间变长,因此并不推荐。作为其他的解决方案,上述微博中还提到了下面的内容。
“The way to fix this is to have Selenium repeat its actions and assertions until they work. If you don't, Selenium races your browser. ”为了解决这个问题,Selenium 在得到正确的结果前必须重复动作或断言(assertions)处理,不这样的话 Selenium 就会和浏览器的画面显示速度持续竞争。
── How to Lose Races and Win at Selenium(http://sauceio.com/index.php/2011/04/how-to-lose-races-and-win-at-selenium/ )
即命令执行失败时,重复执行该命令多次,直到正常运行为止。
但是用通过 Selenium IDE 做成的 HTML 形式的测试用例进行测试的情况下,是由 Selenium Server 来解析测试用例并操作浏览器的,因此不修改 Selenium Server 的代码就无法实现“重复数次执行该命令直到正常运行为止 ”。若要重复地实施处理,就需要通过编程语言(Java、Ruby、Python、Perl)来制作测试用例,自己添加重复处理。
下面介绍在用 Selenium IDE 制作测试用例的情况下,应该如何加入重复处理。按照下述步骤就可以修改从 Selenium IDE 导出的测试脚本中的任意一条命令。
❶ 打开 Selenium IDE 的“Option”→“Option…”→“Formats”标签
❷ 选择作为例子所使用的格式(Java/JUnit4/RemoteControl 等),点击 source 按键,复制所显示的代码
❸ 点击 Add 按键,将步骤 2 复制的代码粘贴上去,对需要修改的地方进行修改
❹ 输入格式名称并保存
❺ 重启 Selenium IDE 和 Firefox
❻ 以步骤 4 做成的格式导出测试用例
将代码清单 7.3 修改为代码清单 7.4 那样,就可以反复执行确认画面中的文字内容是否显示的 verifyTextPresent 命令。
代码清单 7.3 修改前的代码
function verifyTrue(expression) { return "verifyTrue(" + expression.toString() + ");"; }代码清单 7.4 修改后的代码
function verifyTrue(expression) { return "for (int second = 0;; second++) {\n" + "\tif (second >= 60) fail(\"timeout\");\n" + "\ttry { " + (expression.setup ? expression.setup() + " " : "") + "if (assertTrue(" + expression.toString() + ")) break; } catch(Exception e) {}\n" + "\tThread.sleep(1000);\n" + "}\n"; }这样就能导出如代码清单 7.5 所示的测试脚本。在该测试脚本中,即使画面没有显示要确认的文字而造成 assertTrue 失败,经过一定时间后还会重新尝试确认。
代码清单 7.5 导出后的测试用例
for (int second = 0;; second++) { if (second >= 60) fail("timeout"); try { if (assertTrue(selenium.isTextPresent("请输入ID、密码"))) break; } catch (Exception e) {} Thread.sleep(1000); }不要直接使用 Selenium IDE 制作的测试用例或以某种格式导出的测试脚本,根据测试脚本的制作方法的不同,确实能够减少由于页面加载时间所造成的测试失败。
