1. 程式人生 > 其它 >python + uiautomator2 常用公共方法封裝

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 class BasePage():
  2     """ 建構函式 """
  3     def __init__(self, driver):
  4         self.driver = driver
  5 
  6     def click(self, element, sleepTime=0):
  7         """ 點選 """
  8         if str(element).startswith("com"):  # 若開頭是com則使用ID定位
  9             self.driver(resourceId=element).click()  # 點選定位元素
 10         elif re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
 11             self.driver.xpath(element).click()  # 點選定位元素
 12         else:  # 若以上兩種情況都不是,則使用描述定位
 13             self.driver(description=element).click()  # 點選定位元素
 14         time.sleep(sleepTime)
 15 
 16     # 點選直到元素消失
 17     def click_until_gone(self, element, kind):
 18         if kind == "id":
 19             self.driver(resourceId=element).click_gone()
 20         elif kind == "class":
 21             self.driver(className=element).click_gone()
 22         elif kind == "text":
 23             self.driver(text=element).click_gone()
 24         else:  # 若以上兩種情況都不是,則使用描述定位
 25             self.driver(description=element).click_gone()  # 點選定位元素
 26 
 27     # 組合定位classname和text
 28     def click_by_classname_text(self, element1, element2):
 29         self.driver(className=element1, text=element2).click()
 30 
 31     # 組合定位classname和description
 32     def click_by_classname_description(self, element1, element2):
 33         self.driver(className=element1, description=element2).click()
 34 
 35     # 組合定位text和description
 36     def click_by_text_description(self, element1, element2):
 37         self.driver(text=element1, description=element2).click()
 38 
 39 
 40 
 41     # 根據id點選(包括非com開頭的id點選定位元素)
 42     def click_by_id(self, element, sleepTime=0):
 43         self.driver(resourceId=element).click()
 44         time.sleep(sleepTime)
 45 
 46     # 根據文字點選
 47     def click_by_text(self, element, sleepTime=0):
 48         self.driver(text=element).click()  # 點選定位元素
 49         time.sleep(sleepTime)
 50 
 51     # 根據百分比座標點選
 52     def click_by_zuobiao(self, x, y, sleepTime=0):
 53         size = self.driver.window_size()
 54         self.driver.click(int(size[0] * x), int(size[1] * y))
 55         time.sleep(sleepTime)
 56 
 57     # 根據座標點選元素
 58     def click_coord(self, x, y):
 59         self.driver.click(x, y)
 60 
 61     # 根據座標雙擊元素
 62     def double_click_by_zuobiao(self, x, y, sleepTime=0):
 63         size = self.driver.window_size()
 64         self.driver.double_click(int(size[0] * x), int(size[1] * y))
 65         time.sleep(sleepTime)
 66 
 67     # 超時時間設定點選,根據文字定位,針對點選螢幕元素反應慢的元素
 68     def click_by_text_time_out(self, element, timeout=30, sleepTime=0):
 69         self.driver(text=element).click(timeout=timeout)
 70         time.sleep(sleepTime)
 71 
 72     # 長按
 73     def long_click_extend(self, element, dur=1, sleepTime=0):
 74         zhmodel = re.compile(u'[\u4e00-\u9fa5]')
 75         if str(element).startswith("com"):  # 若開頭是com則使用ID定位
 76             self.driver(resourceId=element).long_click(dur)  # 點選定位元素
 77         elif re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
 78             self.driver.xpath(element).long_click()  # 點選定位元素
 79         elif zhmodel.search(str(element)):
 80             self.driver(description=element).long_click(dur)
 81         else:
 82             self.driver(className=element).long_click(dur)
 83         time.sleep(sleepTime)
 84 
 85     # 通過文字長按
 86     def long_click_by_text(self, element, sleepTime=0):
 87         self.driver(text=element).long_click()
 88         time.sleep(sleepTime)
 89 
 90     #  通過座標長按
 91     def long_click_by_zuobiao(self, x, y, sleepTime=0,duration: float = .5):
 92         self.driver.long_click(x, y,duration)
 93         time.sleep(sleepTime)
 94 
 95     # 清空輸入框中的內容
 96     def clear(self, element):
 97         if str(element).startswith("com"):  # 若開頭是com則使用ID定位
 98             self.driver(resourceId=element).clear_text()  # 清除文字
 99         elif re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
