前言

由于公司UI自动化框架底层用的是Uiautomator2,所以我就用Uiautomator2搭了一套UI自动化框架,思路其实和Appnium一样的。

uiautomator2是一个自动化测试开源工具,仅支持android平台的自动化测试,其封装了谷歌自带的uiautomator2测试框架;

u2 现在google 官方使用的是apk的形式来实现的,有大神封装了python来实现u2的功能的使用。

具体的了解相关的功能和实现的原理可以查看开源库:github 的地址:https://github.com/openatx/uiautomator2

ui2 的下载安装与环境配置等,见之前写的一篇帖子:https://www.cnblogs.com/gancuimian/p/16725664.html

ui2 的常用方法使用(未封装),见之前写的一篇帖子:https://www.cnblogs.com/gancuimian/p/16947337.html

 整体框架介绍:(非固定模式,每个人的习惯不同,框架会有些出入,有些包可以是非必要)

框架搭建

ps:这里主要讲 common 包下面的公共方法类(basepage.py模块)的封装,其它包/模块不做详细介绍

先创建一个BasePage.py

  • 为什么要单独封装一个BasePage呢? 如果说以后我们不用uiautomator2这个框架了,我们只需要更改BasePage即可,不会影响到其他类的代码。

  • 另外,这个类也可以封装自己写的公用的方法,例如:重复性很高的代码,这些方法不论在哪个app里都能用的话,我们就单独拧出来封装成一个方法。

模块创建完成后,先导入需要用到的内置库或需要提前安装的第三方库。

1 import os
2 import re
3 import time
4 import random
5 from typing import Union
6 from data.Swipe_Direction import SwipeDirection
# 第6行导入的是下方的一个类;在下面代码 207 行的方法中有引用。

下面代码为本人工作中会用到的一些操作 方法的封装。

  1 import os
