你真的了解appium吗?

2023-08-04  质量实验室 

作者:赵泽鑫 | QE_LAB

对于QA同学来说,appium应该都不陌生,作为市面上最流行的app自动化测试框架之一,凭借强大的扩展性、跨平台能力和活跃的社区,使得它成为了移动端自动化测试的首选。今天让我们一起重新了解下这个工具!

appium运行原理

appium有几个重要的部分组成,分别是appium client、web driver以及 appium server。Appium server,负责接受客户端请求并与移动设备进行通信。它使用WebDriver协议来与客户端进行通信,并使用移动设备的原生测试框架Ui automation2或者XCUITest来执行自动化测试。appium自动化app的所有指令都是基于W3C的web driver协议的。所以如果你认真看过appium的log的话,会发现每一个动作查找元素或者点击元素都是一次http请求。

官方给我们提供的driver有UIautomator和XCUITest等,所以我们可以直接下载对应的driver同Android以及iOS平台进行通讯,如果是其他平台的话,比如webOS TV,官方没有提供相应的driver,那我们就要根据web driver协议自定义一份适合webOS 的driver来完成跟webOS应用通讯的目的。对于自定义driver有兴趣的可以了解下web driver协议以及base driver

从appium日志角度了解相关的操作逻辑

@classmethod
def start(cls):
caps = {
“platformName”: “Android”,
“appium:deviceName”: “liangzai_test_simulator”,
“appium:appPackage”: “tv.danmaku.bili”,
“appium:appActivity”: “.MainActivityV2”,
“appium:newCommandTimeout”: 6000,
“appium:automationName”: “UiAutomator2”,
“appium:ensureWebviewsHavePages”: True,
“appium:nativeWebScreenshot”: True,
“appium:connectHardwareKeyboard”: True
}
cls.driver = webdriver.Remote(“http://127.0.0.1:4723“, caps)

从log截图中可以看到建立连接是通过post请求,产生一个session,内容是capability中的相关信息。


连接建立成功后系统会寻找ADB工具,理论上每一个Android的SDK都会带有一个adb工具,这里会全部list出来然后选择一个进行使用。首先会去判断simulator上是否已经存在appium.settings,没有则安装。然后检查io.appium.uiautomator2.server,没有则安装。

ADB工具负责连接simulator查看其中是否存在目标app,没有找到则尝试使用adb install app路径 命令安装。如果在连接的capability中没有设置安装app的选项,appium会认为该应用已经被安装在模拟器上并寻找,找到后如果没有设置NoReset为True的话,adb会使用am和pm命令停止正在运行的app并且清除已有数据。

打开app会使用ADB的shell am start命令,打开会进入设置好的main activity页面。接下来进行的操作就会转移到Uiautomator2进行。

driver.implicitly_wait(10)
el1 = driver.find_element(by=AppiumBy.ID, value=”tv.danmaku.bili:id/agree”).click()
el2 = driver.find_element(by=AppiumBy.ID, value=”tv.danmaku.bili:id/tv_skip”).click()
el4 = driver.find_element(by=AppiumBy.ID, value=”tv.danmaku.bili:id/search_text”).click()
el5 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value=”Search query”)
el5.send_keys(“Demon Slayer: Kimetsu No Yaiba”)
el6 = driver.find_element(by=AppiumBy.ID, value=”tv.danmaku.bili:id/action_search”)
el6.click()

隐式等待实际上是一个timeout的请求,每个请求都会带有一个session id,—>代表client发出的请求,<—代表server返回的结果。那么find_element和click操作的时候是怎么执行的呢?

这里使用的是by ID的操作,我们可以通过日志发现其实是有个转化的过程的,UIautomator并不是直接使用ID = XX进行查找的,而是由 [“id”,”tv.danmaku.bili:id/agree”,”380fc8dc-7d2e-4426-b326-0a4b97c37cf8”]的形式转成了{“strategy”:”id”,”selector”:”tv.danmaku.bili:id/agree”,”context”:””,”multiple”:false}这样的形式。

  • strategy: 定位策略,这里是id,表示使用元素的id属性来定位元素。
  • selector: 元素定位器,这里是 “tv.danmaku.bili:id/agree”,表示要定位的元素的id属性值为”tv.danmaku.bili:id/agree”。
  • context: 上下文环境,这里为空,表示在当前页面中查找元素。
  • multiple: 是否允许定位多个元素,这里为false,表示只查找一个符合条件的元素。如果使用find_elements,这里就是True。

