python --automated testing UiAutomator2

install adb

After installing adb, use the command adb devicesto display the following figure;
insert image description here

Install python dependencies (uiautomator2,weditor)

pip install uiautomator2==2.16.23 weditor==0.6.8 -i https://pypi.doubanio.com/simple

# 在手机上安装 atx-agent 应用 

# 安装apk服务到手机上
python -m uiautomator2 init

Solve the error UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 825 when installing weditor

insert image description here

The script is as follows (can be copied and run directly)

import os
 
# https://pypi.douban.com/simple  # 豆瓣镜像
# https://pypi.tuna.tsinghua.edu.cn/simple  # 清华镜像
 
mirror = " -i https://pypi.douban.com/simple"
 
os.system("python -m pip install --upgrade pip" + mirror)  # 更新 pip
os.system("pip install --pre -U uiautomator2" + mirror)  # 安装 uiautomator2
os.system("pip install --pre weditor" + mirror)  # 安装 weditor
os.system("python -m uiautomator2 init")  #安装 atx-agent 至手机

run demo

import uiautomator2 as ui
import os
from loguru import logger

ui.DEBUG = False


class Connet(object):
    '''连接设备'''

    def __init__(self):
        self.devices_name_list: list = []  # 所有设备名称
        self.device: list = []  # 所有设备连接对象(每一个元素都是连接对象)
        os.environ['PATH'] += ';' + os.path.join(os.getcwd(), 'ADB')  # 设置adb为环境变量

    def _get_devices_name(self) -> None:
        '''提取设备名称'''
        devices_all = []
        for i in os.popen('adb devices').read().split('\n'):
            if i not in ('List of devices attached', ''):
                devices_all.append(i.split()[0])

        if len(devices_all) < 1:
            logger.error('检测不到设备')
        self.devices_name_list = devices_all

    def _connect(self):
        '''连接设备并提取设备对象至变量'''
        for device_name in self.devices_name_list:
            logger.warning(f'设备名称:【{device_name}】')
            self.device.append(ui.connect(device_name))
            logger.success(f'【{device_name}】...连接成功')

	def _start_vx(self):
        for device in self.device:
            try:
                device(scrollable=True).scroll.toEnd()   # 滑动至屏幕最底部
            except ui.exceptions.UiObjectNotFoundError as e:
                ...
            device(text='微信').click()  # 点击微信
           #  qujianma_assembly = device(resourceId="com.landicorp.jd.delivery:id/etPickUpCode")
           #  if not qujianma_assembly.exists:
           #      logger.error(f'找不到取件码组件,请切换至取件码页面')
           #     exit()

    def start(self, weishu):
        self._get_devices_name()
        self._connect()
        self._start_vx()
        
c = Connet()
c.start()

Detailed explanation of API methods

1.1 Device connection Device
connection is mainly divided into wired connection and wireless connection, as follows:

# 有线连接
d = u2.connect_usb(id)  # id 为 adb devices 命令中得到的设备 id
 
# 无线连接
d = u2.connect(ip)  # ip 为 手机 ip

Description: The returned d is the connection handle, and the operation on the mobile phone can be realized through d.