2 import re
3 import time
4 import random
5 from typing import Union
6 from data.Swipe_Direction import SwipeDirection
7
8
9 class BasePage: # 构造函数
10 def __init__(self, driver):
11 self.driver = driver
12
13 # 点击
14 def click(self, element, sleepTime=0):
15 if str(element).startswith("com"): # 若开头是com则使用ID定位
16 self.driver(resourceId=element).click() # 点击定位元素
17 elif re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
18 self.driver.xpath(element).click() # 点击定位元素
19 else: # 若以上两种情况都不是,则使用描述定位
20 self.driver(description=element).click() # 点击定位元素
21 time.sleep(sleepTime)
22
23
24 # 点击直到元素消失
25 def click_until_gone(self, element, kind):
26 if kind == "id":
27 self.driver(resourceId=element).click_gone()
28 elif kind == "class":
29 self.driver(className=element).click_gone()
30 elif kind == "text":
31 self.driver(text=element).click_gone()
32 else: # 若以上两种情况都不是,则使用描述定位
33 self.driver(description=element).click_gone() # 点击定位元素
34
35 # 组合定位classname和text
36 def click_by_classname_text(self, element1, element2):
37 self.driver(className=element1, text=element2).click()
38
39 # 组合定位classname和description
40 def click_by_classname_description(self, element1, element2):
41 self.driver(className=element1, description=element2).click()
42
43 # 组合定位text和description
44 def click_by_text_description(self, element1, element2):
45 self.driver(text=element1, description=element2).click()
46
47
48 # 根据id点击(包括非com开头的id点击定位元素)
49 def click_by_id(self, element, sleepTime=0):
50 self.driver(resourceId=element).click()
51 time.sleep(sleepTime)
52
53
54 # 根据文本点击
55 def click_by_text(self, element, sleepTime=0):
56 self.driver(text=element).click() # 点击定位元素
57 time.sleep(sleepTime)
58
59
60 # 根据百分比坐标点击
61 def click_by_zuobiao(self, x, y, sleepTime=0):
62 size = self.driver.window_size()
63 self.driver.click(int(size[0] * x), int(size[1] * y))
64 time.sleep(sleepTime)
65
66
67 # 根据坐标点击元素
68 def click_coord(self, x, y):
69 self.driver.click(x, y)
70
71
72 # 根据坐标双击元素
73 def double_click_by_zuobiao(self, x, y, sleepTime=0):
74 size = self.driver.window_size()
75 self.driver.double_click(int(size[0] * x), int(size[1] * y))
76 time.sleep(sleepTime)
77
78
79 # 超时时间设置点击,根据文本定位,针对点击屏幕元素反应慢的元素
80 def click_by_text_time_out(self, element, timeout=30, sleepTime=0):
81 self.driver(text=element).click(timeout=timeout)
82 time.sleep(sleepTime)
83
84
85 # 长按
86 def long_click_extend(self, element, dur=1, sleepTime=0):
87 zhmodel = re.compile(u'[\u4e00-\u9fa5]')
88 if str(element).startswith("com"): # 若开头是com则使用ID定位
89 self.driver(resourceId=element).long_click(dur) # 点击定位元素
90 elif re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
91 self.driver.xpath(element).long_click() # 点击定位元素
92 elif zhmodel.search(str(element)):
93 self.driver(description=element).long_click(dur)
94 else:
95 self.driver(className=element).long_click(dur)
96 time.sleep(sleepTime)
97
98
99 # 通过文本长击
100 def long_click_by_text(self, element, dur=0.5, sleepTime=0):
101 self.driver(text=element).long_click(dur)
102 time.sleep(sleepTime)
103
104
105 # 通过坐标长击
106 def long_click_by_zuobiao(self, x, y, sleepTime=0,duration: float = 1):
107 self.driver.long_click(x, y,duration)
108 time.sleep(sleepTime)
109
110
111 # 清空输入框中的内容
112 def clear(self, element):
113 if str(element).startswith("com"): # 若开头是com则使用ID定位
114 self.driver(resourceId=element).clear_text() # 清除文本
115 elif re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
116 self.driver.xpath(element).clear_text() # 清除文本
117 else: # 若以上两种情况都不是,则使用描述定位
118 self.driver(description=element).clear_text() # 清除文本
119
120
121 # 输入
122 def input(self, element, value, sleepTime=0):
123 if str(element).startswith("com"): # 若开头是com则使用ID定位
124 self.driver(resourceId=element).set_text(value) # send_keys
125 elif re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
126 self.driver.xpath(element).set_text(value)
127 else: # 若以上两种情况都不是,则使用描述定位
128 self.driver(description=element).set_text(value)
129 time.sleep(sleepTime)
130
131 # 不存在搜索按钮的搜索
132 def input_by_send_keys(self, element, value):
133 self.driver.set_fastinput_ime(True) # 切换成FastInputIME输入法
134 if str(element).startswith("com"): # 若开头是com则使用ID定位
135 self.driver(resourceId=element).send_keys(value) # send_keys
136 elif re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
137 self.driver.xpath(element).send_text(value)
138 else: # 若以上两种情况都不是,则使用描述定位
139 self.driver(description=element).send_keys(value)
140 self.driver.set_fastinput_ime(False) # 切换成正常的输入法
141 self.driver.send_action("search") # 模拟输入法的搜索
142
143
144 # 查找元素
145 def find_elements(self, element, timeout=5): # 找元素
146 is_exited = False
147 try:
148 while timeout > 0:
149 xml = self.driver.dump_hierarchy() # 获取网页层次结构
150 if re.findall(element, xml):
151 is_exited = True
152 break
153 else:
154 timeout -= 1
155 except:
156 print("元素未找到!")
157 finally:
158 return is_exited
159
160
161 # 断言元素是否存在, 不能判定xpath元素
162 def assert_existed(self, element): #
163 # assert self.find_elements(element) == True, "断言失败,{}元素不存在!".format(element)
164 return self.find_elements(element) == True
165
166 # 判断元素是否存在,如随机弹窗等
167 def judge_existed(self, element, type: str = "text", timeout=2):
168 if re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
169 return self.driver.xpath(element).exists == True
170 elif type == "text":
171 return self.driver(text=element).exists(timeout=timeout) == True
172 elif type == "dec":
173 return self.driver(description=element).exists(timeout=timeout) == True
174 else:
175 pass
176
177
178 # 截图
179 def screenshot(self, imageName):
180 if os.path.exists(r"./images"):
181 if os.path.exists(fr"./images/{imageName}.png"):
182 image = self.driver.screenshot()
183 image.save(fr"./images/{imageName}_bak.png")
184 else:
185 image = self.driver.screenshot()
186 image.save(fr"./images/{imageName}.png")
187 else:
188 os.mkdir(r"./images")
189 image = self.driver.screenshot()
190 image.save(fr"./images/{imageName}.png")
191
192
193 # 拿取文本
194 def get_text_extend(self, element=None, type: str = "id"):
195
196 if re.findall("//", str(element)): # 若//开头则使用正则表达式匹配后用xpath定位
197 return self.driver.xpath(element).get_text()
198 elif type == "id":
199 return self.driver(resourceId=element).get_text()
200 elif type == "selected":
201 return self.driver(selected=True).get_text()
202 else:
203 pass
204
205
206 # 滑动 (正常屏幕滑动,向上滑动解锁,返回主界面,解锁等通用)
207 # 坐标支持数据类型:Union[int, str]
208 def swipe_extend(self, x1=0.5, y1=0.99, x2=0.5, y2=0.3, dur: Union[int, str] = 0.2,
209 sleepTime=0, type: str = "percent"):
210 if type == "percent":
211 size = self.driver.window_size()
212 x1 = int(size[0] * x1)
213 y1 = int(size[1] * y1)
214 x2 = int(size[0] * x2)
215 y2 = int(size[1] * y2)
216 self.driver.swipe(x1, y1, x2, y2, dur)
217 time.sleep(sleepTime)
218 else:
219 self.driver.swipe(x1, y1, x2, y2, dur)
220 time.sleep(sleepTime)
221
222
223 # 按下之后滑动,长按滑动
224 def long_click_swipe(self, x1, y1, x2, y2, dur=0.8, sleepTime=0):
225 self.driver.touch.down(x1, y1).sleep(dur).move(x1, y1).move(x2, y2).up(x2, y2)
226 time.sleep(sleepTime)
227
228
229 # 向上滑动解锁,返回主界面,解锁用
230 def swipe_up(self, time=0.2):
231 size = self.driver.window_size()
232 x1 = int(size[0] * 0.5)
233 y1 = int(size[1] * 1)
234 y2 = int(size[1] * 0.3)
235 self.driver.swipe(x1, y1, x1, y2, time)
236
237
238 # 滑动,根据方向滑动
239 def swipe_ext_extend(self, direction: Union[SwipeDirection, str] = "up", scale=0.9, sleepTime=0):
240 self.driver.swipe_ext(direction, scale=scale)
241 time.sleep(sleepTime)
242
243
244 # 缩放
245 def pinch_extend(self, element, kind: str = "out or in", percent=100, steps=50):
246 """ 在元素上面,做两个手指缩放的操作,kind in 或者out,放大或者缩小"""
247 zhmodel = re.compile(u'[\u4e00-\u9fa5]')
248 if str(element).startswith("com"):
249 selector = self.driver(resourceId=element)
250 elif not zhmodel.search(str(element)):
251 selector = self.driver(className=element)
252 elif zhmodel.search(str(element)): # 若以上两种情况都不是,则使用描述定位
253 selector = self.driver(description=element)
254
255 if kind == "in":
256 selector.pinch_in(percent, steps)
257 elif kind == "out":
258 selector.pinch_out(percent, steps)
259 else:
260 raise Exception("输入kind不能是非in/out")
261
262
263 # 关机
264 def reboot_physical_key(self):
265 self.driver.shell("sendevent /dev/input/event0 1 116 1")
266 self.driver.shell("sendevent /dev/input/event0 0 0 0")
267 time.sleep(3)
268 self.driver.shell("sendevent /dev/input/event0 1 116 0")
269 self.driver.shell("sendevent /dev/input/event0 0 0 0")
270 time.sleep(1)
271 self.click_by_text("关闭电源")
272
273
274 # 截图
275 def screenshot_physical_key(self):
276 self.driver.shell("sendevent /dev/input/event0 1 114 1")
277 self.driver.shell("sendevent /dev/input/event0 0 0 0")
278 self.driver.shell("sendevent /dev/input/event0 1 116 1")
279 self.driver.shell("sendevent /dev/input/event0 0 0 0")
280 self.driver.shell("sendevent /dev/input/event0 1 116 0")
281 self.driver.shell("sendevent /dev/input/event0 0 0 0")
282 self.driver.shell("sendevent /dev/input/event0 1 114 0")
283 self.driver.shell("sendevent /dev/input/event0 0 0 0")
284
285
286 # 推送文件到手机
287 def push_extend(self, root: Union[list, str], target, sleepTime=1):
288 peojectPath = "\\".join(os.path.abspath(os.path.dirname(__file__)).split("\\")[:-1])
289 if isinstance(root, list):
290 for i in root:
291 self.driver.push(peojectPath+i, target)
292 elif isinstance(root, str):
293 self.driver.push(root, target)
294 time.sleep(sleepTime)
295
296
297 def randmon_phone(self):
298 """ 随机生成一个手机号,或者其他想生成的数据 """
299 while True:
300 phone = "AAAAA新建"
301 for i in range(8):
302 num = random.randint(0, 9)
303 phone += str(num)
304 return phone
305
306
307 def power(self, kind: str='power or kill'):
308 '''模拟power键'''
309 if kind == 'power':
310 self.driver.screen_on() # 息屏
311 elif kind == 'kill':
312 self.driver.screen_off() # 亮屏
313 else:
314 raise Exception("输入kind有误")
315
316
317 def virtual_key(self,kind: str = "home or delete or up or down or volume_up or volume_down or volume_mute or power or back"):
318 """ 常用虚拟按键 """
319 if kind == "home":
320 self.driver.press("home") # 点击home键
321 elif kind == "delete":
322 self.driver.press("delete") # 点击删除键
323 elif kind == "up":
324 self.driver.press("up") # 点击上键
325 elif kind == "down":
326 self.driver.press("down") # 点击下键
327 elif kind == "volume_up":
328 self.driver.press("volume_up") # 点击音量+
329 elif kind == "volume_down":
330 self.driver.press("volume_down") # 点击音量-
331 elif kind == "volume_mute":
332 self.driver.press("volume_mute") # 点击静音
333 elif kind == "power":
334 self.driver.press("power") # 点击电源键
335 elif kind == "back":
336 self.driver.press("back") # 点击返回键
337 else:
338 raise Exception("输入kind有误")