按照这样的策略在当前页面寻找元素,如果找不到但是又因为设置了隐式等待没有超时的情况下,appium会重试再次寻找该元素直到超时。找到元素后会返回对应的ID值,也就是这里的 element/00000000-0000-005e-ffff-ffff00000011。接下来对这个元素进行点击操作的时候也是继续使用这个id:POST /element/00000000-0000-005e-ffff-ffff00000011/click] 执行click操作。

通过上面的分析我们可以直观的了解appium client的各种操作其实都是一次次的HTTP请求,每次操作都映射一个对应的请求,这也是appium支持各种不同类型编程语言的重要原因。

JSONWP协议

JSONWP的全称是Mobile JSON Wire Protocol,appium client所有的库都是基于此建立的。所以我们直接使用协议,按照协议的请求方式发送curl命令,一样可以完成自动化的操作。它本质是web driver协议的扩展协议,所以有些说法是appium基于web driver协议的也没问题。由于移动端的自动化测试不完全和web测试一样,移动端不仅有native应用,还有hybrid以及纯H5的应用,所以原有的web driver协议不能满足需求,于是便有了JSONWP协议。以下是一些比较常用的内容:

协议增加了Capabilities,如:automationName、platformName、platformVersion、deviceName等,增加了定位策略,如accessibility id;

增加了Page Source,所以我们可以使用pagesource方法获取当前页面所有的元素,这对于我们判断某元素是否存在很有帮助,同时我们也可以通过打印page source 进行debug。

为了同时支持native和webview两种不同格式的元素寻找,还增加了context内容,所以我们在进行hybrid测试的时候可以用过切换上下文的方式使用appium和selenium的不同寻址方式操作元素。

查看完整的协议内容请点击:https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md

appium源码分析

这一部分主要是给想要看源代码的人提供一点思路,在日志部分我们知道了appium使用了很多adb命令,如果你更有好奇心想要知道它是怎么操作这些adb命令的?或者好奇find_element 是怎么实现元素查找的?那么你就需要通过查看源代码来解答你的疑问。appium的源代码分成两个部分,appium仓库内的代码是将底层内容整合在一起的一个体现,可以通过appium -> lib - > main.js开始一步一步的了解这个过程。如果想了解更加底层的东西,可以通过package.json中的dependency来看。

🌰:如果想看Android的底层实现,就在appium的package.json中寻找相应的Android依赖,(注意:在appium 2.x中已经将driver分离出去了,也就是需要单独npm install driver,依赖的driver不会写在package.json中,使用这种方法请切换1.x版本分支)可以找到appium-uiautomator2-driver,再进到appium-uiautomator2-driver的package.json 中我们可以找到appium-uiautomator2-server,它是一个java编写的应用并且没有依赖其他的库,所以这就是最下面的实现了。将它clone 下来后我们就能在里面找到uiautomator2操作app的各种命令。比如find_element是这么实现的:

protected AppiumResponse safeHandle(IHttpRequest request) throws UiObjectNotFoundException {
FindElementModel model = toModel(request, FindElementModel.class);
final String method = model.strategy;
final String selector = model.selector;
final String contextId = isBlank(model.context) ? null : model.context;
if (contextId == null) {
Logger.info(String.format(“method: ‘%s’, selector: ‘%s’”, method, selector));
} else {
Logger.info(String.format(“method: ‘%s’, selector: ‘%s’, contextId: ‘%s’”,
method, selector, contextId));
}
ElementsCache elementsCache = AppiumUIA2Driver.getInstance().getSessionOrThrow().getElementsCache();
final By by = ElementsLookupStrategy.ofName(method).toNativeSelector(selector);
final AccessibleUiObject

87°/870 人阅读/0 条评论 发表评论

登录 后发表评论