python + uiautomator2 常用公共方法封装
前言
由于公司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 常用公共方法封装的更多相关文章
- JS常用公共方法封装
_ooOoo_ o8888888o 88" . "88 (| -_- |) O\ = /O ____/`---'\____ .' \\| |// `. / \\||| : |||/ ...
- iOS常用公共方法
iOS常用公共方法 字数2917 阅读3070 评论45 喜欢236 1. 获取磁盘总空间大小 //磁盘总空间 + (CGFloat)diskOfAllSizeMBytes{ CGFloat si ...
- iOS 常用公共方法
iOS常用公共方法 1. 获取磁盘总空间大小 //磁盘总空间 + (CGFloat)diskOfAllSizeMBytes{ CGFloat size = 0.0; NSError *error; N ...
- appium安卓自动化的 常用driver方法封装
appium安卓自动化的 常用driver方法封装 做安卓自动化的时候,很多方法写起来会造成代码冗余,把这部分封装起来 ,添加到androidUI工具类里,随时可调用 都放在这个类下面: @Compo ...
- 常用js方法封装
常用js方法封装 var myJs = { /* * 格式化日期 * @param dt 日期对象 * @returns {string} 返回值是格式化的字符串日期 */ getDates: fun ...
- appium+python自动化24-滑动方法封装(swipe)
swipe介绍 1.查看源码语法,起点和终点四个坐标参数,duration是滑动屏幕持续的时间,时间越短速度越快.默认为None可不填,一般设置500-1000毫秒比较合适. swipe(self, ...
- python的常用魔法方法详细总结
构造和初始化 __init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数". 实际上, 当我们调用x = SomeClass()的时候调用,__init_ ...
- python字符串常用的方法解析
这是本人在学习python过程中总结的一些关于字符串的常用的方法. 文中引用了python3.5版本内置的帮助文档,大致进行翻译,并添加了几个小实验. isalnum S.isalnum() -> ...
- SFTP环境搭建及客户代码调用公共方法封装
一.背景 在开发应用软件的过程中,广泛使用FTP在各子系统间传送文本数据.但FTP存在安全问题,开放到外网存在安全漏洞,容易被攻击.替换方案是使用SFTP,SFTP提供更高的安全性,当然传输的效率也会 ...
- Python基础之公共方法
公共方法:就是列表,元组,字典,字符串能共同使用的方法: Python内置函数 内置函数罗列 函数 描述 备注 len(item) 计算容器中的元素个数 del(item) 删除变量 del有两种方法 ...
随机推荐
- redis注册成windows服务步骤
1.cmd命令切换到redis的安装目录 2.执行如下命令,将redis注册成为windows的服务 redis-server --service-install redis.windows.conf ...
- 处理uniapp激励广告
使用uniapp查看广告 激励视频广告组件.激励视频广告组件是一个原生组件,并且是一个全局单例.层级比上屏 Canvas 高,会覆盖在上屏 Canvas 上.激励视频 广告组件默认是隐藏的,需要调用 ...
- 【转载】 VCS编译的基本参数,//code细节,注释
https://www.bilibili.com/read/cv18255106?spm_id_from=333.999.0.0
- gcc 中weak弱函数
1.weak弱函数 weak 函数用于定义变量或者函数.弱函数一般用于多个模块间的交互接口 int __attribute__((weak)) test_lib_a(int a, int b) { p ...
- winform高dpi问题探索
winform的高dpi适应问题由来已久,属于一个历史遗留问题.主要是由于winform对不同尺寸.不同分辨率的屏幕适配不足造成的.接下来我们简单说一下如何解决,最后我们探索一下解决此问题的原理. ...
- uniapp安卓本地文件读取(html5+)
plus.io.resolveLocalFileSystemURL( "_www/static/caise.json",//static下路径 ...
- codeforces 165C Another Problem on Strings 二分or双指针
题意:给一个01字符串s,找出s包含恰好k个1的连续字串个数 解法: 显然是简单的双指针or二分的题,但由于k=0的存在,使得双指针的边界条件十分难写,所以应该选择二分! #include<bi ...
- CentOS 6.7 hadoop free版本Spark 1.6安装与使用
最近的工作主要围绕文本分类,当前的解决方案是用R语言清洗数据,用tm包生成bag of words,用libsvm与liblinear训练模型.这个方案可以hold住6/70万的训练集: LIBLIN ...
- 爬qqhentai
import requestsfrom bs4 import BeautifulSoupimport timeimport reimport osimport randomagentlist = [& ...
- Linux安装证书
Linux安装 vCenter root CA: 1.访问vCenter管理页面,下载"下载受信任的根 CA 证书" 2.压缩文件内带有数字作为扩展名(.0..1 等)的文件是根证 ...