• 1
  • 0
分享
  • selenium ,webdriver 运行原理与机制
  • 曼倩诙谐 2022-08-15 11:23:34 字数 8019 阅读 1953 收藏 0

  最近在看一些底层的东西。driver翻译过来是驱动,司机的意思。如果将webdriver比做成司机,竟然非常恰当。

  我们可以把WebDriver驱动浏览器类比成出租车司机开出租车。在开出租车时有三个角色:

  · 乘客:他/她告诉出租车司机去哪里,大概怎么走。

  · 出租车司机:他按照乘客的要求来操控出租车。

  · 出租车:出租车按照司机的操控完成真正的行驶,把乘客送到目的地。

  在WebDriver中也有类似的三个角色:

  · 自动化测试代码:自动化测试代码发送请求给浏览器的驱动(比如火狐驱动、谷歌驱动)。

  · 浏览器的驱动:它来解析这些自动化测试的代码,解析后把它们发送给浏览器。

  · 浏览器:执行浏览器驱动发来的指令,并最终完成工程师想要的操作。

  所以在这个类比中:

  · 工程师写的自动化测试代码就相当于是乘客。

  · 浏览器的驱动就相当于是出租车司机。

  · 浏览器就相当于是出租车。

  下面再从技术上解释下WebDriver的工作原理:

  从技术上讲,也同样是上面的三个角色:

  · WebDriver API(基于Java、Python、C#等语言)。

  · 对于java语言来说,就是下载下来的selenium的Jar包,比如selenium-java-3.8.1.zip包,代表Selenium3.8.1的版本。

  · 浏览器的驱动(browser driver),每个浏览器都有自己的驱动,均以exe文件形式存在。比如谷歌的chromedriver.exe、火狐的geckodriver.exe、IE的IEDriverServer.exe浏览器。

  浏览器当然就是我们很熟悉的常用的各种浏览器。那在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相当于是客户端,而小小的浏览器驱动才是服务器端。

  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。

  Selenium 是将各个浏览器的API封装成" Selenium自己设计定义的协议,名字叫做The WebDriver Wire Protocol " 的webdriver API

  操作层面:

  1、测试人员编写UI自动化测试脚本(java,python等等),运行脚本后,程序会打开指定的webdriver浏览器。

  webdriver浏览器作为一个remote-server 接受脚本的命令,同时webservice会打开一个端口:http://localhost:9515 浏览器则会监听这个端口。

  2、webservice会将脚本语言翻译成json格式传递给浏览器执行操作命令。

  逻辑层面:

  1、测试人员执行测试脚本后,就创建了一个session, 通过http 请求向webservice发送了restfull的请求。

  2、webservice翻译restfull的请求为浏览器能懂的脚本,然后接受脚本执行结果。

  3、webservice将结果进行封装--json 给到客户端client/测试脚本 ,然后client就知道操作是否成功,同时测试也可以进行校验了。

  我们可以验证一下:

  下载好chromedriver,放到环境变量里,注意要和chrome浏览器版本对上,然后执行chromedriver

  可以看到,会启动一个server, 并开启端口9515:

  andersons-iMac:~ anderson$ chromedriver
  Starting ChromeDriver 2.39.562713 (dd642283e958a93ebf6891600db055f1f1b4f3b2) on port 9515
  Only local connections are allowed.
  GVA info: Successfully connected to the Intel plugin, offline Gen9

  强调了只允许本地连接。前面已经提过了,乘客向司机发一个请求,行为是构造一个http请求。构造的请求是这样子的:

  请求方式 :POST

  请求地址 :http://localhost:9515/session

  请求body :

  capabilities = {
      "capabilities": {
          "alwaysMatch": {
              "browserName": "chrome"
          },
          "firstMatch": [
              {}
          ]
      },
      "desiredCapabilities": {
          "platform": "ANY",
          "browserName": "chrome",
          "version": "",
          "chromeOptions": {
              "args": [],
              "extensions": []
          }
      }
  }
  我们可以尝试使用python requests 向 ChromeDriver发送请求
  import requests
  import json
  session_url = 'http://localhost:9515/session'
  session_pars = {"capabilities": {"firstMatch": [{}], \
                        "alwaysMatch": {"browserName": "chrome",\
                                        "platformName": "any", \
                                        "goog:chromeOptions": {"extensions": [], "args": []}}}, \
                  "desiredCapabilities": {"browserName": "chrome", \
                               "version": "", "platform": "ANY", "goog:chromeOptions": {"extensions": [], "args": []}}}
  r_session = requests.post(session_url,json=session_pars)
  print(json.dumps(r_session.json(),indent=2))
  结果:
  {
    "sessionId": "44fdb7b1b048a76c0f625545b0d2567b",
    "status": 0,
    "value": {
      "acceptInsecureCerts": false,
      "acceptSslCerts": false,
      "applicationCacheEnabled": false,
      "browserConnectionEnabled": false,
      "browserName": "chrome",
      "chrome": {
        "chromedriverVersion": "2.40.565386 (45a059dc425e08165f9a10324bd1380cc13ca363)",
        "userDataDir": "/var/folders/yd/dmwmz84x5rj354qkz9rwwzbc0000gn/T/.org.chromium.Chromium.RzlABs"
      },
      "cssSelectorsEnabled": true,
      "databaseEnabled": false,
      "handlesAlerts": true,
      "hasTouchScreen": false,
      "javascriptEnabled": true,
      "locationContextEnabled": true,
      "mobileEmulationEnabled": false,
      "nativeEvents": true,
      "networkConnectionEnabled": false,
      "pageLoadStrategy": "normal",
      "platform": "Mac OS X",
      "rotatable": false,
      "setWindowRect": true,
      "takesHeapSnapshot": true,
      "takesScreenshot": true,
      "unexpectedAlertBehaviour": "",
      "version": "71.0.3578.80",
      "webStorageEnabled": true
    }
  }

  如何打开一个网页,类似driver.get(url)

  那么构造的请求是:

  请求方式 :POST

  请求地址 :http://localhost:9515/session/:sessionId/url

  注意:上述地址中的 ":sessionId"

  要用启动浏览器的请求返回结果中的sessionId的值

  例如:我刚刚发送请求,启动浏览器,返回结果中"sessionId": "44fdb7b1b048a76c0f625545b0d2567b"  

  然后请求的URL地址

  请求地址:http://localhost:9515/session/b2801b5dc58b15e76d0d3295b04d295c/url

  请求body :{"url": "https://www.baidu.com", "sessionId": "44fdb7b1b048a76c0f625545b0d2567b"}

  即:

  import requests
  url = 'http://localhost:9515/session/44fdb7b1b048a76c0f625545b0d2567b/url'
  pars = {"url": "https://www.baidu.com", "sessionId": "44fdb7b1b048a76c0f625545b0d2567b"}
  r = requests.post(url,json=pars)
  print(r.json())

  如何定位元素,类似driver.finde_element_by_xx:

  请求方式 :POST

  请求地址 :http://localhost:9515/session/:sessionId/element

  注意:上述地址中的 ":sessionId"

  要用启动浏览器的请求返回结果中的sessionId的值。

  例如:我刚刚发送请求,启动浏览器,返回结果中"sessionId": "b2801b5dc58b15e76d0d3295b04d295c"  

  然后我构造 查找页面元素的请求地址

  请求地址:http://localhost:9515/session/b2801b5dc58b15e76d0d3295b04d295c/element

  请求body :{"using": "css selector", "value": ".postTitle a", "sessionId": "b2801b5dc58b15e76d0d3295b04d295c"}

  即:

  import requests
  url = 'http://localhost:9515/session/b2801b5dc58b15e76d0d3295b04d295c/element'
  pars = {"using": "css selector", "value": ".postTitle a", "sessionId": "b2801b5dc58b15e76d0d3295b04d295c"}
  r = requests.post(url,json=pars)
  print(r.json())

  如何操作元素:类似click()

  请求方式 :POST

  请求地址 :http://localhost:9515/session/:sessionId/element/:id/click

  注意:上述地址中的 ":sessionId"

  要用启动浏览器的请求返回结果中的sessionId的值

  :id 要用元素定位请求后返回ELEMENT的值

  例如:我刚刚发送请求,启动浏览器,返回结果中"sessionId": "b2801b5dc58b15e76d0d3295b04d295c"  

  元素定位,返回ELEMENT的值"0.11402119390850629-1"

  然后我构造 点击页面元素的请求地址

  请求地址:http://localhost:9515/session/b2801b5dc58b15e76d0d3295b04d295c/element/0.11402119390850629-1/click

  请求body :{"id": "0.11402119390850629-1", "sessionId": "b2801b5dc58b15e76d0d3295b04d295c"}

  即:

  import requests
  url = 'http://localhost:9515/session/b2801b5dc58b15e76d0d3295b04d295c/element/0.11402119390850629-1/click'
  pars ={"id": "0.5930642995574296-1", "sessionId": "b2801b5dc58b15e76d0d3295b04d295c"}
  r = requests.post(url,json=pars)
  print(r.json())

  从上面可以看出来,UI自动化,其实也可以写成API自动化。

  只是,只是

  好繁琐,没有封装好的wedriver指令好用,有点脱裤子放屁的感觉。

  我们来写段代码感觉一下:

  import requests
  import time
  capabilities = {
      "capabilities": {
          "alwaysMatch": {
              "browserName": "chrome"
          },
          "firstMatch": [
              {}
          ]
      },
      "desiredCapabilities": {
          "platform": "ANY",
          "browserName": "chrome",
          "version": "",
          "chromeOptions": {
              "args": [],
              "extensions": []
          }
      }
  }
  # 打开浏览器 http://127.0.0.1:9515/session
  res = requests.post('http://127.0.0.1:9515/session', json=capabilities).json()
  session_id = res['sessionId']
  # 打开百度
  requests.post('http://127.0.0.1:9515/session/%s/url' % session_id,
                json={"url": "http://www.baidu.com", "sessionId": session_id})
  time.sleep(3)
  # 关闭浏览器,删除session
  requests.delete('http://127.0.0.1:9515/session/%s' % session_id, json={"sessionId": session_id})

  其实搞懂真正的原理,也就是为了方便解决问题,在debug的时候,更方便的查看和解决问题。

  当然,如果在接口自动化里面也需要调用少量的UI自动化,可以考虑这种方式。



作者:谢小玲    

来源:http://www.51testing.com/html/36/n-4479336.html


  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 测试策略是除了测试用例之外的其他注意事项,和测试力度,以及一些关注点。当系统较庞大,功能较多时,除了各个模块自己的功能相关的具体测试设计,还需要测试组长制定一些整体的测试决策,测试框架、测试策略、测试计划。测试策略主要覆盖哪些方面呢?主要是测试范围和测试重点。测试范围是指定测试的广度,比如,通话,短信和联系人,测试时候需要覆盖这些模块。测试重点是指需要重点关注的功能点,比如,长时间通话,文本超长的短信,彩信,一个联系人有多个号码,联系人姓名是特殊字符,等等。制定测试策略特别像古代的军师出谋划策,以最少的成本完成最高的测试质量并尽可能覆盖全测试的各个模块,保证产品质量。运筹帷幄之中,决胜千里之外...
            5 5 7698
            分享
          •   据路透社报道,印度南部泰米尔纳德邦塔塔电子工厂因大火停产,该工厂为苹果 iPhone 制造组件。  塔塔电子是印度主要的 iPhone 代工厂之一,与富士康齐名。IT之家获悉,该公司表示没有人员伤亡,正在调查火灾原因,并采取必要措施保障工厂员工和其他人员的安全。  “我们现在不能进去,因为通道被毁了,”负责处理工业安全的高级地区官员 J. Saravanan 称,“需要一天时间才能冷却下来。”  消息人士表示,这场火灾与化学品有关。由于目前无法进入该设施,火灾造成的损失评估将不得不推迟进行。作者:沛霖(实习)原文链接:IT之家(ithome.com)
            0 0 366
            分享
          • Vim是Linux系统上最常用的文本编辑器,本文将介绍一些vim常用的命令。插入命令a 在光标后插入A 在光标所在行的行尾后插入i 在光标前插入I 在光标所在行的行首前插入o 在光标下插入新行O 在光标上插入新行gi 进入到上一次插入模式的位置<ESC> 退出插入模式定位命令:set number 设置显示行号:set nonumber 取消行号gg 到第一行G 到最后一行nG 跳到第n行:n 跳到第n行$ 移至行尾0 移至行首删除命令x 删除光标所在处的字符nx 删除光标所在处后n个字符dd 删除光标所在行ndd 删除光标在内的n行dG 删除光标所在行到文件末尾的内容D 删除光标...
            0 0 1227
            分享
          • 1、开始测试1年半左右,一直在做的是游戏测试。现在对于黑盒的游戏测试感觉到迷茫,对于做游戏的黑盒测试来说,技术成长性在哪里?哪些知识能更好的帮助自己的工作?是这样的,测试入门的门槛相对其他技术工种来说不高,但可以提升空间是很大的。黑盒也好,白盒也好,其实所描绘的是测试立场和角度,最早国外上世纪提出的 是基于开发者了解内部结构去寻找问题(谓之白盒),黑盒是使用者的角度,不需要了解内部结构去验收(谓之黑盒),如果以测试策略来描绘的话,这个就是PDCA,P前面的target,这个阶段的测试目标是什么如你所言,接触服务端测试接触到了lr的基础(压力测试及负载测试),服务端相关测试还有对应的window...
            1 2 1996
            分享
          •   PICT(Pairwise Independent Combinatorial Testing,成对独立组合测试)是微软开发的一款测试用例生成工具(生成配对测试用例的工具很多,感兴趣的可以参考http://www.pairwise.org/tools.asp的介绍),它可以生成测试用例和测试配置,其理论基础是成对测试技术(Pairwise Testing,之前有文章单独介绍过,在此不多介绍)。  一、PICT安装  通过官网链接?http://download.microsoft.com/download/f/5/5/f55484df-8494-48fa-8dbd-8c6f76cc014b...
            13 13 3054
            分享
      • 51testing软件测试圈微信