以上为个人常用公共方法封装,但不是全部,有些场景可能未覆盖到。更多的 ui2 相关知识可自行网上学习。

随机推荐几个ui2相关的帖子,更多的ui2的相关知识自行网上搜索了解。

https://blog.csdn.net/Makasa/article/details/124358921

https://ceshiren.com/t/topic/5396

https://blog.csdn.net/weixin_43444734/article/details/124703281

python + uiautomator2 常用公共方法封装的更多相关文章

  1. JS常用公共方法封装

    _ooOoo_ o8888888o 88" . "88 (| -_- |) O\ = /O ____/`---'\____ .' \\| |// `. / \\||| : |||/ ...

  2. iOS常用公共方法

      iOS常用公共方法 字数2917 阅读3070 评论45 喜欢236 1. 获取磁盘总空间大小 //磁盘总空间 + (CGFloat)diskOfAllSizeMBytes{ CGFloat si ...

  3. iOS 常用公共方法

    iOS常用公共方法 1. 获取磁盘总空间大小 //磁盘总空间 + (CGFloat)diskOfAllSizeMBytes{ CGFloat size = 0.0; NSError *error; N ...

  4. appium安卓自动化的 常用driver方法封装

    appium安卓自动化的 常用driver方法封装 做安卓自动化的时候,很多方法写起来会造成代码冗余,把这部分封装起来 ,添加到androidUI工具类里,随时可调用 都放在这个类下面: @Compo ...

  5. 常用js方法封装

    常用js方法封装 var myJs = { /* * 格式化日期 * @param dt 日期对象 * @returns {string} 返回值是格式化的字符串日期 */ getDates: fun ...

  6. appium+python自动化24-滑动方法封装(swipe)

    swipe介绍 1.查看源码语法,起点和终点四个坐标参数,duration是滑动屏幕持续的时间,时间越短速度越快.默认为None可不填,一般设置500-1000毫秒比较合适. swipe(self, ...

  7. python的常用魔法方法详细总结

    构造和初始化 __init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数". 实际上, 当我们调用x = SomeClass()的时候调用,__init_ ...

  8. python字符串常用的方法解析

    这是本人在学习python过程中总结的一些关于字符串的常用的方法. 文中引用了python3.5版本内置的帮助文档,大致进行翻译,并添加了几个小实验. isalnum S.isalnum() -> ...

  9. SFTP环境搭建及客户代码调用公共方法封装

    一.背景 在开发应用软件的过程中,广泛使用FTP在各子系统间传送文本数据.但FTP存在安全问题,开放到外网存在安全漏洞,容易被攻击.替换方案是使用SFTP,SFTP提供更高的安全性,当然传输的效率也会 ...

  10. Python基础之公共方法

    公共方法:就是列表,元组,字典,字符串能共同使用的方法: Python内置函数 内置函数罗列 函数 描述 备注 len(item) 计算容器中的元素个数 del(item) 删除变量 del有两种方法 ...

随机推荐

  1. PR如何导出mp4格式的视频

    PR如何导出mp4格式的视频 PR是一款强大的视频处理软件,有时我们想导出mp4格式的视频,但好像找不到这种格式,具体怎么操作呢?PR如何导出mp4格式的视频? 1.打开PR,然后处理完视频后,点击左 ...

  2. matlab算符合集

    1.逻辑算符 1)且 : A & B -- 两个逻辑数组之间 逐个元素 进行逻辑"与"操作 AB可为矩阵. 首先判断表达式A的逻辑值,然后判断B,继而进行逻辑"与 ...

  3. Java Maven环境搭建

    下载 Maven 官网地址: https://maven.apache.org/ 点击左侧的 "Download" 下载 Files 下面的 zip 文件 推荐使用迅雷进行下载,速 ...

  4. Babel与webpack

    一.基础认知1 babel是js编译器,将新版本js代码转换成大多数浏览器支持的es5,es3代码 浏览器其实也不认识require函数,bable配合webpack解决模块的问题: 主要编译的是语法 ...

  5. 蓝牙信标、智能楼宇应用国产低功耗芯片BLE5.2 PHY6252

    PHY6252是一款支持BLE 5.2功能的系统级芯片(SoC),集成了低功耗的高性能多模射频收发机,搭载32位高性能低功耗处理器,提供64K retention SRAM.可选512/256K Fl ...

  6. sql-log

    使用插件必须 先设置打印sql日志级别    debug 不然打不出来 SQL Params Setter插件 Ctrl+V mybaties log

  7. 操作系统实战45讲笔记- 05 CPU工作模式:程序执行的三种模式

    实模式 实模式又称实地址模式,实,即真实,这个真实分为两个方面,一个方面是运行真实的指令,对指令的动作不作区分,直接执行指令的真实功能,另一方面是发往内存的地址是真实的,对任何地址不加限制地发往内存. ...

  8. linux并行执行线程

    资料来源: (1) https://www.jianshu.com/p/d8d58846c53f(作者:黄甫一) 1.并行任务较少时: (1) 给需要并行运行的命令行在结尾加上"&& ...

  9. Ubuntu22 vim配置

    插件管理器 vim-plug # 下载插件管理器 sh -c 'curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ https://raw.githu ...

  10. clear_buff-cache.sh

    #! /bin/bash sync;echo 1 > /proc/sys/vm/drop_caches # 表示清除pagecache sync;echo 2 > /proc/sys/vm ...