100             self.driver.xpath(element).clear_text()  # 清除文字
101         else:  # 若以上兩種情況都不是,則使用描述定位
102             self.driver(description=element).clear_text()  # 清除文字
103 
104     # 輸入
105     def input(self, element, value, sleepTime=0):
106         if str(element).startswith("com"):  # 若開頭是com則使用ID定位
107             self.driver(resourceId=element).set_text(value)  # send_keys
108         elif re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
109             self.driver.xpath(element).set_text(value)
110         else:  # 若以上兩種情況都不是,則使用描述定位
111             self.driver(description=element).set_text(value)
112         time.sleep(sleepTime)
113 
114     # 不存在搜尋按鈕的搜尋
115     def input_by_send_keys(self, element, value):
116         self.driver.set_fastinput_ime(True)  # 切換成FastInputIME輸入法
117         if str(element).startswith("com"):  # 若開頭是com則使用ID定位
118             self.driver(resourceId=element).send_keys(value)  # send_keys
119         elif re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
120             self.driver.xpath(element).send_text(value)
121         else:  # 若以上兩種情況都不是,則使用描述定位
122             self.driver(description=element).send_keys(value)
123         self.driver.set_fastinput_ime(False)  # 切換成正常的輸入法
124         self.driver.send_action("search")  # 模擬輸入法的搜尋
125 
126     # 查詢元素
127     def find_elements(self, element, timeout=5):  # 找元素
128         is_exited = False
129         try:
130             while timeout > 0:
131                 xml = self.driver.dump_hierarchy()  # 獲取網頁層次結構
132                 if re.findall(element, xml):
133                     is_exited = True
134                     break
135                 else:
136                     timeout -= 1
137         except:
138             print("元素未找到!")
139         finally:
140             return is_exited
141 
142     # 斷言元素是否存在, 不能判定xpath元素
143     def assert_existed(self, element):  #
144         # assert self.find_elements(element) == True, "斷言失敗,{}元素不存在!".format(element)
145         return self.find_elements(element) == True
146 
147     # 判斷元素是否存在(比如隨機彈窗等)
148     def judge_existed(self, element, type: str = "text", timeout=2):
149         if re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
150             return self.driver.xpath(element).exists == True
151         elif type == "text":
152             return self.driver(text=element).exists(timeout=timeout) == True
153         elif type == "dec":
154             return self.driver(description=element).exists(timeout=timeout) == True
155         else:
156             pass
157 
158     #  截圖
159     def screenshot(self, imageName):
160         if os.path.exists(r"./images"):
161             if os.path.exists(fr"./images/{imageName}.png"):
162                 image = self.driver.screenshot()
163                 image.save(fr"./images/{imageName}_bak.png")
164             else:
165                 image = self.driver.screenshot()
166                 image.save(fr"./images/{imageName}.png")
167         else:
168             os.mkdir(r"./images")
169             image = self.driver.screenshot()
170             image.save(fr"./images/{imageName}.png")
171 
172 
173     # 拿取文字
174     def get_text_extend(self, element=None, type: str = "id"):
175 
176         if re.findall("//", str(element)):  # 若//開頭則使用正則表示式匹配後用xpath定位
177             return self.driver.xpath(element).get_text()
178         elif type == "id":
179             return self.driver(resourceId=element).get_text()
180         elif type == "selected":
181             return self.driver(selected=True).get_text()
182         else:
183             pass
184 
185 
186     # 滑動  (正常螢幕滑動,向上滑動解鎖,返回主介面,解鎖等通用)
187     # 座標支援資料型別:Union[int, str]
188     def swipe_extend(self, x1=0.5, y1=0.99, x2=0.5, y2=0.3, dur: Union[int, str] = 0.2,
189                      sleepTime=0, type: str = "percent"):
190         if type == "percent":
191             size = self.driver.window_size()
192             x1 = int(size[0] * x1)
193             y1 = int(size[1] * y1)
194             x2 = int(size[0] * x2)
195             y2 = int(size[1] * y2)
196             self.driver.swipe(x1, y1, x2, y2, dur)
197             time.sleep(sleepTime)
198         else:
199             self.driver.swipe(x1, y1, x2, y2, dur)
200             time.sleep(sleepTime)
201 
202     # 按下之後滑動,長按滑動
203     def long_click_swipe(self, x1, y1, x2, y2, dur=0.8, sleepTime=0):
204         self.driver.touch.down(x1, y1).sleep(dur).move(x1, y1).move(x2, y2).up(x2, y2)
205         time.sleep(sleepTime)
206 
207     # 滑動,根據方向滑動
208     def swipe_ext_extend(self, direction: Union[SwipeDirection, str] = "up", scale=0.9, sleepTime=0):
209         self.driver.swipe_ext(direction, scale=scale)
210         time.sleep(sleepTime)
211 
212     # 縮放
213     def pinch_extend(self, element, kind: str = "out or in", percent=100, steps=50):
214         """ 在元素上面,做兩個手指縮放的操作,kind in 或者out,放大或者縮小"""
215         zhmodel = re.compile(u'[\u4e00-\u9fa5]')
216         if str(element).startswith("com"):
217             selector = self.driver(resourceId=element)
218         elif not zhmodel.search(str(element)):
219             selector = self.driver(className=element)
220         elif zhmodel.search(str(element)):  # 若以上兩種情況都不是,則使用描述定位
221             selector = self.driver(description=element)
222 
223         if kind == "in":
224             selector.pinch_in(percent, steps)
225         elif kind == "out":
226             selector.pinch_out(percent, steps)
227         else:
228             raise Exception("輸入kind不能是非in/out")
229 
230     # 關機
231     def reboot_physical_key(self):
232         self.driver.shell("sendevent /dev/input/event0 1 116 1")
233         self.driver.shell("sendevent /dev/input/event0 0 0 0")
234         time.sleep(3)
235         self.driver.shell("sendevent /dev/input/event0 1 116 0")
236         self.driver.shell("sendevent /dev/input/event0 0 0 0")
237         time.sleep(1)
238         self.click_by_text("關閉電源")
239 
240     #   截圖。用命令  模擬安卓物理按鍵事件(需要手機有root許可權)
241     def screenshot_physical_key(self):
242         self.driver.shell("sendevent /dev/input/event0 1 114 1")
243         self.driver.shell("sendevent /dev/input/event0 0 0 0")
244         self.driver.shell("sendevent /dev/input/event0 1 116 1")
245         self.driver.shell("sendevent /dev/input/event0 0 0 0")
246         self.driver.shell("sendevent /dev/input/event0 1 116 0")
247         self.driver.shell("sendevent /dev/input/event0 0 0 0")
248         self.driver.shell("sendevent /dev/input/event0 1 114 0")
249         self.driver.shell("sendevent /dev/input/event0 0 0 0")
250 
251     # 推本地檔案到手機
252     def push_extend(self, root: Union[list, str], target, sleepTime=1):
253         peojectPath = "\\".join(os.path.abspath(os.path.dirname(__file__)).split("\\")[:-1])
254         if isinstance(root, list):
255             for i in root:
256                 self.driver.push(peojectPath+i, target)
257         elif isinstance(root, str):
258             self.driver.push(root, target)
259         time.sleep(sleepTime)
260 
261 
262     def randmon_phone(self):
263         """ 隨機生成一個手機號,或者其他想生成的資料 """
264         while True:
265             phone = "130"
266             for i in range(8):
267                 num = random.randint(0, 9)
268                 phone += str(num)
269             return phone

以上為個人常用公共方法封裝,但不是全部,有些場景可能未覆蓋到。更多的 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