自动化工具Nico,从零开始干掉Appium,移动端自动化测试框架实现
这篇将用较短的篇幅给大家介绍我是如何实现iOS和Android的inspector(元素审查工具)的。
实现原理
为了更方便的显示UI界面,且更容易制作,我选择了使用web端来承载整个元素树展示。同时我选用Flask一次性梭哈前后端(因为本身就是个体量不大的web page,如果要用react或者vue有点杀鸡用牛刀的感觉),不得不说Flask 在编写一些小型的web应用的时候真的是个利器,非常推荐大家试用。
左上方是元素结构展示,左下方是具体悬停的元素的详情,右侧是当前移动端画面展示。
首先在启动初始化 inspector 的时候,会先向移动端的 sever 发送请求获取图像和元素树结构。
// 在python端构建url装饰器@app.route('/refresh_image')def refresh_image():port = int(os.environ.get('RemoteServerPort'))platform = os.environ.get('nico_ui_platform')if platform == "android":new_data = send_tcp_request(port, "get_png_pic:100")else:new_data = send_tcp_request(port, "get_jpg_pic:1.0")base64_data = base64.b64encode(new_data)return base64_data@app.route('/refresh_ui_xml')def refresh_ui_xml():root = dump_ui_tree()# 构建HTML列表 html_list = xml_to_html_list(root)# 渲染模板并传递构建的HTML列表 return html_list
// 这里使用jquery实现请求发送。function refreshData() {bounds_list = []// 发送GET请求到服务器,刷新图片$.get('/refresh_image', function(data) {var img = document.querySelector('img');img.src = 'data:image/png;base64,' + data;img.setAttribute("id","image_")});// 发送GET请求到服务器,刷新XML$.get('/refresh_ui_xml', function(data) {var xmlContainer = document.querySelector('.content-inner');xmlContainer.innerHTML = data;initImageControl()addTextControlHoverListeners(); // 添加新的事件监听器addImageListeners(); // 添加新的事件监听器});}
拿到整个元素树之后,我通过递归遍历所有节点并为其创建一个个 div 标签。
# 递归函数,用于将XML元素及其属性转换为HTML列表项def xml_to_html_list(element, depth=0):# 开始列表项random_number = random.randint(100000, 999999)html = f'<div class="node" style="text-indent: -1em; padding-left: {depth + 1}em; word-wrap: break-word;"'# 如果元素有属性,将它们添加到列表项中if element.attrib:html += " " + " ".join([f'{k}="{v}"' for k, v in element.attrib.items()])html += f'''identifier_number = "nico_{random_number}"'''if depth != 0:content = element.tagelse:html += f'''id = "Title" '''html += f'''current_package_name = {os.environ.get("current_package_name")} '''html += f'''nico_ui_platform = {os.environ.get('nico_ui_platform')}'''content = f"{element.tag} for {os.environ.get('nico_ui_platform')}"html += f'><strong style="font-size: 2em;">{content}</strong>'# 如果元素有属性,将它们作为文本添加if element.attrib:html += " (Attributes: "html += ", ".join([f'{k}="{v}"' for k, v in element.attrib.items()])html += ")"# 如果元素有文本内容,将其添加到列表项中if element.text and element.text.strip():html += f" - Text: {element.text.strip()}"html += "</div>"# 处理子元素children = list(element)if children:for child in children:html += xml_to_html_list(child, depth + 1)return html
我们在生成的 html 界面可以看到,每一级元素节点对应一个 div 标签。
由于html和xml拥有几乎相同的特性,所以转化起来其实很简单。然后除了处理转换之外,我还为每个div事件都添加了监听事件,方便当我们鼠标悬停在某一个节点时。下方会展示该元素对应的详细属性值。
细心的同学可能会注意到,我在xml_to_html_list
,这个方法开头加入了使用了一个随机数 random_number = random.randint(100000, 999999)
,这个可大有用处。使用过其他 inspector 的同学应该知道,当我们鼠标悬停在具体的控件节点上时,右侧图像通常会有一个半透明的遮罩,告知你当前控件节点在实际设备上的具体位置。而当鼠标悬停在图像上的某一位置时,也会出现一个半透明的遮罩,告知你当前悬停处,所显示的控件,同时左侧的元素树也会相应跳转到对应位置。
或者这样
为了实现这个功能,我在创建 div 层级的时候,每生成一个控件 div 标签的同时就会在右侧的图像上方添加一个透明的 div 标签。添加监听事件,当我们鼠标悬停在对应位置的控件上时,会将这个透明的控件改为半透明的绿色,来展示我们当前悬停位置的控件是什么。而图像上的透明 div 标签的长宽坐标就来源于左侧控件 div 标签里的提供的属性。
当然这样一来还不够,为了能够实现控件文本节点和控件图像的一一对应关系,前面提到的随机数就发挥了作用。我用这个随机数为每个控件文本 div 标签添加了唯一 id 属性。
identifier_number
,
在生成对应透明控件 div 标签时也加上一样的 id 属性img-identifier_number
那么这样就能实现当我鼠标悬停在任意一边的时候,可以传递该id,在另一侧中找到相同id的 div 标签,并高亮处理。
于是整个inspector工具就完成啦!目前这个工具还是只能够看,未来有空也许会把点击等操作的功能一并加入进去。
结束语:
写了这么多,终于也是到了尾声。其实当初之所以构建这个框架,除了给自己公司的项目使用,更多的还是抱着一个学习的态度。我是一个使用东西喜欢刨根问底,探究原理的人。研究一个东西最好的办法就是拆开它然后自己重现它。
在编程领域不外如此,熟读别人的源码,读深了,读透了,读到自己也能够独立写一个了,那就算成了。那么成了之后,我也希望能够让更多的人看到,于是我选择了开源,并且选择用一篇文章的形式将我的设计思路和做这个框架的心路历程写出来分享给大家看。一方面对于我来说是一种回顾和记录,一方面能够为更多对这方面感兴趣的同学不敢说指引方向,只能说避避坑少走一些弯路。
之前有看到论坛里的有同学说,现在写技术文章的人越来越少了。有人说大环境这样,人人自危还有谁有心情有时间抽出来分享技术。怎么说呢,这确实是一个无解的题。但我们能将做就是在尚能安身立命的时候,仍然抱着对技术的热情,做更多的事。这样保证自己在大环境回归的那一天,仍能寒芒不减当年。也希望未来能够更好吧~!
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末自行领取】
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!
相关文章:

