51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1672|回复: 0
打印 上一主题 下一主题

WebDriver 工作原理

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-1-25 15:26:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

WebDriver是W3C的一个标准,由Selenium主持。

具体的协议标准可以从http://code.google.com/p/seleniu ... l#Command_Reference   查看。

从这个协议中我们可以看到,WebDriver之所以能够实现与浏览器进行交互,是因为浏览器实现了这些协议。这个协议是使用JOSN通过HTTP进行传输。

它的实现使用了经典的Client-Server模式。客户端发送一个requset,服务器端返回一个response。


我们明确几个概念。

Client

调用 WebDriverAPI的机器。


Server

运行浏览器的机器。Firefox浏览器直接实现了WebDriver的通讯协议,而Chrome和IE则是通过ChromeDriver和InternetExplorerDriver实现的。


Session

服务器端需要维护浏览器的Session,从客户端发过来的请求头中包含了Session信息,服务器端将会执行对应的浏览器页面。


WebElement

这是WebDriverAPI中的对象,代表页面上的一个DOM元素。


举个实际的例子,下面代码的作用是”命令”firefox转跳到google主页:

  1. <p style="box-sizing: inherit; margin-top: 16px; margin-bottom: 14px; line-height: 28px;">       WebDriver driver = new FirefoxDriver();
  2.         //实例化一个Driver

  3.         driver.get("http://www.google.com");</p>
复制代码

在执行driver.get("http://www.google.com")这句代码时,client,也就是我们的测试代码向remote server发送了如下的请求:

  1. POSTsession/285b12e4-2b8a-4fe6-90e1-c35cba245956/url  post_data{"url":"http://google.com"}  
复制代码

通过post的方式请求localhost:port/hub/session/session_id/url地址,请求浏览器完成跳转url的操作。

如果上述请求是可接受的,或者说remote server是实现了这个接口,那么remote server会跳转到该post data包含的url,并返回如下的response

  1. {"name":"get","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":""}
复制代码

该response中包含如下信息

  • name:remote server端的实现的方法的名称,这里是get,表示跳转到指定url;
  • sessionId:当前session的id;
  • status:请求执行的状态码,非0表示未正确执行,这里是0,表示一切ok不许担心;
  • value:请求的返回值,这里返回值为空,如果client调用title接口,则该值应该是当前页面的title;

如果client发送的请求是定位某个特定的页面元素,则response的返回值可能是这样的:

  1. {"name":"findElement","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":{"ELEMENT":"{2192893e-f260-44c4-bdf6-7aad3c919739}"}}
复制代码

name,sessionId,status跟上面的例子是差不多的,区别是该请求的返回值是ELEMENT:{2192893e-f260-44c4-bdf6-7aad3c919739},表示定位到元素的id,通过该id,client可以发送如click之类的请求与 server端进行交互。


WebDriver与之前Selenium的JS注入实现不同,直接利用了浏览器native support来操作浏览器。所以对于不同平台,不同的浏览器,必须依赖一个特定的浏览器的native component来实现把WebDriver API的调用转化为浏览器的native invoke。


在我们new一个WebDriver的过程中,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给46350端口上的Web Service。在我们的HTTP request的body中,会以WebDriver Wire协议规定的JSON格式的字符串来告诉Selenium我们希望浏览器接下来做什么事情。


不同浏览器的WebDriver子类,都需要依赖特定的浏览器原生组件,例如Firefox就需要一个add-on名字叫webdriver.xpi。而IE的话就需要用到一个dll文件来转化Web Service的命令为浏览器native的调用。


关于WebDriver Wire协议的细节,比如希望了解这套Web Service能够做哪些事情,可以阅读Selenium官方的协议文档, 在Selenium的源码中,我们可以找到一个HttpCommandExecutor这个类,里面维护了一个Map, 它负责将一个个代表命令的简单字符串key,转化为相应的URL,因为REST的理念是将所有的操作视作一个个状态,每一个状态对应一个URI。所以当我 们以特定的URL发送HTTP request给这个RESTful web service之后,它就能解析出需要执行的操作。截取一段源码如下:

  1. nameToUrl = ImmutableMap.<String, CommandInfo>builder()  
  2.         .put(NEW_SESSION, post("/session"))  
  3.         .put(QUIT, delete("/session/:sessionId"))  
  4.         .put(GET_CURRENT_WINDOW_HANDLE, get("/session/:sessionId/window_handle"))  
  5.         .put(GET_WINDOW_HANDLES, get("/session/:sessionId/window_handles"))  
  6.         .put(GET, post("/session/:sessionId/url"))  

  7.          // The Alert API is still experimental and should not be used.  
  8.         .put(GET_ALERT, get("/session/:sessionId/alert"))  
  9.         .put(DISMISS_ALERT, post("/session/:sessionId/dismiss_alert"))  
  10.         .put(ACCEPT_ALERT, post("/session/:sessionId/accept_alert"))  
  11.         .put(GET_ALERT_TEXT, get("/session/:sessionId/alert_text"))  
  12.         .put(SET_ALERT_VALUE, post("/session/:sessionId/alert_text"))
复制代码

可以看到实际发送的URL都是相对路径,后缀多以/session/:sessionId开头,这也意味着WebDriver每次启动浏览器都会分 配一个独立的sessionId,多线程并行的时候彼此之间不会有冲突和干扰。例如我们最常用的一个WebDriver的 API,getWebElement在这里就会转化为/session/:sessionId/element这个URL,然后在发出的HTTP request body内再附上具体的参数比如by ID还是CSS还是Xpath,各自的值又是什么。收到并执行了这个操作之后,也会回复一个HTTP response。内容也是JSON,会返回找到的WebElement的各种细节,比如text、CSS selector、tag name、class name等等。


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-23 04:28 , Processed in 0.057951 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表