appium工作原理
一.Appium框架组成
Appium框架组成是:
自动化脚本即client端(通过Java编写的代码,也可以通过其他语言编写),
Appium指令服务器,
sdk通信环境(Android模拟器)或移动端设备(这里是测试Windows平台的设备,)。
APPium是一个C/S架构的自动化测试框架,APPium的核心其实是一个暴露了一系列REST API的server。(也就是Appium的核心是一个提供了一组REST API的Web服务器。)
这个server的功能其实很简单:监听一个端口,然后接收由client(客户端)发送来的command(命令)。翻译这些command,把这些command转成移动设备可以理解的形式发送给移动设备,然后移动设备执行完这些command后,把执行结果返回给appium server,appium server再把执行结果返回给client。
在这里client其实就是发起command的设备,一般来说就是我们代码执行的机器,执行appium测试代码的机器。狭义点理解,可以把client理解成是代码,这些代码可以是java/ruby/python/js等编写的,只要它实现了webdriver标准协议就可以。
这样的设计思想带来了一些好处:
- 可以带来多语言的支持;
- 可以把server放在任意机器上,哪怕是云服务器都可以;(是的,appium和webdriver天生适合云测试)
二.Appium通信原理:
Appium通信原理:Client端发送自动化指令给Appium server,Appium Server接收到client发送的指令后,转换为移动端能够识别的指令,然后发送给移动端设备,并对移动端设备进行操作。
2.1 Client端:
一般来说就是运行代码的机器,即我们是用Java语言编写的代码,也可以用其他Selenium支持Python,ruby,C#等语言来编写,Appium提供的Appium-client API是Appium通过扩展Selenium的Webdriver协议而来的,我们编写代码的时只要实现Webdriver标准协议即可。
2.2 Appium Server:
Appium Server功能是监听接口,接收client端发送的command,然后将command转为移动端能够识别的command,然后发送给移动设备进行操作,再等待移动设备返回来的操作结果,将操作结果发送给client端。 Appium server是可以放在client端,也可以放在云端。 Appium server 默认的端口号是4723,用于Appium server监听client端的发送来的请求。
2.3 Android设备
Android端,Appium基于Webdriver协议,利用Bootstrap.jar,最后通过调用UIautomatior命令,实现APP的自动化测试(Android4.2以前的版本是用Instrumentation框架,通过绑定另外一个独立的selendroid项目来实现),Android4.2以后的版本是用UIautomator)。UIAutomator测试框架是Android SDK自带的APP UI自动化测试Java库,另外UIAutomator对H5支持有限,Appium引入了Chromedriver以及Safaridriver来实现H5的自动化。
在Android设备的工作过程:
-
1.Appium server将监听到的4723端口的指令,转发给中间件Bootstrap.jar,Bootstrap.jar是用Java编写的,安装在Android手机上;
-
2.Bootstrap监听4724端口并接收Appium server的指令;
-
3.Bootstrap再通过调用UIautomator的命令来实现具体的command操作。
-
4.最后Bootstrap将执行的结果返回给Appium server。
工作原理
appium工作原理(安卓为例)
首先需要了解,appium不同的版本默认使用的不同的执行引擎
目前最新的版本是UIAutomator2(谷歌官方sdk提供的自动化工具)
appium就是将UIAutomator底层工具集成进来,通过一系列手段完成自动化测试
appium1.13之前的版本的工作原理
appium1.13之后的版本的工作原理
主要区别
Appium1.13版本之前
主要区别:
1.端口映射有差异(4724端口作为appium在电脑上占用的端口,主要为了和手机做端口映射
4724是手机设备上启动uiautomator1占用的端口)
2.执行引擎不同,adb命令在手机设备上安装
appiumbootstrap.jar---->中间件
【Bootstrap是Appium在初始化的时候推送到安卓手机上的一个UiAutomator测试脚本,脚本作用:在手机端开启一个SocketServer(通讯模块),用来监听Appium服务发过来的命令发送给UiAutomator来执行处理】
(adb shell 进入终端下/data/local/tmp路径存在有该jar,appium执行后生成的),用以操作待测app;
3.启动appiumbootstrap.jar,在设备上创建socket服务
appium-server 和手机设备建立通信;
4.appiumbootstrap监听到4724端口由appium发送过来的相关请求,将请求转换成UiAutomator1能够识别的命令发给UiAutomator1进行处理;
除上述之外,原理基本和Appium1.13以后的版本一致;
工作原理详细说明
1,启动appium服务,
其实是appium启动了一个http接口,默认端口号是4723
2,脚本向appium发送以下请求
//POST /wd/hub/session ,参数如下(举例)
//包含安装包以及手机设备信息等
{
"desiredCapabilities": {
"unicodeKeyboard": true,
"deviceName": "D8YDU16325002121",
"newCommandTimeout": "120",
"noReset": false,
"resetKeyboard": true,
"appPackage": "com.boyaa.lordland.sina",
"platformName": "Android",
"appActivity": "com.boyaa.lordland.sina.Game"
}
}
3,appium server 接收到脚本请求后,如何与设备建立通信
再此过程中,,appium-server充当客户端,netty server充当服务端
详细流程如下:
appium启动脚本开始执行后,首先会进行一系列初始化操作(这里在启动appium示打出log,可以进一步分析),如上图示,主要如下行为:
-
使用adb命令安装辅助工具appium-setting到手机设备上(appium本地安装包路径下有appium-setting的安装包,npm_modules/node_modules/appium/node_modules/io.appium.setting/apks)
-
使用adb命令安装辅助工具uiautomator2.server和uiautomator2.server.test到手机设备上(ppium本地安装包路径下有uiautomator2.server和uiautomator2.server.test的安装包,ode_modules/appium/node_modules/appium-uiautomator2.server/apks)
作用解释:- uiautomator2.server一个是驱动模块,负责创建会话session
- appium调用Android Driver 为客户端生成一个session 仅在第一次请求时,且有对应的sessionid(代表打开的设备),且将sessionid返回给客户端并保存,再次访问携带作为唯一标识,关闭Driver(driver.quit()) 示session生命周期结束
uiautomator2.server.test是服务器模块,当驱动模块初始化完成,监听appium服务发送的请求,将请求发送给真正底层的UiAutomator2
-
第二步完成,检测到uiautomator2.server.test服务可用
端口映射,forwarding System:8200 to devices 6790,即当电脑上的8200端口收到请求会转发到手机的6790端口
解释:- 8200是appium在电脑上占用的端口,主要为了和手机做端口映射
- 6790是手机设备上启动uiautomator2占用的端口
-
启动手机上的uiautomator2.server.test,会创建一个netty server(对socket进一步封装,比socket高级的通讯框架),该netty server端口号就是6790,用来操作待测app
解释:- 初始化完成 至此,AppiumDriver 启动成功,会话创建完成,打开app;
- 日志如示:[HTTP]<-- POST /wd/hub/session 500 65473 ms - 1421
[服务器会生成唯一session,随后调用相关方法生成sessionId,用来标识session对象。同时会把sessionId返回给客户端(脚本)保存,之后每次请求都会携带sessionId]
注:可以在appium执行完后控制台log如下:
[Appium] New AndroidDriver session created successfully, session a942b672-b86d-44a4-bd46-14a6cd516f11 added to master session list
[debug] [BaseDriver] Event ‘newSessionStarted’ logged at 1515135795839 (15:03:15 GMT+0800 (中国标准时间))
4,执行测试脚本如以下:
例如,脚本中查找元素,完成点击操作:
driver.find_element_by_id(‘com.android.calculator2:id/digit1’).click()
控制台可以看到 ,脚本向服务发送请求
(1)POST /wd/hub/session/{sessionId}/element {"using":"id","sessionId":"xxxxxxxxxx","value":"com.android.calculator2:id/digit1"}发送了post请求,调用的是AppiumDriver.findElement方法,返回找到该元素的id,比如:{"ELEMENT":"1"};
(2) 上面查找到的请求返回的id ,会作为参数进行传递,脚本再次想服务器发送请求
POST /wd/hub/session/{sessionId}/element/1/click ,这里的1就是上个步骤返回的该元素的id;
(3)在手机端的点击成功后,返回{“value”:true, “status”:0},最终返回 到客户端就是true,至此完成整个流程;
三、appium的整体架构是C/S模式,整体流程(返回顺序为逆向):
脚本请求 ——> 4723端口appium server ——> 解析参数给PC端4724端口 ——> 发送给设备4724端口 ——> 通过设备4724端口发给bootstrap.jar ——> Bootstrap.jar把命令发给uiautomator
appium工作流程
1、脚本请求 ——> 4723端口appium server :
首先我们要开启appium服务,即Appium server,默认监听4723端口。4723端口专门和脚本打交道,基于WebDriver协议。webdriver是按照server – client的经典设计模式设计的,作用就是启动基于WebDriver Wire协议的appium服务,接下来脚本与appium server的通信实际上是一个HTTP request请求给appium server,在请求的body中,会以WebDriver Wire协议规定的JSON格式的字符串来告诉appium服务我们希望设备接下来做什么事情。
appium中的Json wire protocol继承自selenium的webdriver wire protocol,并进行了扩展,使得Json wire protocol能够控制不同的移动设备的行为。
desired Capabilities
上面说到的是脚本请求对设备进行操作,但前提是,我们要对谁进行操作测试呢?这里就引入一个新名词:desired Capabilities。了解了上述之后,再去看脚本怎么将desired Capabilities传递给appium server就明白多了,脚本通过Json Wire Protocol协议以json格式发送测试设备信息给server端,测试设备信息被携带在Desired Capabilities中,这个东西实质上是一个key-value形式的对象,Desired Capabilities最重要的作用是告诉server本次测试的上下文。
- 这次是要进行浏览器测试还是移动端测试?
- 如果是移动端测试的话是android还是ios?
- 如果android的话我们要测试哪个app?
server的这些疑问Desired Capabilities都必须给予解答,否则server不买账,针对我们现在所说的安卓,它带来的影响就是无法完成app的启动。
session
那么,将测试设备信息告知之后,是不是就可以开始进行测试了呢?答案是:NO。这里又要引入一个名词:session。session就是一个会话,在webdriver/appium,你的所有工作永远都是在session start后才可以进行的。client 创建1个session,在该session中通过http向appium server发送请求,appium server解析请求,完成相应操作并返回response。
Session在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息,对应到这里其实就是desired Capabilities中的配置信息参数。
脚本通过POST /session这个URL,然后传入Desired Capabilities就可以开启session了,由于这是第一次请求创建session,所有并没有一个已创建的session id,所以appium server会调用android driver(appium升级到2.0.0后,原有的AppiumDriver函数变成抽象函数了,需更改为AndroidDriver)为client生成一个session并且生成一个与此session相关联的session id,这个 session id将被在本次响应中返回给客户端保存,当下次脚本发出操作请求时就会自带session id为唯一标识,代表所打开的设备,Appium server会按照此session id把这个session检索出来使用,脚本向appium server发送的请求即是存在于创建的session中的。
Session 的作用就是它在appium服务上保持设备的状态信息,供在任何时间进行访问,在多次的操作行为中,存储在 Session对象中的配置信息将不会丢失,而是在整个用户会话中一直存在下去,整个测试进程中设备与程序的联系不会断开,也不需要每次都发送带配置信息的请求,程序都知道对哪个设备进行测试操作。当测试结束后,需关闭webdriver,driver.quit()会关闭所有关联窗口和session,并且也会把进程也关闭。
2、解析参数给PC端4724端口 ——> 发送给设备4724端口 ——> 通过设备4724端口发给bootstrap.jar ——> Bootstrap.jar把命令发给uiautomator:
创建session成功之前,就已将bootstrap.jar放入手机中,并开启设备上的基于appium bootstrap的socket服务,绑定本机和boostrap通信的端口号4724用于和Android设备通讯,默认监听4724端口,等待client的连接。
Appium server将脚本的请求解析后给到4724端口,通过设备的4724端口转发解析后的请求, 此时,对于socket服务来说,appium server就充当了client的角色,appium server通过4724端口主动去请求设备上的socket服务,即向socket服务发送请求,即bootstrap.jar,Bootstrap.jar再把Appium的命令转换成uiautomator的命令来让uiautomator进行处理。有请求就有返回,socket接收到请求后会做出响应,原路返回给脚本,然后脚本再进行下一次的请求。
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。appium和手机的通信过程,主要是数据交换的一个过程,socket的作用是就是为了实现双向通信,它需要一对端口号,对应到这里就是4724,手机端的bootstrap就是socket-server端,appium server就是socket-client端。
关于socket的通信原理,先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。