源码解读:webdriver client的原理
前言
又到年底了,群里很多朋友说要开始备战2020金三银四,其实,我建议是,如果你不是技术大牛,就不要去凑热闹。
其实,现在(11,12月份)就是最佳换工作的时候,因为很多人想等着拿了年终再走,虽然招聘的公司不多,但是找工作的更少,所以,可以去试试有没有好机会。
很多人说要去面试自动化,问我有没有面试题,而好点的公司都喜欢问底层原理。
想必用过selenium的测试朋友都知道18种定位方式吧?

示例脚本准备
如果你去面试自动化,面试官估计会问你webdriver client的原理,因为这样才能看出你自动化的深度,这需要去研究下源码。
懵逼?别急,下面我就结合源码给大家简单介绍下(看主要源码即可,本来想画个调用流程图,但是着实精力有限)
我用的selenium版本是__version__ = '3.0.1'
写一个简单的样例
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.cnblogs.com/uncleyong/')
driver.close() # 关闭当前窗口,但是没结束进程
driver.quit()

运行示例,成功打开目标页面

源码分析
打开selenium的目录结构,webdriver下有各种客户端浏览器,比如chrome,firefox,ie等

那webdriver client的原理是什么呢?回到下面的示例,点击下图第2行Chrome

下面第20行可以看到,Chrome是一个别名

点击上图第20行WebDriver
下图可以看到,WebDriver类继承了RemoteWebDriver类,RemoteWebDriver类是as的别名,是另外一个py文件中WebDriver类的别名
下面这个WebDriver类是在seleniumwebdriver.chrome下的webdriver.py中
这个类的注释:Controls the ChromeDriver and allows you to drive the browser,含义是:控制ChromeDriver并允许驱动浏览器。

实例化WebDriver(也就是执行webdriver.Chrome())的时候,过程中会先启动chromedriver,然后调用父类RemoteWebDriver的__init方法

上图57行实例化Service类,点击这个类

点击上图35行free_port
下面是获取到一个可用的端口

执行下图62行self.service.start()后,启动了chromedriver

点击上图62行start
可用看到,subprocess调用命令行打开webdriver.exe

也就是selenium启动了chromedriver,可以看到对应的进程

我们把获取到的端口打印出来



要执行了self.service.start()后,启动了chromedriver,下面命令才有结果

我们来看下RemoteWebDriver这个父类,点击下图65行RemoteWebDriver

名称还是WebDriver,注意,只要不在同一个py文件,类名是可以相同的,但是如果其中一个要引用另外一个,另外一个要as取别名,如上图中from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
这个类的注释:Controls a browser by sending commands to a remote server,含义是:通过向远程服务器发送命令来控制浏览器。

来看其__init__方法,
下图58行__init__中参数command_executor的默认值是http://127.0.0.1:4444/wd/hubremote,
下图86行创建RemoteConnection类对象也需要参数command_executor,说明是连接remote server的URL,RemoteConnection类是负责与Remote WebDriver server连接的类

点击上图92行start_session,
可以看到,下图180行生成sessionId

点击上图179行execute

点击上图234行execute
command表示执行的命令

点击上图407行command_info

点击上图402行_commands,打开self._commands这个dict,下图199行就是我们前面的发送请求的命令Command.NEW_SESSION,发送一个路径为/session的post请求,生成sessionId,返回响应

回到下图,点击407行_request

在_request方法中,try下面第一行将打开目标浏览器,并绑定到指定端口,绑定完成后,该启动的浏览器实例就做为WebDriver的remote server
resp是一个返回的对象

后面执行resp.read(),获取到对象内的data数据,debug模式下,可用看到,data中包括已经生成的sessionId

另外,WebDriver类是在selenium.webdriver.remote下的webdriver.py文件中

回到我们的示例脚本

点击上方第3行get

上图248行url是我们传的参数,点击上图248行GET,通过下图可知Command是一个类,GET是类属性

