• 源代码层面分析Appium-inspector工作原理


    Appium-inspector功能

      Appium Inspector 基于 Appium 框架,Appium 是一个开源工具,用于自动化移动应用(iOS 和 Android)和桌面应用(Windows 和 Mac)。Appium 采用了客户端-服务器架构,允许用户通过客户端(例如 Appium Inspector)与 Appium 服务器通信,以自动化测试应用程序。主要用这个工具来查找获取元素的locator,提升mobile UI自动化测试效率。

      Appium-inspector的界面如下图所示,在界面上可以点击app页面元素,左边会显示被选择元素的selector,在app source的具体位置等信息。

    Appium-inspector工作原理

    Appium-inspector的工作流程大致可以分为如下四个部分
    连接移动设备或模拟器:Appium Inspector 连接到目标移动设备或模拟器。可以是物理设备,也可以是虚拟设备(模拟器或仿真器)。
    启动移动应用:Appium 服务器接收客户端(Appium Inspector)的指令,通过相应的驱动(如 uiautomator2 驱动用于 Android,XCUITest 驱动用于 iOS),启动目标移动应用。
    获取应用页面源代码:一旦应用启动,Appium 服务器通过驱动与移动设备或模拟器通信,获取当前页面的源代码(DOM 树),包括所有 UI 元素及其属性。这个页面源代码通常以 XML 格式表示。
    展示 UI 元素:Appium Inspector 将获取到的页面源代码解析并展示在图形界面上。用户可以在 Appium Inspector 中看到当前应用页面的结构、元素的层次关系,以及每个元素的属性(如 resource-id、class、text 等)。
    生成 Locator:用户在 Appium Inspector 中可以选择某个 UI 元素,Appium Inspector 会根据该元素的属性生成唯一的定位符(Locator)。这些 Locator 可以用来在自动化脚本中定位和操作该元素。常见的 Locator 类型包括:ID、XPath、Class Name、Accessibility ID 等。

    Appium-inspector如何显示mobile app UI

      在appium inspector上面,可以加载显示mobile app应用页面,这个是如何实现的呢?实际appium inspector是通过截图来显示app页面UI的,实现这个过程主要有四个步骤       

    截图获取: Appium Inspector使用Appium Server通过WebDriver协议与移动设备建立连接。一旦连接建立,Inspector会向设备发送命令以获取当前应用的屏幕截图。

    截图传输: 设备收到命令后,会执行截图操作,并将截图数据传输回Appium Server。这通常是以base64编码的图像数据的形式返回。

    解码和展示: Appium Server接收到截图数据后,会对其进行解码,并在Inspector界面中展示。Inspector本身会使用相关的图像处理技术,如将base64编码的图像数据解析为可见的图像。

    图像渲染: 解码后的图像在Inspector界面中进行渲染,以显示移动应用的当前界面。这个过程涉及将图像数据转换为可视化的UI元素,通常以实时或者轮询的方式更新截图以显示最新状态。

      当用appium-inspector连接到appium server时,可以在appium server的日志中看到如下信息,可以看到调用了appium server的接口获取screenshot信息和page source信息。screenshot就是用来显示mobile app UI的。

    Appium-inspector如何生成selector

      当鼠标hover到某个元素时,appium inspector会设置元素的x,y坐标信息。有了坐标信息,就可以发送给appium server来获取页面元素信息。(需要补充修改)

    Appium-inspector源码阅读

     下图是appium-inspector source code,component里面是用react写各个自定义组件,这些组件会组合到containers下面的InspectorPage.js和SessionPage.js中,其中SessionPage.js及时appium-inspector连接信息输入的界面,InspectorPage.js就是连接成功后显示source page,locator等信息的的界面。另外,这里使用了react reducers技术,在 React 应用中,useReducer 钩子是一种处理复杂状态逻辑的方式,特别是当组件状态由多个子值组成,或者当状态逻辑需要根据多种不同的动作进行更改时。

      以reducers目录下Inspector.js为例,接受action.type,来更新state信息。

       以action目录下的Inspector.js为例,文件里面定义了很多方法,主要是通过dispatch来分发动作。

      像获取整个页面的pageSource,screenshot等,是call appium server的api来获取的,这些需要与appium server进行交互的部分,都封装在callClientMethod方法里,这个方法也定义在action/Inspector.js文件中。

      除了上面的一些关键source code,当用户点击页面的某个element的时候,会在appium-inspector中显示生成的selector,这个是如何实现的呢?在utils目录下有个locator-generation.js文件,里面有getSuggestedLocactor方法,里面调用了getSimpleSuggestedLocators和getComplexSuggestedLocators方法,再将获取的locator进行对比选取合理的locator。

      SimpleSuggestedLocators方法主要是看元素信息中是否有content-desc,id,resource-id等信息,如果有这些信息,就用这些信息来生成locator。

      ComplexSuggestedLocators方法,主要是使用'-ios class chain','-android uiautomator'等来生成定义页面元素的locator。

      上面生成locator的前提是要给出选定element的相关信息,从appium-inspector中下载source page,如下图所示,source page中除了有元素的content-desc等信息外,还有个关键信息是bounds,也就是元素的坐标信息。当鼠标选中某个element的时候,可以计算得到element的坐标信息,再根据这个坐标信息从source page中获取到对应的目标元素信息。那appium-inspector是什么时候计算所选元素的坐标信息的呢?

        在component目录下的Screenshot.jsx文件中,有个handleMouseMove的方法,可以看到,当鼠标移动的时候,这里会调用setX,setY来设置坐标信息。

      以上就是Appium-inspector源码部分解读,可以看到Appium源码理解可以分为下面7部分

    1:使用react来构建appium-inspector的UI,主要保卡session页面和inspector页面。

    2:使用了react的reducer来管理复杂的状态信息,对于涉及异步调用的部分,还使用了redux thunk。

    3:需要与appium server交互的部分,都封装在clientMethod中。

    4:inspector界面显示的app UI是通过调用appium server api,获取到app的截图信息,然后通过截图显示来实现app UI展示的。

    5:inspector界面的page source也是调用appium server的api获取的。

    6:获取的page source里面包含每个页面元素的坐标信息,当鼠标move到某个元素上面时,会设置元素的x,y坐标信息,这样当选中某个元素的时候,可以通过坐标信息,获取目标元素在page source中的所有信息

    7:获取到元素信息后,封装了一些方法来生成locator,主要包括简单locator生成和复杂locator生成。实际只通过坐标获取到当前元素属性信息,还无法直接生成所有的locator,在source code的utils目录下有个source-parsing文件,即把xml格式的page source信息转换成JSON格式的信息,有了JSON格式的数据,才能通过当前元素查找这个元素的父亲元素,从而生成完善的locator信息,生成locator信息后,还可以调用appium-server的api确定这个locator是否是正确和唯一的。

      以下图为例,当选中左边的username input输入框的时候,右边的locator有两种方式,第一种是通过id查找element,第二种是通过xpath查找element。当确定了selector后,appium-inspector会调用appium server的api来确认是否能查找到元素。

      如下图所示,是inspector调用apppium server的api的截图信息,当输入id的信息,调用接口时,返回了‘no such element’,当输入xpath信息,调用接口,返回的element的信息,返回的状态码也是200,说明匹配到元素。

    xml格式转换成json格式

      Appium-inspector中,当获取到xml格式的page source后,还调用了util里面的parsing方法将xml格式转换成json格式的page source,source code内容如下图所示:

      这个方法的作用就是将xml格式的page source转换成JSON格式,转换后,就可以检索获取当前element的层级信息了,便于生成xpath信息。假设输入的xml格式的source如下图所示:

      调用方法后,生成的JSON格式的内容如下图所示:可以看到,在json格式数据中,可以通过path信息,计算出所有元素之间的层级关系。

    1. {
    2. "children": [
    3. {
    4. "children": [
    5. {
    6. "children": [],
    7. "tagName": "title",
    8. "attributes": {
    9. "lang": "en"
    10. },
    11. "path": "0.0"
    12. },
    13. {
    14. "children": [],
    15. "tagName": "author",
    16. "attributes": {},
    17. "path": "0.1"
    18. },
    19. {
    20. "children": [],
    21. "tagName": "year",
    22. "attributes": {},
    23. "path": "0.2"
    24. },
    25. {
    26. "children": [],
    27. "tagName": "price",
    28. "attributes": {},
    29. "path": "0.3"
    30. }
    31. ],
    32. "tagName": "book",
    33. "attributes": {
    34. "category": "children"
    35. },
    36. "path": "0"
    37. },
    38. {
    39. "children": [
    40. {
    41. "children": [],
    42. "tagName": "title",
    43. "attributes": {
    44. "lang": "en"
    45. },
    46. "path": "1.0"
    47. },
    48. {
    49. "children": [],
    50. "tagName": "author",
    51. "attributes": {},
    52. "path": "1.1"
    53. },
    54. {
    55. "children": [],
    56. "tagName": "year",
    57. "attributes": {},
    58. "path": "1.2"
    59. },
    60. {
    61. "children": [],
    62. "tagName": "price",
    63. "attributes": {},
    64. "path": "1.3"
    65. }
    66. ],
    67. "tagName": "book",
    68. "attributes": {
    69. "category": "web"
    70. },
    71. "path": "1"
    72. }
    73. ],
    74. "tagName": "bookstore",
    75. "attributes": {},
    76. "path": ""
    77. }

       以上就是整个appium-inspector实现原理的梳理和总结。

  • 相关阅读:
    Ubuntu 24.04安装zabbix7.0.0图形中文乱码
    进阶JAVA篇-深入了解 Set 系列集合
    SpringBoot漏洞大全
    Hugging News #0616: 有几项非常重要的合作快来围观、最新中文演讲视频回放发布!
    RPC接口测试技术-Tcp 协议的接口测试
    024 - STM32学习笔记 - 液晶屏控制(一) - LTDC与DMA2D初始
    实践torch.fx第二篇-fx量化实操
    LeetCode热题100——图论
    【Leetcode每日一题:882. 细分图中的可到达节点~~~单源最短路径Dijkstra算法】
    从零开始学JAVA(01):配置Java运行环境、实现HelloWorld
  • 原文地址:https://blog.csdn.net/qiaotl/article/details/140020026