你真的了解appium吗?

背景:对于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)

图片

建立连接是通过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的不同寻址方式操作元素。

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 element = contextId == null
               ? findElement(by)
               : findElement(by, elementsCache.get(contextId));
       if (element == null) {
           throw new ElementNotFoundException();
       }
       AndroidElement androidElement = elementsCache.add(element, true, by, contextId);
       return new AppiumResponse(getSessionId(request), androidElement.toModel());
   }

这段代码我们可以发现,其实是接受了一个IHttpRequest request然后进行解析,解析的内容有strategy,selector,context,这就解释了上面日志中为什么对find请求的内容做了一次转化的原因。如果元素没有找到,那就会抛出ElementNotFoundException异常。

同样的方式也适用于iOS,相关的内容可以追溯到一个名叫WebDriverAgent的仓库中,他是一个object-c编写的应用。在里面有XCUITest对于ios底层相关操作的实现逻辑。

知道这些后就可以尝试对appium进行二次封装然后在本地使用自己定制化的appium啦!下次有时间再写二次封装的相关内容。(下次一定🧐)最后希望大家看到这里能有所收获,对appium有新的了解,如果有更多想法也欢迎一起探讨!

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值