WDA原理分析

WDA原理分析1、什么是WDAWebDriverAgent是Facebook在17年的SeleniumConf大会上推出了一款新的iOS移动测试框架。下面摘录一段官方对于WebDriverAgent的介绍字段:(官方文档:https://github.com/facebook/WebDriverAgent)WebDriverAgent在iOS端实现…

大家好,又见面了,我是你们的朋友全栈君。

1、什么是WDA

WebDriverAgent是Facebook 在17年的 SeleniumConf 大会上推出了一款新的iOS移动测试框架。

下面摘录一段官方对于WebDriverAgent的介绍字段:(官方文档:https://github.com/facebook/WebDriverAgent

WebDriverAgent 在 iOS 端实现了一个 WebDriver server ,借助这个 server 我们可以远程控制 iOS 设备。你可以启动、杀死应用,点击、滚动视图,或者确定页面展示是否正确。This makes it a perfect tool for application end-to-end testing or general purpose device automation.(它说它是iOS上一个完美的e2e的自动化解决方案) It works by linking XCTest.framework and calling Apple’s API to execute commands directly on a device.(链接XCTest.framework调用苹果的API直接在设备上执行命令) WebDriverAgent is developed and used at Facebook for end-to-end testing and is successfully adopted by Appium. (Appium封装工作正在进行中,如果一旦封装好,那么以后就可以直接用Appium提供的binding了。)It is currently maintained by Marek Cirkos and Mehdi Mulani。

image2018-10-12%2017%3A1%3A25.png?versio

2、简单原理图

WebDriver之所以能够实现与浏览器进行交互,是因为浏览器实现了Mobile JSON Wire Protocol Specification协议,这个协议是使用JOSN通过HTTP进行传输。

它的实现使用了经典的Server-Client架构(C/S),客户端发送一个requset,服务器端返回一个response。

image2018-10-12%2017%3A50%3A50.png?versi
在开始下面的内部实现细节的讲解前,我们下明确几个概念:

1、WDAClient

WDAClient是基于WebDriverAgent实现的WDA的客户端。facebook-wda 就是 WDA 的 Python 客户端库,通过直接构造HTTP请求直接跟WebDriverAgent通信。

2、WDAServer

运行WDA App的机器,实现了WebDriver的通讯协议

3、Session

服务器端需要维护客户端的Session,客户端首次发送请求的字符串是’/session/sessionId/urlurlurlsessionId/url′。服务器端根据url打开对应的url地址,同时将sessionId解析成真实的值。然后返回给客户端。以后客户端再向浏览器发送请求时,将会携带session值一起发送。

1
2
3
4
5
6
7
8
[debug] [BaseDriver] Creating session with W3C capabilities: {

"alwaysMatch"
:{"platformNa...
[BaseDriver] Session created with session 
id
: 
7a1d8eca
-
8c48
-
4a94
-
8256
-
ab283e2af4c3
 
[Appium] New AndroidUiautomator2Driver session created successfully, session 
7a1d8eca
-
8c48
-
4a94
-
8256
-
ab283e2af4c3 added to master session 
list
[debug] [BaseDriver] Event 
'newSessionStarted'
 
logged at 
1540198593998
 
(
16
:
56
:
33
 
GMT
+
0800
 
(中国标准时间))
[debug] [W3C] Cached the protocol value 
'W3C'
 
for
 
the new session 
7a1d8eca
-
8c48
-
4a94
-
8256
-
ab283e2af4c3
[debug] [W3C] Responding to client with driver.createSession() result: {

"capabilities"
:{

"platform"
:
"LINUX"
,
"webStorageEnabled"
:false,
"takesScreenshot"
:true,
"javascriptEnabled"
:true,
"databaseEnabled"
:false,
"networkConnectionEnabled"
:true,
"locationContextEnabled"
:false,
"warnings"
:{},
"desired"
:{

"platformName"
:
"Android"
,
"unicodeKeyboard"
:true,
"command_executor"
:
"http://127.0.0.1:4723/wd/hub"
,
"noReset"
:true,
"appActivity"
:
"com.didi.sdk.app.launch.DidiLoadDexActivity"
,
"automationName"
:
"uiautomator2"
,
"newCommandTimeout"
:
300
,
"deviceName"
:
"68de2f65"
,
"recreateChromeDriverSessions"
:
"true"
,
"platformVersion"
:
"7.0"
,
"appPackage"
:
"com.sdu.didi.psnger"
},
"platformName"
:
"Android"
,
"unicodeKeyboard"
:true,
"command_executor"
:
"http://127.0.0.1:4723/wd/hub"
,
"noReset"
:true,
"appActivity"
:
"com.didi.sdk.app.launch.DidiLoadDexActivity"
,
"automationName"
:
"uiautomator2"
,
"newCommandTimeout"
:
300
,
"deviceName"
:
"68de2f65"
,
"recreateChromeDriverSessions"
:
"true"
,
"platformVersion"
:
"7.0"
,
"appPackage"
:
"com.sdu.didi.psnger"
,
"deviceUDID"
:
"68de2f65"
,
"deviceScreenSize"
:
"1440x2560"
,
"deviceScreenDensity"
:
640
,
"deviceModel"
:"SM...
[HTTP] <
-
-
 
POST 
/
wd
/
hub
/
session 
200
 
27897
 
ms 
-
 
1238

1
2
3
[debug] [JSONWP Proxy] Matched 
'/session'
 
to command name 
'createSession'
[debug] [JSONWP Proxy] Proxying [POST 
/
session] to [POST http:
/
/
localhost:
8200
/
wd
/
hub
/
session] with body: {

"desiredCapabilities"
:{

"platform"
:
"LINUX"
,
"webStorageEnabled"
:false,
"takesScreenshot"
:true,
"javascriptEnabled"
:true,
"databaseEnabled"
:false,
"networkConnectionEnabled"
:true,
"locationContextEnabled"
:false,
"warnings"
:{},
"desired"
:{

"platformName"
:
"Android"
,
"unicodeKeyboard"
:true,
"command_executor"
:
"http://127.0.0.1:4723/wd/hub"
,
"noReset"
:true,
"appActivity"
:
"com.didi.sdk.app.launch.DidiLoadDexActivity"
,
"automationName"
:
"uiautomator2"
,
"newCommandTimeout"
:
300
,
"deviceName"
:
"68de2f65"
,
"recreateChromeDriverSessions"
:
"true"
,
"platformVersion"
:
"7.0"
,
"appPackage"
:
"com.sdu.didi.psnger"
},
"platformName"
:
"Android"
,
"unicodeKeyboard"
:true,
"command_executor"
:
"http://127.0.0.1:4723/wd/hub"
,
"noReset"
:true,
"appActivity"
:
"com.didi.sdk.app.launch.DidiLoadDexActivity"
,
"automationName"
:
"uiautomator2"
,
"newCommandTimeout"
:
300
,
"deviceName"
:
"68de2f65"
,
"recreateChromeDriverSessions"
:
"true"
,
"platformVersion"
:
"7.0"
,
"appPackage"
:
"com.sdu.didi.psnger"
,
"deviceUDID"
:
"68de2f65"
,
"deviceScreenSize"
:
"1440x2560"
,
"deviceScreenDensity"
:
640
,"deviceMod...
[debug] [JSONWP Proxy] Got response with status 
200
: {

"sessionId"
:
"eef20bb5-f3ed-4cbc-9977-b32f8be4eea9"
,
"status"
:
0
,
"value"
:
"Created Session"
}

4、WebElement

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

5、JsonWireProtocol

JsonWireProtocol(以下简称JWP)是通过使用webdriver与remote server进行通信的
web service 协议
。通过http请求,完成和remote server的交互。

6、Mobile JSON Wire Protocol Specification

移动端自动化协议

7、iOS Accessibility

3、执行流程

1.  启动webdriveragent

image2018-10-12%2018%3A26%3A11.png?versi

2.  启动App

向WebdriverAgent发送post请求 ,请求参考WDA项目中 FBSessionCommands.m

请求地址:url=http://#{ip}:8100/session,WevDriverAgent会响应启动app,并返回session数据;

3. 启动app后,定位元素以及操作元素

定位元素 post请求:url+/session/element, 请求参数是定位元素标签以及值 参考 FBFindElementCommands.m;响应会返回elementId

操作*元素post请求:url+/session/element/id/* 参考项目中文件:/Commands/FBElementCommands.m 里面介绍了很多元素操作的方法 进行相应的转换即可。
image2018-10-12%2018%3A32%3A49.png?versi

4、测试代码与Webdriver的交互

接下来我会以获取界面元素这个基本的操作为例来分析两者之间的关系。
在测试代码中,我们第一步要做的是新建一个webdriver类的对象:

image2018-10-12%2018%3A36%3A22.png?versi

这里新建的driver对象是一个webdriver.Remote()类的对象,而webdriver.Remote()类的本质是

image2018-10-12%2018%3A42%3A42.png?versi

也就是一个来自Remote的WebDriver类。这个.remote.webdriver是继承了selenium.webdriver.remote.command

image2018-10-12%2018%3A45%3A20.png?versi

以python为例,在selenium库中,通过ID获取界面元素的方法是这样的:

image2018-10-12%2019%3A0%3A4.png?version

DATest对其进行二次封装后是这样的:

image2018-10-12%2019%3A0%3A39.png?versio

find_elements_by_id是selenium.webdriver.remote.webdriver.WebDriver类的实例方法。在代码中,我们直接使用的其实不是selenium.webdriver.remote.webdriver.WebDriver这个类,而是针对各个浏览器的webdriver类,例如webdriver.Chrome()、webdriver.Remote()。
所以说在测试代码中执行各种浏览器操作的方法其实都是selenium.webdriver.remote.webdriver.WebDriver类的实例方法。


接下来我们再深入selenium.webdriver.remote.webdriver.WebDriver类来看看具体是如何实现例如find_element_by_id()的实例方法的。

通过Source code可以看到:

image2018-10-15%2017%3A56%3A57.png?versi

这个方法最后call了一个execute方法,方法的定义如下:
image2018-10-15%2010%3A33%3A41.png?versi

如注释中提到的,其中的关键在于一个名为command_executor的对象执行了execute方法。

response = self.command_executor.execute(driver_command, params)

名为command_executor的对象是RemoteConnection类的对象,并且这个对象是在新建selenium.webdriver.remote.webdriver.WebDriver类对象的时候就完成赋值的

self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive)



结合selenium.webdriver.remote.webdriver.WebDriver类的类注释来看:
image2018-10-15%2017%3A5%3A11.png?versio

WebDriver类的功能是通过给一个remote server发送指令来控制浏览器。而这个remote server是一个运行WebDriver wire protocol的server。而RemoteConnection类就是负责与Remote WebDriver server的连接的类。

可以注意到有这么一个新建WebDriver类的对象时候的参数command_executor,默认值=’http://127.0.0.1:4444/wd/hub‘。这个值表示的是访问remote server的URL。因此这个值作为了RemoteConnection类的构造方法的参数,因为要连接remote server,URL是必须的。

现在再来看RemoteConnection类的实例方法execute。

image2018-10-15%2017%3A7%3A21.png?versio

这个方法有两个参数:

  • command
  • params

command表示期望执行的指令的名字。通过观察self._commands这个dict可以看到,self._commands存储了selenium.webdriver.remote.command.Command类里的常量指令和WebDriver wire protocol中定义的指令的对应关系。


image2018-10-15%2017%3A8%3A39.png?versio

以FIND_ELEMENT为例可以看到,指令的URL部分包含了几个组成部分:

  • HTTP请求方法。WebDriver wire protocol中定义的指令是符合RESTful规范的,通过不同请求方法对应不同的指令操作。

  • sessionId。Session的概念是这么定义的:

    The server should maintain one browser per session. Commands sent to a session will be directed to the corresponding browser.

    也就是说sessionId表示了remote server和浏览器的一个会话,指令通过这个会话变成对于浏览器的一个操作。

  • element。这一部分用来表示具体的指令。

selenium.webdriver.remote.command.Command类里的常量指令又在各个具体的类似find_elements的实例方法中作为execute方法的参数来使用,这样就实现了selenium.webdriver.remote.webdriver.WebDriver类中实现各种操作的实例方法与WebDriver wire protocol中定义的指令的一一对应。
selenium.webdriver.rmote.webelement.WebElement中各种在WebElement上的操作也是用类似的原理实现的。

实例方法execute的另一个参数params则是用来保存指令的参数的,这个参数将转化为JSON格式,作为HTTP请求的body发送到remote server。
remote server在执行完对浏览器的操作后得到的数据将作为HTTP Response的body返回给测试代码,测试代码经过解析处理后得到想要的数据。



image2018-10-23%2011%3A7%3A17.png?versio


通过对python selenium库的分析,希望能够帮助大家对selenium和webdriver的实现原理有更进一步的了解,在日常的自动化脚本开发中更加快捷的定位问题和解决问题。


附录:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/158136.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月12日 上午9:00
下一篇 2022年7月12日 上午9:00


相关推荐

  • less、scss/sass的区别

    less、scss/sass的区别less scss sass 的区别一 less scss sassscss sass 是动态样式语言 比 css 多出很多功能 如变量 嵌套 运算 混入 Mixin 继承 颜色处理 函数等 更方便阅读和维护 less 也是动态样式语言 一样也比 css 多处很多功能 如变量 继承 运算 函数 Less 既可以在客户端上运行 也可在服务端运行 Node js scss 和 sass 的关系 Sass 是缩排语法 对于习惯 css 的 web 开发者来说很不直观 还是有点学习成本 也不能将 css 代码加入到 sass

    2026年3月17日
    2
  • 共轭函数_复共轭函数

    共轭函数_复共轭函数定义设函数,定义函数为此函数称为函数f的共轭函数,使上述上确界有限,即差值在domf有上界的所有构成了共轭函数的定义域,下图描述了此定义(图中y即为公式中的t)。xy相当于是以y为斜率且过原

    2022年8月3日
    8
  • matlab最炫名族风,matlab版 “最炫民族风” — 跟风之作(附音频效果)(转)[通俗易懂]

    matlab最炫名族风,matlab版 “最炫民族风” — 跟风之作(附音频效果)(转)[通俗易懂]该楼层疑似违规已被系统折叠隐藏此楼查看此楼晚上看到个帖,matlab演奏卡农,眼前一亮,以前从没想过用matlab出声,作者真乃大牛。看到一条评论:卡农算什么,有本事来最炫民族风。。。。于是我就手贱了。。。花了几个小时。。。1/3在读懂卡农的程序,1/3在学习简谱知识(还好小学初中学过点),1/3在把简谱转换成代码。。。不多说,程序如下,欢迎试听哈:%%%%%%%%%%%%%%%%%%%%…

    2026年2月12日
    6
  • 登录注册页面跳转_登录注册界面

    登录注册页面跳转_登录注册界面用HTML、jQuery和css写一个简单的登录注册页面看了一些前端部分的视频,有点手痒,想起大学时做的某管理系统的前端部分,当时基本都是靠着CV写的,现在想想应该可以自己写一点了。话不多说,先上图:首先是登录页面:点击注册按钮可以跳转到注册页面:注册页面做了一点简单的判断:伪非空验证:还有伪密码验证:红字提示存在两秒,两秒后消失,清除密码框内的内容,但是不清除用户名框内的文本。然后当用户名和密码输入正确以后(其实两次密码一样就行,用户名不空就好)就可以跳转到登录页面。这里有一个坑

    2025年7月3日
    8
  • zookeeper入门(1)「建议收藏」

    zookeeper入门(1)「建议收藏」zookeeper应用场景zookeeper特点zookeeper数据模型Ubuntu配置zookeeper是一个典型的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能,高可用,且具有严格顺序访问控制能力的分布式协调存储服务应用场景维护配置信息分布式锁服务集群管理生成分布式唯一ID维护配置信息如java编程经常遇到配置项,比如数据路连接的url,password等等。通常这些配置文件需要放在服务器上,但需要更改配置文件的时候需要去服务器上更改。但是随着分布式系统的兴起,由于

    2022年8月8日
    5
  • volatile关键字及其作用

    volatile关键字及其作用概述:本文主要介绍Java语言中的volatile关键字,内容涵盖volatile的保证内存可见性、禁止指令重排等。

    2022年5月31日
    35

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号