Selenium最早由Jason Huggins在2004年创建的,最初只是一些能够与web页面交互的JavaScript的函数库。这套函数库渐渐地就成为了之后Selenium RC 和Selenium IDE的基础。但是随着web功能的日益强大和安全性的问题,基于JavaScript的Selenium RC 的局限性越来越大。在2006年的Google的工程师开始了另一个项目WebDriver。与Selenium RC最大的不同是,WebDriver不在通过中介JavaScript,而是利用浏览器和操作系统提供的原生方法直接操作浏览器。08年的时候Selenium与WebDriver项目合并了,变成了现在我们熟知的Selenium。
主要组成部件就是Selenium RC,他的工作原理就是通过一堆JavaScript函数来操作浏览器。从Selenium 3.0开始已经不再支持RC,它正式退出了历史舞台。
早期的Selenium1.x使用的是Javascript注入技术与浏览器打交道,需要Selenium RC启动一个Server,将操作Web元素的API调用转化为一段段Javascript,在Selenium内核启动浏览器之后注入这段Javascript。开发过Web应用的人都知道,Javascript可以获取并调用页面的任何元素,自如的进行操作。由此才实现了Selenium的目的:自动化Web操作。这种Javascript注入技术的缺点是速度不理想,而且稳定性大大依赖于Selenium内核对API翻译成的Javascript质量高低。
相比于Selenium1.x,Selenium2.x版本整合了WebDriver以及原版Selenium。Selenium 2 = Selenium 1 + WebDriver。虽然名字还叫Selenium,但也可以叫WebDriver。WebDriver是直接调用浏览器的方法来实现各种操作的,所以在Web Driver 的环境中需要有各种浏览器内核的驱动。在3.0中,已经不再附带任何浏览器内核的驱动,需要的可以去官网下载。
当Selenium2.x 提出了WebDriver的概念之后,它提供了完全另外的一种方式与浏览器交互。那就是利用浏览器原生的API,封装成一套更加面向对象的Selenium WebDriver API,直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏,窗口大小,启动,关闭,安装插件,配置证书之类的)。由于使用的是浏览器原生的API,速度大大提高,而且调用的稳定性交给了浏览器厂商本身,显然是更加科学。然而带来的一些副作用就是,不同的浏览器厂商,对Web元素的操作和呈现多少会有一些差异,这就直接导致了Selenium WebDriver要分浏览器厂商不同,而提供不同的实现。例如Firefox就有专门的FirefoxDriver,Chrome就有专门的ChromeDriver(甚至包括了AndroidDriver和iOS WebDriver)。
其实相对于与Selenium2,Selenium3没有做太多的改动。
对于Selenium3来说最大的变动可能就是更加专注于手机和web的测试,尤其是手机的支持。
Selenium 3将删除最初的Selenium核心实现,官方不再推荐使用RC API。旧版本仍然可以单独下载,但是除了非常紧急的修复之外,活跃的开发将停止。官方仍将提供一个由WebDriver支持的RC API的实现,因此可以继续运行现有的测试,但官方推荐以后使用WebDriver API。
是Firefox的一个插件,提供简单的录制回放的功能,并且可以基于录制的步骤生成指定语言的代码,支持的语言非常多样。
Grid让你的测试脚本在多个环境中分布执行成为了可能。如果你有大量的测试用例,或者有多种测试环境需要验证,它能节省大量的时间。
早期的Selenium使用的是JavaScript注入技术与浏览器打交道,需要Selenium RC启动一个Server,将操作Web元素的API调用转化为一段段Javascript,在Selenium内核启动浏览器之后注入这段Javascript。开发过Web应用的人都知道,Javascript可以获取并调用页面的任何元素,自如的进行操作。由此才实现了Selenium的目的:自动化Web操作。这种Javascript注入技术的缺点是速度不理想,而且稳定性大大依赖于Selenium内核对API翻译成的Javascript质量高低。
WebDriver是W3C的一个标准,由Selenium主持。具体的协议标准可以从官方网站查看。
从这个协议中我们可以看到,WebDriver之所以能够实现与浏览器进行交互,是因为浏览器实现了这些协议。这个协议是使用JOSN通过HTTP进行传输。
它的实现使用了经典的Client-Server模式。客户端发送一个requset,服务器端返回一个response。
在我们创建一个WebDrive实例的过程中,Selenium首先会确认浏览器的native component是否存在可用而且版本匹配。接着就在目标浏览器里启动一整套Web Service,这套Web Service使用了Selenium自己设计定义的协议,名字叫做The WebDriver Wire Protocol。这套协议非常之强大,几乎可以操作浏览器做任何事情,包括打开、关闭、最大化、最小化、元素定位、元素点击、上传文件等等等等。
WebDriver Wire协议是通用的,也就是说不管是FirefoxDriver还是ChromeDriver,启动之后都会在某一个端口启动基于这套协议的Web Service。例如FirefoxDriver初始化成功之后,默认会从http://localhost:7055开始,而ChromeDriver则大概是http://localhost:46350之类的。接下来,我们调用WebDriver的任何API,都需要借助一个ComandExecutor发送一个命令,实际上是一个HTTP request给监听端口上的Web Service。在我们的HTTP request的body中,会以WebDriver Wire协议规定的JSON格式的字符串来告诉Selenium我们希望浏览器接下来做社么事情。
使用Selenium实现驱动浏览器,主要涉及三个东西。
1、测试代码
测试代码就是程序员利用不同的语言和相应的selenium API库完成的代码。自动化测试代码发送请求给浏览器的驱动(比如火狐驱动、谷歌驱动)
2、Webdriver
Webdriver是针对不同的浏览器开发的,不同的浏览器有不同的webdriver。例如针对Chrome使用的chromedriver。
它来解析这些自动化测试的代码,解析后把它们发送给浏览器。
3、浏览器
浏览器和相应的Webdriver对应。执行浏览器驱动发来的指令,并最终完成工程师想要的操作。
测试代码中包含了各种期望的对浏览器界面的操作,例如点击。测试代码通过给Webdriver发送指令,让Webdriver知道想要做的操作,而Webdriver根据这些操作在浏览器界面上进行控制,由此测试代码达到了在浏览器界面上操作的目的。
对于三个部分的关系模型,可以用一个日常生活中常见的例子来类比。
对于打的这个行为来说,乘客和出租车司机进行交互,告诉出租车想去的目的地,出租车司机驾驶汽车把乘客送到目的地,这样乘客就乘坐出租车到达了自己想去的地方。
这和Webdriver的实现原理是类似的,测试代码中包含了各种期望的对浏览器界面的操作,例如点击。测试代码通过给Webdriver发送指令,让Webdriver知道想要做的操作,而Webdriver根据这些操作在浏览器界面上进行控制,由此测试代码达到了在浏览器界面上操作的目的。
1.对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动
2.浏览器驱动中包含了一个HTTP Server,用来接收这些http请求
3.HTTP Server接收到请求后根据请求来具体操控对应的浏览器
4.浏览器执行具体的测试步骤
5.浏览器将步骤执行结果返回给HTTP Server
6.HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息。
JSON Wire protocol是在http协议基础上,对http请求及响应的body部分的数据的进一步规范。body部分主要传送具体的数据,在WebDriver中这些数据都是以JSON的形式存在并进行传送的,这就是JSON Wire protocol。所以在Client和Server之间,只要是基于JSON Wire Protocol来传递数据,就与具体的脚本语言无关了,这样同一个浏览器的驱动就即可以处理Java语言的脚本,也可以处理Python语言的脚本了。
先通过一个简单的类比说个好理解的,这个比喻是我从美版知乎Quora上看到的,觉得比较形象、好理解拿来用用。
我们可以把WebDriver驱动浏览器类比成出租车司机开出租车。
在开出租车时有三个角色:
乘客:他/她告诉出租车司机去哪里,大概怎么走
出租车司机:他按照乘客的要求来操控出租车
出租车:出租车按照司机的操控完成真正的行驶,把乘客送到目的地
在WebDriver中也有类似的三个角色:
自动化测试代码:自动化测试代码发送请求给浏览器的驱动(比如火狐驱动、谷歌驱动)
浏览器的驱动:它来解析这些自动化测试的代码,解析后把它们发送给浏览器
浏览器:执行浏览器驱动发来的指令,并最终完成工程师想要的操作。
所以在这个类比中:
1. 工程师写的自动化测试代码就相当于是乘客
2. 浏览器的驱动就相当于是出租车司机
3. 浏览器就相当于是出租车
下面再从技术上解释下WebDriver的工作原理:
从技术上讲,也同样是上面的三个角色:
1. WebDriver API(基于Java、Python、C#等语言)
对于java语言来说,就是下载下来的selenium的Jar包,比如selenium-java-3.8.1.zip包,代表Selenium3.8.1的版本
2. 浏览器的驱动(browser driver)
每个浏览器都有自己的驱动,均以exe文件形式存在;比如谷歌的chromedriver.exe、火狐的geckodriver.exe、IE的IEDriverServer.exe
3. 浏览器
浏览器当然就是我们很熟悉的常用的各种浏览器。
那在WebDriver脚本运行的时候,它们之间是如何通信的呢?为什么同一个browser driver即可以处理java语言的脚本,也可以处理python语言的脚本呢?让我们来看一下,一条Selenium脚本执行时后端都发生了哪些事情:
对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动
浏览器驱动中包含了一个HTTP Server,用来接收这些http请求
HTTP Server接收到请求后根据请求来具体操控对应的浏览器
浏览器执行具体的测试步骤
浏览器将步骤执行结果返回给HTTP Server
HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息。
为什么使用HTTP协议呢?
因为HTTP协议是一个浏览器和Web服务器之间通信的标准协议,而几乎每一种编程语言都提供了丰富的http libraries,这样就可以方便的处理客户端Client和服务器Server之间的请求request及响应response,WebDriver的结构中就是典型的C/S结构,WebDriver API相当于是客户端,而小小的浏览器驱动才是服务器端。
那为什么同一个浏览器驱动即可以处理Java语言的脚本,也可以处理Python语言的脚本呢?
这就要提到WebDriver基于的协议:JSON Wire protocol。
JSON Wire protocol是在http协议基础上,对http请求及响应的body部分的数据的进一步规范。
我们知道在HTTP请求及响应中常常包括以下几个部分:http请求方法、http请求及响应内容body、http响应状态码等。
常见的http请求方法:
GET:用来从服务器获取信息。比如获取网页的标题信息
POST:向服务器发送操作请求。比如findElement,Click等
http响应状态码:
在WebDriver中为了给用户以更明确的反馈信息,提供了更细化的http响应状态码,比如:
7: NoSuchElement
11:ElementNotVisible
200:Everything OK
现在到了最关键的http请求及响应的body部分了:
body部分主要传送具体的数据,在WebDriver中这些数据都是以JSON的形式存在并进行传送的,这就是JSON Wire protocol。
JSON是一种数据交换的格式,是对XML的升级与替代,下面是一个JSON文件的例子:
{
"firstname": "Alex",
"lastname": "Smith",
"moble": "13300000000"
}
下面的例子是WebDriver中在成功找到一个元素后JSON Wire Protocol的返回:
{"status" : 0, "value" : {"element" : "123422"}}
所以在Client和Server之间,只要是基于JSON Wire Protocol来传递数据,就与具体的脚本语言无关了,这样同一个浏览器的驱动就即可以处理Java语言的脚本,也可以处理Python语言的脚本了。