Selenium源码分析之WebDriver 分类: Selenium 2015-07-16 00:20 18人阅读 评论(0) 收藏

最近比较空闲就仔细看了一下Selenium的源码,由于主要是使用WebDriver因此重点关注了一下WebDriver的工做原理。在前一篇blog里已经解释过了WebDriver与以前Selenium的JS注入实现不一样,直接利用了浏览器native support来操做浏览器。因此对于不一样平台,不一样的浏览器,必须依赖一个特定的浏览器的native component来实现把WebDriver API的调用转化为浏览器的native invoke。java


在咱们new一个WebDriver的过程当中,Selenium首先会确认浏览器的native component是否存在可用并且版本匹配。接着就在目标浏览器里启动一整套Web Service,这套Web Service使用了Selenium本身设计定义的协议,名字叫作The WebDriver Wire Protocol。这套协议很是之强大,几乎能够操做浏览器作任何事情,包括打开、关闭、最大化、最小化、元素定位、元素点击、上传文件等等等等。web


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咱们但愿浏览器接下来作社么事情。浏览器


这里笔者初步画了一个图来表示各类WebDriver的工做原理:session


从上图中咱们能够看出,不一样浏览器的WebDriver子类,都须要依赖特定的浏览器原生组件,例如Firefox就须要一个add-on名字叫webdriver.xpi。而IE的话就须要用到一个dll文件来转化Web Service的命令为浏览器native的调用。另外,图中还标明了WebDriver Wire协议是一套基于RESTful的web service。若是不明白什么是RESTful的,能够参见笔者以前另一篇介绍REST的blog(http://blog.csdn.net/ant_yan/article/details/7963517)多线程


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

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

            // The Alert API is still experimental and should not be used.
        .put(GET_ALERT, get("/session/:sessionId/alert"))
        .put(DISMISS_ALERT, post("/session/:sessionId/dismiss_alert"))
        .put(ACCEPT_ALERT, post("/session/:sessionId/accept_alert"))
        .put(GET_ALERT_TEXT, get("/session/:sessionId/alert_text"))
        .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等等。如下是解析咱们说的HTTP response的代码片断:

try {
        response = new JsonToBeanConverter().convert(Response.class, responseAsText);
      } catch (ClassCastException e) {
        if (responseAsText != null && "".equals(responseAsText)) {
          // The remote server has died, but has already set some headers.
          // Normally this occurs when the final window of the firefox driver
          // is closed on OS X. Return null, as the return value _should_ be
          // being ignored. This is not an elegant solution.
          return null;
        }
        throw new WebDriverException("Cannot convert text to response: " + responseAsText, e);
      } //...



相信总结道这里,应该对WebDriver的运行原理应该清楚了!其实挺佩服这一套RESTful web service的设计。感受封装WebDriver暴露出来的public API还能够更加友好跟强大一点。



参考文章:http://blog.csdn.net/ant_ren/article/details/7970793ui

相关文章
相关标签/搜索