自动化工具Nico,从零开始干掉Appium,移动端自动化测试框架实现
这篇将用较短的篇幅给大家介绍我是如何实现iOS和Android的inspector(元素审查工具)的。 实现原理 为了更方便的显示UI界面,且更容易制作,我选择了使用web端来承载整个元素树展示。同时我选用Flask一次性梭哈前后端(因…...
Fast CRC32
链接: Fast CRC32 Error Checking Real life data tends to get corrupted because machines (and humans) are never as reliable as we wish for. One efficient way is make sure your data wasnt unintendedly modifiied is to generate some kind of hash. T…...
生成一个带有二维数据和对应标签的螺旋形数据集(非线性可分数据集)的代码解析
def create_dataset():np.random.seed(1)m 400 # 数据量N int(m/2) # 每个标签的实例数D 2 # 数据维度X np.zeros((m,D)) # 数据矩阵Y np.zeros((m,1), dtypeuint8) # 标签维度a 4 for j in range(2):ix range(N*j,N*(j1))t np.linspace(j*3.12,(j1)*3.12,N) np.rando…...
PHP unset() 函数的作用
PHP 中的 unset() 函数用于销毁指定的变量。具体来说,它会解除变量名与其数据之间的关联,从而释放该变量所占用的内存。不过需要注意的是,unset() 并不是删除变量的内容,而是取消对变量名的引用。如果变量是数组中的某个元素或者对…...

长篇故事可视化方法Story-Adapter:能够生成更高质量、更具细腻交互的故事图像,确保每一帧都能准确地传达故事情节。
今天给大家介绍一个最新的长篇故事可视化方法Story-Adapter,它的工作原理可以想象成一个画家在创作一幅长画卷。首先,画家根据故事的文本提示画出初步的图像。这些图像就像是画卷的草图。接下来,画家会不断回顾这些草图,逐步添加细…...

C++基础面试题 | 什么是C++中的运算符重载?
文章目录 回答重点:示例: 运算符重载的基本规则和注意事项: 回答重点: C的运算符重载是指可以为自定义类型(如类或结构体)定义运算符的行为,使其像内置类型一样使用运算符。通过重载运算符&…...

深入 IDEA 字节码世界:如何轻松查看 .class 文件?
前言: 作为一名 Java 开发者,理解字节码对于优化程序性能、调试错误以及深入了解 JVM 运行机制非常重要。IntelliJ IDEA 作为最流行的开发工具之一,为开发者提供了查看 .class 文件字节码的功能。在本文中,我将带你一步步探索如何…...
NodeJS 利用代码生成工具编写GRPC
生成的 gRPC 代码优点 自动化和效率: 减少手动编码:生成代码自动处理了消息的序列化和反序列化、服务接口的定义等,减少了手动编码的工作量。一致性:生成的代码确保了客户端和服务器之间的一致性,避免了手动编码可能带来的错误。跨语言支持: 多语言兼容:gRPC 支持多种编…...

uni-app基础语法(一)
我们今天的学习目标 基础语法1. 创建新页面2.pages配置页面3.tabbar配置4.condition 启动模式配置 基础语法 1. 创建新页面 2.pages配置页面 属性类型默认值描述pathString配置页面路径styleObject配置页面窗口表现,配置项参考pageStyle 我们来通过style修改页面的…...

Linux:进程控制(三)——进程程序替换
目录 一、概念 二、使用 1.单进程程序替换 2.多进程程序替换 3.exec接口 4.execle 一、概念 背景 当前进程在运行的时候,所执行的代码来自于自己的源文件。使用fork创建子进程后,子进程执行的程序中代码内容和父进程是相同的,如果子进…...
LeetCode279:完全平方数
题目链接:279. 完全平方数 - 力扣(LeetCode) 代码如下 class Solution { public:int numSquares(int n) {vector<int> dp(n 1, INT_MAX);dp[0] 0;for(int i 1; i * i < n; i){for(int j i * i; j < n; j){dp[j] min(dp[j …...

python爬虫--某动漫信息采集
python爬虫--tx动漫 一、采集主页信息二、采集详情页信息三、代码供参考一、采集主页信息 略。 二、采集详情页信息 如上图所示,使用xpath提取详情页的标题、作者、评分、人气、评论人数等数据。 三、代码供参考 import csv import time import random import requests fr…...

使用Rollup.js快速开始构建一个前端项目
Rollup 是一个用于 JavaScript 项目的模块打包器,它将小块代码编译成更大、更复杂的代码,例如库或应用程序。Rollup 对代码模块使用 ES6 模块标准,它支持 Tree-shaking(摇树优化),可以剔除那些实际上没有被…...
10.15学习
1.程序开发的步骤 定义程序的目标→设计程序→编写代码(需要选择语言,一种语言对应一种编译器)→编译→运行程序→测试和调试程序→维护和修改程序 2.ANSI/ISO C标准 1989年ANSI批准通过,1990年ISO批准通过,因此被称…...

mongodb-7.0.14分片副本集超详细部署
mongodb介绍: 是最常用的nosql数据库,在数据库排名中已经上升到了前六。这篇文章介绍如何搭建高可用的mongodb(分片副本)集群。 环境准备 系统系统 BC 21.10 三台服务器:192.168.123.247/248/249 安装包:…...
C++运算出现整型溢出
考虑如下代码: int aINT_MAX; int b 1; long c ab; 这段代码没有编过! 原因是a和b都是int型,相加之后会溢出。 请记住,c语言没有赋值,只有表达式,右侧会存在一个暂存的int保存ab的值,而明…...

LeetCode岛屿数量
题目描述 给你一个由 1(陆地)和 0(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网…...

Karmada核心概念
以下内容为翻译,原文地址 Karmada 是什么? | karmada 一、Karmada核心概念 一)什么是Karmada 1、Karmada:开放,多云,多集群Kubernetes业务流程 Karmada (Kubernetes Armada)是一个Kubernetes管理系统&…...

Rust 与生成式 AI:从语言选择到开发工具的演进
在现代软件开发领域,Rust 语言正在逐步崭露头角,尤其是在高性能和可靠性要求较高的应用场景。与此同时,生成式 AI 的崛起正在重新塑造开发者的工作方式,从代码生成到智能调试,生成式 AI 的应用正成为提升开发效率和质量…...
Python爬虫高效数据爬取方法
大家好!今天我们来聊聊Python爬虫中那些既简洁又高效的数据爬取方法。作为一名爬虫工程师,我们总是希望用最少的代码完成最多的工作。下面我ll分享一些在使用requests库进行网络爬虫时常用且高效的函数和方法。 1. requests.get() - 简单而强大 requests.get()是我们最常用的…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...