点击上上图248行execute
我们直接看response后面的代码,self对象后面是一个名为command_executor的对象,这个对象执行了execute方法。

点击上图234行command_executor
下图86行可以看到,对象command_executor是RemoteConnection类的实例,
这个实例是在__init__方法中,说明是在创建selenium.webdriver.remote.webdriver.WebDriver类对象的时候也完成了command_executor对象的创建

点击上图86行RemoteConnection类
RemoteConnection类就是负责与Remote WebDriver server连接的类

再回到下图,来看RemoteConnection类的方法execute

点击上图234行execute
command表示执行的命令

点击上图407行command_info

点击上图402行_commands,打开self._commands这个dict,
下图206行就是我们前面的发送请求的命令Command.GET,通过sessionId确定请求的浏览器,也就是说sessionId表示了remote server和对应浏览器的一个会话,指令通过这个会话变成对浏览器的一个操作,
remote server在执行完对浏览器的操作后得到的数据将作为响应的body返回给测试代码。
另外,216行是常用的

手工模拟
为了更加清晰查看请求过程中的path、params、method、url、body值,我们先自己写一个mylogger文件
修改源文件remote_connection.py

点击上图411行_request,添加如下内容

运行示例,控制台打印出日志

完整版日志:
[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session ----- 【params】:{'desiredCapabilities': {'browserName': 'chrome', 'version': '', 'platform': 'ANY', 'javascriptEnabled': True, 'chromeOptions': {'extensions': [], 'args': []}}, 'requiredCapabilities': {}}
[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session +++++ 【body】:{"desiredCapabilities": {"browserName": "chrome", "version": "", "platform": "ANY", "javascriptEnabled": true, "chromeOptions": {"extensions": [], "args": []}}, "requiredCapabilities": {}}
[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session/80c0d4efaeea62ae67c11a9ae3c656fd/url ----- 【params】:{'url': 'https://www.cnblogs.com/uncleyong/', 'sessionId': '80c0d4efaeea62ae67c11a9ae3c656fd'}
[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session/80c0d4efaeea62ae67c11a9ae3c656fd/url +++++ 【body】:{"url": "https://www.cnblogs.com/uncleyong/", "sessionId": "80c0d4efaeea62ae67c11a9ae3c656fd"}
通过debug调试可知, 执行2-5行代码时,driver_command的值分别为:newSession、get、close、quit

上面日志中,
前两行是执行response = self.execute(Command.NEW_SESSION, capabilities)产生的日志
后两行是执行self.execute(Command.GET, {'url': url})产生的日志
还原上面第2-3行过程
手动执行chromedriver.exe,模拟selenium先启动了chromedriver(不要手动关闭)

模拟上面日志中第一个post请求,browserName是chrome,表示告诉remote driver打开chrome浏览器

打开了浏览器(不要手动关闭)

响应中,返回了sessionId,后面和浏览器的交互都是基于这个sessionId进行的


模拟上面日志第二个post请求,sessionId替换为上面返回的值:8832915b0bc0151aab6573ab33b4be93

浏览器展示了我们的目标url页面内容

响应status等于0,即表示命令执行成功

结论
看懂上面的分析,你应该知道结论了吧?
如果还是迷糊,那请关注公众号“全栈测试笔记”,回复“原理”获取结论。
源码解读:webdriver client的原理的更多相关文章
- Vue源码--解读vue响应式原理
原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...
- selenium之python源码解读-webdriver继承关系
一.webdriver继承关系 在selenium中,无论是常用的Firefox Driver 还是Chrome Driver和Ie Drive,他们都继承至selenium\webdriver\re ...
- React 源码解读参考,理解原理。
Rubix - ReactJS Powered Admin Template 文档: http://rubix-docs.sketchpixy.com/ ===================== ...
- zorka源码解读之Instrument实现原理
主要用到三方面技术: beanshell来实现可扩展:告诉zorkaAgent插桩的具体需求,包括插桩的方法和值.插桩的时机.插桩追踪记录方式等. Instrument来通过代理的方式访问JVM,实现 ...
- Webpack探索【16】--- 懒加载构建原理详解(模块如何被组建&如何加载)&源码解读
本文主要说明Webpack懒加载构建和加载的原理,对构建后的源码进行分析. 一 说明 本文以一个简单的示例,通过对构建好的bundle.js源码进行分析,说明Webpack懒加载构建原理. 本文使用的 ...
- Webpack探索【15】--- 基础构建原理详解(模块如何被组建&如何加载)&源码解读
本文主要说明Webpack模块构建和加载的原理,对构建后的源码进行分析. 一 说明 本文以一个简单的示例,通过对构建好的bundle.js源码进行分析,说明Webpack的基础构建原理. 本文使用的W ...
- 2,MapReduce原理及源码解读
MapReduce原理及源码解读 目录 MapReduce原理及源码解读 一.分片 灵魂拷问:为什么要分片? 1.1 对谁分片 1.2 长度是否为0 1.3 是否可以分片 1.4 分片的大小 1.5 ...
- Vue 源码解读(3)—— 响应式原理
前言 上一篇文章 Vue 源码解读(2)-- Vue 初始化过程 详细讲解了 Vue 的初始化过程,明白了 new Vue(options) 都做了什么,其中关于 数据响应式 的实现用一句话简单的带过 ...
- 第二十三课:jQuery.event.add的原理以及源码解读
本课主要来讲解一下jQuery是如何实现它的事件系统的. 我们先来看一个问题: 如果有一个表格有100个tr元素,每个都要绑定mouseover/mouseout事件,改成事件代理的方式,可以节省99 ...
随机推荐
- 剑指Offer-1.二维数组中的查找(C++/Java)
题目: 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...
- OpenCV Error: Unknown error code -10 (Raw image encoder error: Empty JPEG image (DNL not supported)) in throwOnEror 错误
出现上面这样的错误可以肯定是传了空指针导致的, 刚开始出现这样的问题, 并且是概率性的, 网上找了一遍都没找到解决方案, 然后自己一行一行代码注释, 发现还是会出现这样的问题, 当时就懵逼了, 我从打 ...
- ASP.NET Core 之 Identity 入门(转载)
原文地址:https://www.cnblogs.com/gongap/p/9504562.html 前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库 ...
- (三十四)golang--接口
golang的多态特性主要体现在接口上: 主要优势:高内聚低耦合: package main import ( "fmt" ) type usb interface { start ...
- JAVA 运行springboot jar包设置classpath
Java 命令行提供了如何扩展bootStrap 级别class的简单方法. -Xbootclasspath: 完全取代基本核心的Java class 搜索路径.不常用,否则要重新写所有Java 核心 ...
- Loj #2553. 「CTSC2018」暴力写挂
Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...
- python接口自动化4-常用取token值方法
前言 在接口测试中我们经常是需要一个登陆token,或者获取其他用到的参数来关联下一个接口用到的参数.这里介绍一些本人常用的方法. 一.简介 不过在哪里我们也是能实现自动化api测试的,我们都知道to ...
- oracle like模糊查询不能走索引?
这里要纠正一个网上很多教程说的模糊匹配不能走索引的说法,因为在看<收获,不止SQL优化>一书,里面举例说到了,并且自己也跟着例子实践了一下,确实like一些特殊情况也是可以走索引的 例子来 ...
- Linux RAID 磁盘管理
Linux RAID 磁盘管理 RAID工作模式介绍:https://www.cnblogs.com/xiangsikai/p/8441440.html 本章主要讲解 Linux下 RAID5 与 R ...
- 使用AvalonEdit实现WPF的Lua编辑器
原文发布于:https://www.chenxublog.com/2019/07/14/use-avalonedit-make-wpf-lua-editor.html 由于LLCOM里面内置了Lua代 ...