Selenium 的发展经历了三个阶段:Selenium Core、Selenium RC 和 Selenium WebDriver。本文将依次介绍每一个阶段的工做原理,若有错误,请及时指正。html
提示:Selenium Core 用户不直接接触,而 Selenium RC 已通过时,不感兴趣的同窗能够直接看第三节 Selenium WebDriver。
术语列表:java
术语 | 全称 | 中文全称/简介 |
---|---|---|
AUT | Application Under Test | 被测应用 |
Selenium | Selenium | 一款跨浏览器自动化工具 |
Selenium Core | Selenium Core | Selenium 第一代版本,简称 Selenium 1.0,于 2004 年发布 |
Selenium RC | Selenium Remote Control | 基于 Selenium Core 的改进版,属于 Selenium 1.0 |
WebDriver | WebDriver | 和 Selenium Core 相似的跨浏览器自动化工具,于 2007 年首次发布源码 |
w3c WebDriver | w3c WebDriver | 由 WebDriver 发展而来的浏览器自动化协议,有时简称为 WebDriver 协议/规范/API |
Selenium WebDriver | Selenium WebDriver | 2009年8月由 Selenium 1.0 和 WebDriver 项目合并而成,遵循 w3c WebDriver 协议,早期又称做 Selenium 2.0 |
Driver | Driver/Browser Driver | 浏览器驱动,实现了 w3c WebDriver 接口,每一个浏览器都有本身的驱动程序 |
DriverService | DriverService | 驱动服务,运行驱动程序后所起的 HTTP 服务,接收 WebDriver 接口调用后操做浏览器 |
在 2004 年 Internet Explorer 有 93.25% 的市场占有率,当时的开源测试工具要么关注单个浏览器的测试(如 IE),要么是浏览器模拟工具(如 HttpUnit )。没有开源自动化测试工具能支持多浏览器的测试,手工测试执行须要耗费大量的时间和精力。nginx
提示:HttpUnit 是浏览器模拟工具,测试脚本与 HttpUnit 交互,HttpUnit 和真实服务端交互,不通过真实的浏览器,即不是模拟用户操做真实的浏览器。
不过全部浏览器都支持 JavaScript,这为开发支持多浏览器的自动化工具提供了可能。受 Fit: Framework for Integrated Test 启发,ThoughtWorks 的 Jason Huggins 和他的团队想到了一个方案,使用基于表格的关键字驱动语法来编写测试用例。相比原生的 JavaScript 脚本,用户只须要有限的编程能力,另外用例也更容易理解和方便维护。git
基于表格的关键字语法(一个典型的登陆操做):github
Command | Target | Value |
---|---|---|
open | http://example.com/page1.html | |
type | username | testUser |
type | password | testPasword |
clickAndWait | submitButton | |
verifyTextPresent | Welcome, testUser! |
开发团队里有几位成员对 Closure Library 很是熟悉,并且 Closure Library 编译的惟一输出语言是 JavaScript 。因此使用 Closure Library 来开发 Selenium Core 是个合适的选择。web
与不少大型项目同样,Selenium Core 采用了分层构建的架构。
最底层是 Google 开源的 Closure Library,它是一个模块化的、跨浏览器的底层 JavaScript 库,模块化使每一个源文件能够聚焦某个功能而且尽量小,跨浏览器能够帮其屏蔽掉不少浏览器兼容性问题。
中间层是一个包含大量函数的工具层,提供了从简单到复杂的操做,如:获取元素属性值、判断一个元素对用户是否可见、使用合成事件模拟用户点击。该层能够被看做是浏览器自动化的最小操做单元,所以也被称为浏览器自动化原子 Atoms
或 atoms
。
最上层是由 Atoms
组成的适配层,用于实现 Selenium Core
约定的 API,即前面表格里的命令。chrome
至此,只要将表格内容解析为 Selenium Core
的 API,一个完整的跨浏览器自动化工具就实现了。编程
Selenium Core 使用纯 JavaScript 编写,而 JavaScript 存在同源策略的问题,所以使用时须要将 Core 和测试用例部署在与被测应用相同的服务器上(只要被测应用和测试脚本同源就能够)。这也意味着,你没法测试别人的网站,好比 https://www.baidu.com。segmentfault
同源策略:跨域
- Same-origin policy
- 浏览器同源策略及跨域的解决方法
- nginx解决跨域问题
- 补充说明:只有 JavaScript 脚本受同源策略限制,HTML 标签( script、link、img等)不受此影响
Selenium Core 的使用步骤:
selenium-core-1.0.1.zip
。core
文件夹到应用服务器的目录。http://<webservername>:<port>/[path/]core/TestRunner.html
页面运行测试。被测应用有两种运行方式,单窗口模式:在 TestRunner 页面下方使用 iframe
嵌入被测应用,多窗口模式:在新窗口中打开被测应用。访问 https://www.qadoc.org/edu/cat... 查看示例。扩展:使用 HTA 模式(将TestRunner.html
重命名为TestRunner.hta
)能够测试其余网站,但 HTA 仅支持 Windows 上的 IE 浏览器。
最后总结一下:
core
文件夹同级的 tests
文件夹下,使用 HTML 编写基于表格的测试用例页面,并加入测试套件页面。core
和 tests
文件夹到应用服务器的目录,避开同源策略限制,也所以没法测试其余非同源网站,包括页面跳转后的非同源网站。http://<webservername>:<port>/[path/]core/TestRunner.html
页面准备运行测试。Run All tests
或 Run the Selected test
运行测试用例。Selenium Core
获取测试套件的全部测试用例或当前测试用例,解析表格的每一行(固定三列),按行依次调用 Selenium Core
API 执行。Atoms
层函数,Atoms
层调用 Closure Library
函数,由于 Closure Library
和 JavaScript
都是跨浏览器的,因此 Selenium Core
支持多个浏览器。扩展:虽然Selenium Core
自己受限于同源策略,没法测试其余非同源网站,但幸运的是,浏览器插件的 JavaScript 代码并无这个限制,因此基于Selenium Core
的 Selenium IDE 支持任何网站的测试。
上节咱们了解到,Selenium Core
虽然知足了跨浏览器自动化的基本需求,但仍然有一些不足:
淘宝部分子域名示例:
为了解决这些问题,Selenium RC 随之诞生。
为了解决上面的问题,开发团队为 Selenium 写了一个 HTTP 代理服务器,这样 Selenium 能够拦截一切 HTTP 请求。
HTTP 代理: HTTP 代理原理及实现(一)
关于 Selenium RC 架构,官网有详细的描述,这里偷下懒,作下翻译和补充。
下载解压后,启动 Selenium RC Server:
java -jar selenium-server.jar -interactive -browserSideLog -proxyInjectionMode -debug -log seleniumrc.log
当咱们执行测试用例(使用编程语言编写的测试脚本)时,下面的步骤将发生:
补充几点官网没有解释的内容:
Selenium RC 如何启动浏览器
使用命令行方式启动浏览器,启动命令的格式以下:
# 格式:浏览器程序文件路径 + 命令参数 # 示例 "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-hang-monitor --disable-metrics --disable-popup-blocking --disable-prompt-on-repost --proxy-server="localhost:4444" --start-maximized --user-data-dir="C:\Users\Think\AppData\Local\Temp\customProfileDir7a0b6e794a9445338159f582e1b8e3aa" http://oktools.xyz/selenium-server/core/RemoteRunner.html?sessionId=7a0b6e794a9445338159f582e1b8e3aa&multiWindow=false&baseUrl=http%3A%2F%2Foktools.xyz%2F&debugMode=true
更多细节能够翻阅 org.openqa.selenium.server.browserlaunchers.AbstractBrowserLauncher#launch
实现类对应的方法。
Selenium RC 如何和浏览器通讯
这个问题乍看起来好像不是问题,但仔细想,浏览器并非服务端,即没有处理请求的能力,因此 RC 没法通知浏览器。有人可能会想到使用 websocket,但 websocket 2011 年才成为浏览器标准,而在 2004 年的时候,Selenium RC 显然还没法使用它。
当时有一种叫 Comet 的技术,Selenium RC 最终选用的是 XMLHttpRequest 长轮询,用于保持和浏览器的链接。下图是一个简单的示例,客户端(图示中使用的是 Selenium Sever 的交互模式,功能和客户端相同)前后发起了两个命令:getNewBrowserSession
、open
。
服务器收到 getNewBrowserSession
命令后,启动浏览器,浏览器访问 RemoteRunner.html,该页面经过 script 等标签引入了 Selenium Core。
getTitle
命令。setContext
命令,通知 Selenium Core 设置上下文。整个 XMLHttpRequest 长轮询的过程至关因而一个 Response/Request 模式,服务器将要通知浏览器的内容放入 Response Body 中,浏览器将本次 Response 的处理结果放入下次的 Request URL 和 Body中,所以对于服务器来讲,每次 HTTP 响应至关于服务器的请求,每次 HTTP 请求至关于服务器的响应。
因为文章篇幅有限,open 命令的执行过程就不介绍了。上图的 uniqueId
是什么用途呢?每当页面跳转或刷新后,意味着上个页面会关闭,新页面须要继续保持和服务器的链接,这里的 uniqueId
能够理解成页面的标识,每当上个页面关闭后,下个页面就会使用新的 uniqueId
发起请求,同时 sequenceNumber
被重置为 0。
该模式和代理注入很是相似,主要区别在于浏览器以一个被称为 Heightened Privileges
的特殊模式启动,它容许网站作一些一般不被容许的事(好比 XSS、填充文件上传输入框以及对 Selenium 颇有用的东西)。该模式下,Selenium Core 能够直接打开 AUT 并读取它的内容或与它的内容交互,而没必要经过 Selenium RC 服务器访问整个 AUT。
早期的 WebDriver
是和 Selenium Core
几乎同时期的来自 ThoughtWorks 的浏览器自动化工具,也是基于 Atoms
构建,这和后来咱们所熟悉的 w3c WebDriver
协议有些不一样。
Selenium Core
和 WebDriver
的设计理念有很大不一样。WebDriver
的开发人员倾向于向用户隐藏其并不关心的不少细节,提供尽量简单的 API,好让用户聚焦在用例设计和发现 Bug 上,正如他们擅长的那样,而 Selenium Core
则是给用户提供低级别的 API 。如下是 Selenium Core
用于设置 input 元素值的方法:
它们等价于 WebDriver
的 sendKeys
API,正如前面说的同样,WebDriver
努力模仿访问被测应用的用户。xxx 和 xxxNative 的区别在于,前者使用合成事件,后者经过 java.awt.Robot
模拟键盘输入来模拟用户操做。
讲述 Selenium WebDriver
原理以前,咱们先来看下 w3c WebDriver
协议,这里以 ChromeDriver 为例。
术语解释:
- W3C WebDriver 是一个浏览器协议,又称
WebDriver 协议
/WebDriver 规范
/WebDriver API
- Driver 是 WebDriver API 的特定实现,好比 Chrome 浏览器的 ChromeDriver。
- ChromeDriver 是一个能够独立运行的服务器程序,它实现了 WebDriver 协议。
- Selenium WebDriver 是一个基于 WebDriver 协议的 Web 自动化框架。
在命令行窗口执行如下命令启动 ChromeDriver。
# 为了方便,建议先切换到 exe 文件所在目录 chromedriver_80.exe -port=12345
ChromeDriver 启动成功后,将获得一个服务器访问地址 http://localhost:12345
。打开任务管理器,咱们能够看到 chromedriver_80.exe
进程。
一、调用 New Session 接口,建立一个 Session,返回 sessionId=be92a2485bcdb5ca0d3bc44bd0b00a8d。与此同时将打开一个 Chrome 浏览器窗口。
endpoint_webdriver=
http://localhost:12345
思考题:若是再次调用该接口会发生什么呢?
二、调用 Navigate To 接口,访问百度网站,等价于 Java 客户端的 driver.get("https://www.baidu.com")
。
执行结果:
其余接口相似,再也不一一举例。这些步骤是否是和使用 Java/Python 编写测试步骤时似曾相识?总结起来就是两步:
浏览器驱动大部分是各浏览器厂商根据 WebDriver 规范实现的,因此 Selenium 再也不须要直接操做浏览器,而是经过 HTTP 接口向驱动发出符合 WebDriver 规范的指令。
有一点须要注意,虽然低版本的 Firefox(47及如下) 和 Safari 不须要单独下载驱动程序,但这不是说 Firefox 和 Safari 不须要 WebDriver 驱动,而是由于在 Selenium 客户端中内置了浏览器的 webdriver 扩展,这些浏览器扩展起到了和驱动相同的做用,一样遵循 W3C WebDriver 协议。
从 Selenium 的源码中能够发现这些特殊状况:
webdriver.xpi
,经过 HTTP 通讯。client.js
,经过 WebSocket
通讯。Selenium3 中 Safari 使用苹果系统自带的 safaridriver,经过 HTTP 通讯。
/usr/bin/safaridriver
/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver
了解了 WebDriver 协议后,咱们回过头来看 Selenium WebDriver 的工做原理就简单不少了。
若是咱们有一个客户端库,负责浏览器驱动的启动、管理,HTTP 请求的组装、响应的处理等诸多和测试脚本自己无关的事,测试用例的编写将变得更加容易。而 Selenium WebDriver 就提供了一系列这样的客户端库,除此以外还提供了 Selenium Grid 用于分布式执行。
从上图中能够看出,Java 编写的测试脚本中的命令由 Java 客户端转为 HTTP 请求并访问 WebDriver 服务器(驱动服务),浏览器驱动收到 HTTP 请求后,操做浏览器,就用网站用户那样。
若是你读过上面 Selenium RC 的部分,你就会发现,Selenium WebDriver 相比 Selenium RC,从架构上来讲要简单不少,API 也更加简洁。这对用户和 Selenium 的开发者来讲,都是一个质的飞跃。若是你考虑到 Selenium 要支持 X 个操做系统、Y 种浏览器和 Z 种编程语言,你就能理解 Selenium 的开发工做量有多大了,在 Selenium RC 时代,大量代码和编程语言绑定(好比 XHR 的长轮询、SSL 证书管理),这让每次迭代更新都很痛苦。
上面的 Selenium WebDriver 原理图,等价于官网的这张图。
除此以外,还能够有不一样的部署方式,使用 Remote WebDriver
,操做远程主机上的浏览器。
经过 Selenium Server 或 Selenium Grid 与驱动通讯。
由于对 Firefox 的支持,已经从内置的 firefox xpi 变为和其余驱动相似的 geckodriver,因此再也不过多介绍。若是你对 Selenium 客户端中内置的 firefox xpi 插件的工做机制感兴趣,能够阅读参考资料 [1] 中的 16.6. The Remote Driver, and the Firefox Driver in Particular
部分。
参考资料: