Uiautomator2介绍
uiautomator2 是一个可以使用Python对Android设备进行UI自动化的库。其底层基于Google uiautomator,Google提供的uiautomator库可以获取屏幕上任意一个APP的任意一个控件属性,并对其进行任意操作
环境搭建
-
安装JDK,请参考此文章 -
安装Android SDK,构建工具版本需大于24,下载并安装工具包时请注意版本,SDK配置请参考此文章 -
安装uiautomator2 pip install uiautomator2
uiautomator2 version
uiautomator2 --help
-
安装查看器,用于元素定位辅助 pip install weditor
weditor -v
weditor --help
启动查看器,在命令行输入weditor ,或者创建weditor桌面快捷键weditor --shortcut ,通过桌面图标运行程序
连接设备
首次连接设备会安装【ATX】和【com.github.uiautomator.test】两个软件
-
手机开启【开发者模式】,打开【USB调试模式】数据线连接手机,选择【传输文件】 -
通过cmd进入命令行页面,通过adb devices 查看设备是否连接成功,adb相关命令请查看此文章 -
设备连接成功后打开查看器,在查看器页面点击【Connect】,出现绿色树叶表示连接成功,左侧会出现手机投屏(手机亮屏状态) -
通过Python脚本连接手机: import uiautomator2 as u2
driver = u2.connect()
print(driver.info)
输入如下系统信息,说明连接手机成功,就可以开始使用uiautomator2库啦 {'currentPackageName': 'com.oppo.launcher', 'displayHeight': 2297, 'displayRotation': 0, 'displaySizeDpX': 360, 'displaySizeDpY': 800, 'displayWidth': 1080, 'productName': 'OnePlus9R_CH', 'screenOn': True, 'sdkInt': 30, 'naturalOrientation': True}
driver = u2.connect("48fd6742")
常用操作
driver = u2.connect("48fd6742")
driver.screen_on()
driver.screen_off()
driver.unlock()
print(driver.app_info("com.sankuai.meituan"))
print(driver.app_list_running())
print(driver.app_current())
print(driver.window_size())
print(driver.device_info)
print(driver.serial)
print(driver.wlan_ip)
driver.press("back")
driver.open_notification()
driver.open_quick_settings()
driver.info.get("screen_on")
driver.swipe(552,2066,552,700)
driver.swipe_ext("up",scale=0.5)
driver.open_url("https://www.baidu.com")
driver(description="信息").click()
driver.double_click(0.375, 0.496)
driver(description="信息").long_click(1)
driver(description="短信").send_keys("12")
driver(description="短信").set_text("A12")
driver(description="短信").clear_text()
driver(resourceId="com.sankuai.meituan:id/passport_mobile_phone").get_text()
driver(text="美团").drag_to(0.375, 0.375,duration=1)
driver.screenshot(r"D:\Download\test.png")
driver.push(r"C:\Download\test.png","/sdcard/")
driver.pull("/sdcard/rider.txt","rider.txt")
driver.app_icon("com.sankuai.meituan").save(r"D:\Download\icon.png")
driver.app_start("com.sankuai.meituan",stop=True)
driver.implicitly_wait(10)
driver(description="天天领红包").exists()
driver(description="电影/演出").wait(timeout=5)
driver(description="跑腿").wait_gone(timeout=5)
driver.app_wait("com.sankuai.meituan",timeout=30,front=True)
driver.wait_activity("com.meituan.mmp.lib.mp.MPActivity0",timeout=5)
driver.app_install(r"D:\Download\meituan.apk")
driver.app_install("http://www.meituan.com/mobile/download/meituan/android/meituan?from=new")
driver.app_uninstall("com.sankuai.meituan")
driver.app_stop("com.sankuai.meituan")
元素定位
所选元素的属性都可以用来定位,目的是通过这些属性获取唯一的定位元素,属性可以组合定位,常用的定位如下
driver(text="运动健康")
driver(description="我的")
driver.xpath('//*[@text="今日特价"]')
driver(className="android.widget.ImageView")
driver(resourceId="com.sankuai.meituan:id/button")
driver(resourceId="com.android.systemui:id/tile_label", text="省电模式")
还可以根据层次结构中的节点进行定位,如下
driver(className="android.widget.GridLayout").sibling(className="android.view.View")
driver(className="android.widget.GridLayout").child_by_instance(3,className="android.view.View")
driver(className="android.widget.LinearLayout")\
.child_by_text("骑车",allow_scroll_search=True,className="android.widget.TextView")
driver(className="android.widget.FrameLayout")\
.child_by_description("评论插图",allow_scroll_search=True,className="android.view.ViewGroup")
driver(text="网易云音乐").right(className="android.widget.TextView").click()
断言
使用assert判断实际结果是否与预期结果一致
text = driver(resourceId="com.sankuai.meituan:id/passport_account_tips").get_text()
assert text == "账号或密码错误,请重新输入"
assert driver(resourceId="com.sankuai.meituan:id/grouth_tv").exists
对于没有焦点的,显示时间有限的提示框,使用toast 进行断言
tips = driver.toast.get_message()
assert "用户名或密码错误" in tips
案例演示
示例一:设置图形验证码并解锁,真机未能实现,无法进入解锁界面,可能是权限问题,用模拟器执行成功
import time
import uiautomator2 as u2
driver = u2.connect()
driver(text="设置").click()
driver(className="android.widget.LinearLayout").child_by_text("安全", allow_scroll_search=True,className="android.widget.TextView").click()
driver(resourceId="android:id/title", text="屏幕锁定").click()
driver.xpath('//*[@resource-id="com.android.settings:id/list"]/android.widget.LinearLayout[3]/android.widget.RelativeLayout[1]').click()
driver.swipe_points([(0.223, 0.658), (0.5, 0.832), (0.5, 0.652), (0.78, 0.487)], 0.2)
driver(resourceId="com.android.settings:id/footerRightButton").click()
driver.swipe_points([(0.223, 0.658), (0.5, 0.832), (0.5, 0.652), (0.78, 0.487)], 0.2)
driver(resourceId="com.android.settings:id/footerRightButton").click()
driver(resourceId="com.android.settings:id/redaction_done_button").click()
driver.press("power")
time.sleep(3)
driver.unlock()
driver.swipe_points([(0.273, 0.728), (0.5, 0.867), (0.5, 0.728), (0.719, 0.591)], 0.2)
assert driver(text="安全").exists
示例二:以美团APP为例,测试登录、查看订单、搜索商品相关操作,因存在大量定位元素操作,不使用框架就不便于维护,所以使用Pytest+Allure及PO模型实现此操作,结构如下图所示:
以登录为例,首先创建基类basepage.py 文件,封装一些公共方法,比如:定位、点击、输入、清除、获取文本信息、断言等
import re
class BasePage():
def __init__(self, driver):
self.driver = driver
def click(self, element):
if str(element).startswith("com"):
self.driver(resourceId=element).click()
elif re.findall("//", str(element)):
self.driver.xpath(element).click()
else:
self.driver(description=element).click()
def click_text(self, element):
self.driver(text=element).click()
def clear(self, element):
if str(element).startswith("com"):
self.driver(resourceId=element).clear_text()
elif re.findall("//", str(element)):
self.driver.xpath(element).clear_text()
else:
self.driver(description=element).clear_text()
def find_elements(self, element, timeout=5):
is_exited = False
try:
while timeout > 0:
xml = self.driver.dump_hierarchy()
if re.findall(element, xml):
is_exited = True
break
else:
timeout -= 1
except:
print("元素未找到!")
finally:
return is_exited
def assert_exited(self, element):
assert self.find_elements(element) == True, "断言失败,{}元素不存在!".format(element)
然后创建封装相应页面操作的方法,比如登录操作,创建login_page.py 文件,此层主要是封装操作流程
from base.basepage import BasePage
import allure
class LoginPage(BasePage):
agreement = "com.sankuai.meituan:id/permission_agree_btn"
get_loc_info = "com.android.permissioncontroller:id/permission_allow_foreground_only_button"
get_pho_perm = "com.android.permissioncontroller:id/permission_deny_button"
notice = "暂不"
login_in_now = "com.sankuai.meituan:id/button"
pwd_login = "com.sankuai.meituan:id/user_password_login"
username = "com.sankuai.meituan:id/passport_mobile_phone"
password = "com.sankuai.meituan:id/edit_password"
tick = "com.sankuai.meituan:id/passport_account_checkbox"
click_login = "com.sankuai.meituan:id/login_button"
mine = "我的"
growth = "com.sankuai.meituan:id/grouth_tv"
@allure.story('测试美团APP')
@allure.title("APP登录")
def login(self,user,pwd):
self.click(self.agreement)
self.click(self.get_loc_info)
self.click(self.get_pho_perm)
self.click_text(self.notice)
self.click(self.login_in_now)
self.click(self.pwd_login)
self.input(self.username,user)
self.input(self.password,pwd)
self.click(self.tick)
self.click(self.click_login)
self.click(self.mine)
self.assert_exited(self.growth)
self.click(self.homepage)
最后传参并执行用例操作,因用Pytest框架,所以此文件注意格式,文件名需以test开头,创建test_login.py 文件
import uiautomator2 as u2
import pytest
from pageobject.login_page import LoginPage
class TestLogin:
def test_login(self):
driver = u2.connect("48fd6742")
driver.app_start("com.sankuai.meituan", stop=True)
login_page = LoginPage(driver=driver)
login_page.login("16666666666","123456abc")
至此登录操作就算完成,执行测试就可以啦!
搜索操作也是相同的操作,把公共方法直接写入到基类文件中,操作流程单独创建search_page.py 文件
from base.basepage import BasePage
import allure
class SearchPage(BasePage):
inputfield = "com.sankuai.meituan:id/search_edit_flipper_container"
clicksearch = "com.sankuai.meituan:id/search"
inputvalue = "com.sankuai.meituan:id/search_edit"
back = "com.sankuai.meituan:id/back"
allure.title("搜索")
def search(self,value):
self.click(self.inputfield)
self.clear(self.inputvalue)
self.input(self.inputvalue,value)
self.click(self.clicksearch)
self.click(self.back)
self.click(self.back)
创建传参及执行搜索操作的文件test_search.py
import uiautomator2 as u2
import pytest
from pageobject.search_page import SearchPage
class TestSearch:
keyword = ["奶茶","桌球","景点"]
@pytest.mark.parametrize("value",keyword)
def test_search(self,value):
driver = u2.connect("48fd6742")
driver.app_start("com.sankuai.meituan")
search_page = SearchPage(driver=driver)
search_page.search(value)
使用allure执行测试并生成报告
pytest --alluredir=./result testcases
allure serve ./result
关于Pytest、Allure的使用请查看此文章
关于PO模型请查看此文章
|