method name analyze
d.app_install(‘http://domain.com/xxx.apk’) Install the application 【Note】It can only be installed from the URL
d.app_start(“app_package_name”, stop=False) Opening the application (here is the package name) can also be used d(text='支付宝').click(); the stop parameter is whether to cold start, the default is False
d.app_stop(“app_package_name”) close application
d.app_stop_all() close all apps
d.implicitly_wait(20) Implicitly wait for 20s to ensure that the control is loaded (can be 全局set)
d.app_uninstall(‘package_name’) uninstall
d.app_current() Get the package name of the currently running app
d.app_info() Get app information
d.app_list_running() List the running apps, this can also get the package name
d.app_clear(‘paceage_name’) clear app data
d.info() Get basic device information
d.device_info() Get device details
d.window_size() Get device size
d.screenshot(‘d:/hello.png’) Get the screenshot of the device and transfer it to the computer storage path
d.push(‘d:/hello.png’,‘/data/’) Push file (upload file) The first parameter PC needs to upload the file path The second parameter is the storage path of the mobile terminal
d.pull(‘/data/hello.png’,‘d:/desktop/’) Pulling files is the opposite of pushing file parameters
d.click_post_delay = 1.5 全局Set a delay of 1.5 seconds between clicking again after each UI click默认无延迟
---------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
d(resourceId=element ID).click() ID positioning
d(text="Official Account: AirPython").click() Text text positioning
d(description=“AirPython”).click() Control Description Positioning
d(className=“android.widget.TextView”).click() The class to which the control belongs
d.xpath(“//*[@content-desc=‘AirPython’]”) Xpath positioning
d(className=“android.widget.ListView”, resourceId=元素ID) Combined positioning
d(text=‘hello’, className=‘android.widget.TextView’) Select the element whose text is 'hello' and className is 'android.widget.TextView'
d(text=“WiFi”).right(className=“android.widget.Switch”).click() 同d(text="WiFi").right().click()Select "switch" left, right, top, bottom on the right side of "WiFi"
d(text=“hello”, instance=0) Get the element object with "hello" in the first text同device(text="hello")[0]
------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
d.wait_timeout = 10 Set a global timeout of 10s只要设置了全局的超时时间,则其他的操作也会内置智能等待,不需要再进行任何操作
d.app_start(‘packagename’,wait=True) Turn on smart waiting, that is, the following code will be executed after the app is fully opened
d.wait_activity() wait for the page to load
d().wait() wait for element to appear
d().wait_gone() wait for element to disappear
d().exists() Wait for element to exist
--------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
d.click() click control支持xy坐标参数
d.double_click() Double click on the control支持xy坐标参数
d.long_click(duration=0.5) long press control支持xy坐标参数
d.set_text(text) enter text
d.send_keys(text) enter text广播式输入
d.set_fastinput_ime(True) open close input method为True打开 否则关闭
d.clear_text() clear textd[index].clear_text() 定位的控件有多个,通过 index 指定某一个
d.get_text() get text定位的控件只有一个,等价于 element[0].get_text()
--------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
device.screen_on() bright screen
device.screen_off() screen off
device.press(“home”) front page
device.press(“back”) return key
device.press(“left”) move left
device.press(“right”) move right
device.press(“up”) Swipe up
device.press(“down”) underline
device.press(“center”) back to middle page
device.press(“menu”) menu
device.press(“search”) search bar
device.press(“enter”) Enter key
device.press(“delete”) delete key
device.press(“recent”) open recent page
device.press(“camera”) open camera
device.press(“power”) Power button
device.press(“volume_up”) Turn up the volume
device.press(“volume_down”) turn down the volume
device.press(“volume_mute”) Mute
d.orientation Get the screen orientation, the value is {"natural", "left", "right", "upsidedown"}
d.freeze_rotation() lock screen orientation
d.freeze_rotation(True) Unlock screen orientation
d.set_orientation(“left”) turn left to landscaped.set_orientation("right") # 向右转为横屏
device.screenshot(“screenshot.png”)) Screen capture, support custom path
d.screenrecord.start(“screenrecord.mp4”) Screen recording needs to be from cv2d.screenrecord.stop() 停止录屏
device.open_notification() open notification bar

slide operation

device(scrollable=True).fling(steps=5)   飞滑5,默认为1 手势向上,页面向下(正常翻页)
device(scrollable=True).fling.horiz.toBeginning()  横滑  手势向右,页面向左
device(scrollable=True).fling.toEnd()    飞滑到页面最底部


device(scrollable=True).scroll(steps=100)   滑动滚动条 距离为100
device(scrollable=True).scroll.horiz.forward(steps=100)  手势向左 页面向右
device(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000) 手势向右 页面向左
device(scrollable=True).scroll.toEnd()  滑动到末尾
device(scrollable=True).scroll.to(text="Security")  scroll 向前垂直,直到出现指定ui object 

device.swipe(500,500,100,500)
"""
swipe 平滑 
    第一种方式:需要传入四个参数
        startX:起始X坐标
        startY:起始Y坐标
        endX:结束X坐标
        endY:结束Y坐标
   """

"""
    第二种方式:需要传入两个参数
        direction(滑动的方向) : up、down、left、right
        scale(滑动的百分比)
"""
device.swipe_ext('left',scale=0.9)


"""
    第三种方式:先进行元素定位 再滑动
        direction(滑动的方向) : up、down、left、right
        steps(滑动的速度) ms
        d.swipe(sx, sy, ex, ey, duration=0.5)
"""
e = device(text='appname')
e.swipe('left',steps=100)

Drag and drop

d.drag(sx, sy, ex, ey, duration=0.5)

appendix

#设置每次点击UI后再次单击之间延迟1.5秒
d.click_post_delay = 1.5
#设置默认元素等待超时(秒)
d.wait_timeout = 20








d.info
#得出设备链接信息
print(d.window_size())
#获取屏幕大小
print(d.current_app())
#获取当前应用的信息
print(d.serial)
#获取设备序列号
print(d.wlan_ip)
#获取WIFI IP
print(d.device_info)
#获取详细的设备信息







#打开/关闭屏幕
d.screen_on() 
#开启屏幕
d.screen_off() 
#关闭屏幕
d.info.get("screen") 
#获取屏幕开/关状态
#android>=4.4
d.press("home") 
#按下home键
d.press("back") 
#按下back键
d.press(0*07,0*02) 
#按下编码

'''支持按键模式'''
home
#主页按钮
back
#返回
left
#左
right
#右
up
#上
down
#下
center
#回车
menu
#菜单
search
#搜索
enter
#输入
delete ( or del)
#删除
recent (recent apps)
#打开最近项目
volume_up
#音量+
volume_down
#音量—
volume_mute
#静音
camera
#相机
power
#电源键










d.unlock()
#解锁屏幕
d.click(X,Y)
#点击屏幕坐标
d.long_click(x,y)
#长按屏幕
d.long_click(x,y,1)
#长按屏幕1s,默认是0.5,可自行配置
d.swipe(sx, sy, ex, ey)
#根据坐标滑动
d.swipe(sx, sy, ex, ey,1)
#根据坐标滑动,1代表滑动速度,默认0.5
d.drag(sx, sy, ex, ey)
#根据坐标拖动,适用于结算和滑块处理
d.drag(sx, sy, ex, ey, 1)
#根据坐标拖动,拖动时长1s,默认0.5


#截图
d.screenshot('1.jpg')
#截图保存在本地,文件名为1.jpg
#想获取其他格式的需要安装 pillow、numpy和cv2等库,具体不累述
d.open_notification()
#打开通知
d.open_quick_settings()
#打开快速设置
d.freeze_rotation() 
# 冻结旋转
d.freeze_rotation(False) 
# 开启旋转










'''检查特定的UI对象是否存在'''
d(text="Settings").exists 
# 返回布尔值,如果存在则为True,否则为False
d.exists(text="Settings") 
# 另一种写法
# 高级用法
d(text="Settings").exists(timeout=3) 
# 等待'Settings'在3秒钟出现
 d(text="Settings").info
# 获取特定UI对象的信息

'''获取/设置/清除可编辑字段的文本(例如EditText小部件)'''
d(text="Settings").get_text() 
#得到文本小部件
d(text="Settings").set_text("My text...") 
#设置文本
d(text="Settings").clear_text() 
#清除文本
d(text="Settings").center()
# 获取Widget中心点
#d(text="Settings").center(offset=(0, 0)) # 基准位置左前










d.push('1.txt','sdcard/downloacd')
#推送到文件下
d.push('1.txt','sdcard/downloacd/2.txt')
#推送并重命名到文件夹下
with open("foo.txt", 'rb') as f:
 d.push(f, "/sdcard/")
 #push fileobj
d.push("1.sh", "/data/local/tmp/", mode=0o755)
#推送并修改文件模式,在Python中表示八进制的友好方法默认0o755,文件权限设置
d.pull("/sdcard/1.txt", "1.txt")
#从设备侧拉取文件


'''定位方法'''
#text定位单击
d(text="Settings").click()
d(text="Settings", className="android.widget.TextView").click()
 
#resourceId定位单击
d(resourceId="com.ruguoapp.jike:id/tv_title", className="android.widget.TextView").click() 
 
#description定位单击
d(description="确定").click()
d(description="确定", className="android.widget.TextView").click()
 
#className定位单击
d(className="android.widget.TextView").click()
 
#xpath定位单击
d.xpath("//android.widget.FrameLayout[@index='0']/android.widget.LinearLayout[@index='0']").click()
 
#坐标单击
d.click(182, 1264)
















'''常用方法'''
# 等待10s
d.xpath("//android.widget.TextView").wait(10.0)
# 找到并单击
d.xpath("//*[@content-desc='分享']").click()
# 检查是否存在
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
 print("exists")
# 获取所有文本视图文本、属性和中心点
for elem in d.xpath("//android.widget.TextView").all():
 print("Text:", elem.text) 
#获取视图文本
for elem in d.xpath("//android.widget.TextView").all():
 print("Attrib:", elem.attrib)
#获取属性和中心点
#返回: (100, 200)
for elem in d.xpath("//android.widget.TextView").all():
 print("Position:", elem.center())
 
'''xpath常见用法'''
# 所有元素
//*
 
# resource-id包含login字符
//*[contains(@resource-id, 'login')]
 
# 按钮包含账号或帐号
//android.widget.Button[contains(@text, '账号') or contains(@text, '帐号')]
 
# 所有ImageView中的第二个
(//android.widget.ImageView)[2]
 
# 所有ImageView中的最后一个
(//android.widget.ImageView)[last()]
 
# className包含ImageView
//*[contains(name(), "ImageView")]
# 等待10s
d.xpath("//android.widget.TextView").wait(10.0)

# 找到并单击
d.xpath("//*[@content-desc='分享']").click()

# 检查是否存在
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
 print("exists")
 
# 获取所有文本视图文本、属性和中心点
for elem in d.xpath("//android.widget.TextView").all():
 print("Text:", elem.text)
 
#获取视图文本
for elem in d.xpath("//android.widget.TextView").all():
 print("Attrib:", elem.attrib)
 
#获取属性和中心点
#返回: (100, 200)
for elem in d.xpath("//android.widget.TextView").all():
 print("Position:", elem.center())


Guess you like

Origin blog.csdn.net/weixin_44634704/article/details/130998470