前言

由于公司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. 《《《PLSQL Developer编辑器怎么设置圆括号高亮

    转载:PLSQL Developer编辑器怎么设置圆括号高亮-百度经验 (baidu.com) 为了在plsql中调代码好调 LSQL Developer编辑器怎么设置圆括号高亮?有时候对PLSQL ...

  2. lib_0001_file_manager.py

    #!/usr/bin/python # -*- coding: UTF-8 -*- import os import pathlib import shutil import sys class fi ...

  3. 在QAS里面,建立副本请求

  4. MxDraw云图平台 2021.10.28更新,H5在线CAD,网页CAD,网页浏览编辑DWG

    1. 增加转换CAD文件的outpath参数设置,设置输出文件路径2. 修改转换程序,在 windows server 2010上,没有buf,距到中文路径的文件 ,转换会失败问题3. 修改宋体字体显 ...

  5. golang 日志

    package log import ( "NOONASN/global" "github.com/natefinch/lumberjack" "go ...

  6. ansible笔记第二章(ansible-varable变量)

    (1)变量类型 1.1在playbook文件中的play使用变量 [root@m01 project1]# cat vars_1.yml - hosts: oldboy vars:   - web_p ...

  7. a[i]之和小于N的含义

    例题 小白月赛 困难卷积 题目 要求一个暴力算是 \(O(n^2)\) 的东西 同时题目保证 \(\sum a[i] \leq 10^7\) 题解 \(\sum a[i] \leq 10^7\) 的含 ...

  8. Javascript格式化数字字符串,如手机号,银行卡号的格式化

    手机号13312341234转化成133 1234 1234 //方式一 function format_tel(tel){ tel = String(tel); return tel.replace ...

  9. 查看Windows操作系统编码方式

    chcp 编码表: 代码页 国家(地区)或语言 437 美国 708 阿拉伯文(ASMO 708)720 阿拉伯文(DOS)850 多语言(拉丁文 I) 852 中欧(DOS) - 斯拉夫语(拉丁文 ...

  10. wake on lan magic packet

    局域网模式,必须电脑和手机在同一个 局域网,或者是网段内 外网模式  这个就是,你拿着手机,在任何地方,都可以开机电脑.