[Selenium] Webdriver原理

403 阅读4分钟

selenium webdriver驱动浏览器原理

Selenium webdriver 使用WebDriver来远程控制浏览器实例, 并模拟用户与浏览器的交互。

当我们按照入门教程,在本地环境中实现了使用selenium自动化控制浏览器,完成打开baidu首页-> 输入"自动化测试"->点击搜索得到结果的整个过程时,我们可以明确感知到三样东西:

  • selenium测试代码:Selenium测试代码是开发/测试人员使用不同的语言和相应的selenium API库完成的测试代码。
  • 浏览器driver:即我们在运行selenium代码前,下载的针对不同浏览器开发的chromedriver、geckodriver等。
  • 浏览器:自动化程序运行时,会打开代码中与浏览器Driver对应的浏览器,我们可以直观地看到代码的运行效果。

image.png

  1. 对于每一条Selenium脚本,一个http请求会被创建并发送给浏览器的驱动
  2. 浏览器driver包含了一个HTTP Server,用来接收这些http请求
  3. HTTP Server接收到请求后根据请求来具体操控对应的浏览器
  4. 浏览器执行具体的测试步骤
  5. 浏览器将步骤执行结果返回给HTTP Server
  6. HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息

也可以用生活中的例子来类比: image.png

  • 对于打的这个行为来说,乘客和出租车司机进行交互,告诉出租车想去的目的地,出租车司机驾驶汽车把乘客送到目的地,这样乘客就乘坐出租车到达了自己想去的地方。
  • Webdriver的实现原理是类似的,测试代码中包含了各种期望的对浏览器界面的操作,例如点击。测试代码通过给Webdriver发送指令,让Webdriver知道想要做的操作,而Webdriver根据这些操作在浏览器界面上进行控制,由此测试代码达到了在浏览器界面上操作的目的。

WebDriver协议

WebDriver是一个基于HTTP的协议,它提供了一系列的接口用于发现和控制 Web 文档中的 DOM 元素,几乎可以操作浏览器做任何事情。WebDriver协议的详细信息可以在W3C WebDriver 规范中查看。

以访问baidu首页为例,代码实现为driver.get('https://www.baidu.com/'),查看源码,可以在webdriver.py中看到get方法实际上是调用了self.execute(Command.GET, {'url': url}),而WebDriver类是继承自Remoteexecute方法的核心逻辑是response = self.command_executor.execute(driver_command, params),其中Command.GET在remote_connection.py中的定义为Command.GET: ('POST', '/session/$sessionId/url') 。

也就是说,Selenium测试代码运行时,会向浏览器driver发送对应的HTTP请求,浏览器driver在收到请求后对浏览器进行操作,再将结果返回给Selenium测试代码。

那么,如果我们先运行浏览器driver,再通过Postman向其发送HTTP请求,是不是也能控制浏览器呢?答案当然是肯定的。下面我们以在baidu搜索关键词的操作为例,实际操作一下。

Postman发送基于WebDriver协议的HTTP请求,实现UI自动化

首先,启动chromedriver,双击chromedriver文件打开即可:

image.png

启动成功之后,注意到控制台中输出了chromedriver启动和监听的的端口号为9515。

然后启动Postman,我们可以开始发送基于WebDriver协议的请求

  1. 建立连接:driver = webdriver.Chrome(executable_path='/path/to/chromedriver'), 对应的请求为POST http://localhost:9515/session
{
    "desiredCapabilities": {
        "caps": {
            "nativeEvents": false,
            "browserName": "chrome",
            "version": "",
            "platform": "ANY"
        }
    }
}

点击Send后,我们可以看到Chrome自动打开了,检查一下Response,返回了很多信息,其中sessionId是后续所有操作都需要使用的参数,可以将其保存到postman的环境变量中,方便使用。

  1. 进入baidu首页:driver.get('https://www.badiu.com/')

对应的请求为:POST http://localhost:9515/session/{{sessionId}}/url

{"url":"https://www.badiu.com/"}
  1. 查找输入框元素:search_input = driver.find_element(By.ID,'twotabsearchtextbox')

对应的请求为:POST http://localhost:9515/session/{{sessionId}}/element

{"using":"id","value":"twotabsearchtextbox"}

这个请求会返回查找到的元素的ID即ELEMENT,我们将其保存到环境变量searchInputId中。。

  1. 在输入框中输入搜索关键词:search_input.send_keys("自动化测试")

对应的请求为:POST http://localhost:9515/session/{{sessionId}}/element/{{searchInputId}}/value

{"value":["自动化测试"]}
  1. 查找搜索按钮:search_button = driver.find_elements(By.CLASS_NAME,'nav-input')[0]

对应的请求为:POST http://localhost:9515/session/{{sessionId}}/elements

{"using": "className","value": "nav-input"} 

这里会查找到2个元素,第一个是我们想要的搜索按钮,将搜索按钮元素ID保存到环境变量searchButtonId

  1. 点击搜索按钮:search_button.click()

对应的请求为: http://localhost:9515/session/{{sessionId}}/element/{{searchButtonId}}/click

  1. 获取浏览器当前窗口的标题,用做测试用例的断言:assertEqual(driver.title, '亚马逊 : 自动化测试')

获取标题的请求为:GET http://localhost:9515/session/{{sessionId}}/title

标题在返回的value中。

  1. 结束测试,断开连接:driver.close(),对应为DELETE http://localhost:9515/session/{{sessionId}}