首页 > 其他分享 >uiautomator2自动化测试

uiautomator2自动化测试

时间:2022-12-19 16:47:57浏览次数:65  
标签:widget text app 测试 自动化 print uiautomator2 com android

前言

最近公司想要做APP的UI自动化,安排我这边来做。这篇文章主要是作为总结和复习用。

总结

通过uiautomator2+unitest+数据库来实现app自动化,采用po模式,将app页面和业务逻辑分开,为每个页面封装一个方法,在测试用例中可以直接调用,而不涉及到具体的业务,从而提供代码的可维护性

官方教程

https://github.com/openatx/uiautomator2.git

安装

安装uiautomator2

uiautomator2是一个自动化测试开源工具,仅支持android平台的自动化测试,其封装了谷歌自带的uiautomator2测试框架,可以运行在支持Python的任一系统上

使用pip安装

pip install -U uiautomator2

对设备初始化

# init 所有的已经连接到电脑的设备
python -m uiautomator2 init

连接并启动

import uiautomator2 as u2
d = u2.connect()
# 启动app
d.app_start("com.tencent.android.qqdownloader")
# 打印设备信息
print(d.info)

安装weditor

weditor是一款基于浏览器的UI查看器,用来帮助我们查看UI元素定位。因为uiautomator是独占资源,所以当atx运行的时候uiautomatorviewer是不能用的,为了减少atx频繁的启停,就需要用到此工具

使用pip安装

pip install -U weditor

查看安装是否成功

weditor --help

运行weditor

python -m weditor

批量安装模块

创建requirements.txt文件,把所需要安装的模块都写进去,版本号可以为空,为空时自动最新版本
执行命令安装requirements.txt中的模块
pip install -r requirements.txt
已安装模块的导出
pip3 freeze >requirements.txt

使用

  • usb: d = u2.connect('10.0.0.1')
  • wifi :d = u2.connect('8c8a4d4d')

切换输入法

driver.set_fastinput_ime(False) # 关掉FastInputIME输入法,切换回系统默认输入法(此处华为手机默认输入法是华为Swype输入法)

具体实现脚本

import uiautomator2 as u2
import time
import adbutils

# 获取devices列表
devices = [d.serial for d in adbutils.adb.device_list()]


def appDown(device):
    """
    下载应用
    :param device:
    """
    # 建立连接
    d = u2.connect(device)
    # 启动应用商城
    d.app_start('com.tencent.android.qqdownloader')
    # 设置隐式等待
    d.implicitly_wait(20)
    # 打印设备信息
    deviceInfo = d.info
    print(deviceInfo)
    # 等待界面出现
    inputUiObj = d.xpath('//*[@resource-id="com.tencent.android.qqdownloader:id/awt"]')
    # 点击输入框
    inputUiObj.click()
    # 新输入框输入'淘宝'
    d.xpath('//*[@resource-id="com.tencent.android.qqdownloader:id/awt"]').set_text("淘宝")
    # 点击搜索
    d.xpath('//*[@resource-id="com.tencent.android.qqdownloader:id/a5t"]').click()
    # 安装第一个应用
    d.xpath('//*[@resource-id="com.tencent.android.qqdownloader:id/a78"]/android.widget.RelativeLayout['
            '1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout['
            '1]').click()
    time.sleep(5)
    # 关闭应用
    d.app_stop_all()

多线程并发执行脚本

import adbutils
import threading
from script_app.test_down import appDown
# 获取devices列表
devices = [d.serial for d in adbutils.adb.device_list()]
print(devices)


class myThread(threading.Thread):
    def __init__(self, threadID, devices):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.devices = devices

    def run(self):
        print("开启线程: " + self.devices)
        appDown(self.devices)


threadLock = threading.Lock()
threads = []

for device in devices:
    thread = myThread(1, device)
    thread.start()
    threads.append(thread)
# 等待所有线程完成
for t in threads:
    t.join()
print("退出主线程")

APP操作

  1. 获取前台应用 packageName, activity
    d.app_current()

  2. 启动应用( 默认的这种方法是先通过atx-agent解析apk包的mainActivity,然后调用am start -n $package/$activity启动)
    d.app_start("com.example.app")

  3. 通过指定main activity的方式启动应用,等价于调用am start -n com.example.hello_world/.MainActivity
    d.app_start("com.example.hello_world", ".MainActivity")

  4. 启动应用前停止此应用
    d.app_start("com.example.app", stop=True)

  5. 停止应用, 等价于am force-stop,此方法会丢失应用数据
    d.app_stop("com.example.app")

  6. 停止应用, 等价于pm clear
    d.app_clear('com.example.hello_world')

  7. 停止所有应用
    d.app_stop_all()

  8. 停止所有应用,除了某个应用
    d.app_stop_all(excludes=['com.examples.demo'])

  9. 得到APP图标

     img = d.app_icon("com.examples.demo")
     img.save("icon.png")
     列出所有运行中的应用
     d.app_list_running()
    
  10. 确定APP是否启动,也可以通过Session来判断

    pid = d.app_wait("com.example.android") # 等待应用运行, return pid(int)
    if not pid:
    print("com.example.android is not running")
    else:
    print("com.example.android pid is %d" % pid)
    
  11. 设置等待时间

    d.app_wait("com.example.android", front=True) # 等待应用前台运行
    d.app_wait("com.example.android", timeout=20.0) # 最长等待时间20s(默认)
    
    // or
    d.wait_activity(".ApiDemos", timeout=10) # default timeout 10.0 seconds
    
    

截图与hierarchy提取

截图

# take screenshot and save to a file on the computer, require Android>=4.2.
d.screenshot("home1.jpg")

# get PIL.Image formatted images. Naturally, you need pillow installed first
image = d.screenshot()  # default format="pillow"
image.save("home2.jpg")  # or home.png. Currently, only png and jpg are supported

# get raw jpeg data
imagebin = d.screenshot(format='raw')
open("some.jpg", "wb").write(imagebin)

获取页面元素

# get the UI hierarchy dump content (unicoded).
xml = d.dump_hierarchy()

查找(定位)元素

用driver(propertyParameter)去查找元素

# 获取元素
inputUiObj = d(resourceId="com.liinji.billingas.test:id/userName", className="android.widget.EditText")
# 返回的元素类型 -<class 'uiautomator2.session.UiObject'>
print("type(inputUiObj)=%s" % type(inputUiObj))
# 想要继续获取元素属性,则可以通过info
inputUiObjInfo = inputUiObj.info
# info类型为 -<class 'dict'>
print("type(inputUiObjInfo)=%s" % type(inputUiObjInfo))
print(inputUiObjInfo)
# 获取元素属性值
userName = inputUiObjInfo["text"]  # 请输入用户名
print(userName)

用driver.xpath(xpathSelector)去查找元素

# 获取元素
inputUiObj = d.xpath('//*[@resource-id="com.liinji.billingas.test:id/userName"]')
# 返回元素类型 -<class 'uiautomator2.xpath.XPathSelector'>
print("type(inputUiObj)=%s" % type(inputUiObj))
# 想要继续获取元素属性,则可以通过get
inputUiObjInfo = inputUiObj.get()
# get类型为 -<class 'uiautomator2.xpath.XMLElement'>
print("type(inputUiObjInfo)=%s" % type(inputUiObjInfo))
print(inputUiObjInfo)
# 然后才能去用inputXpathElem.attrib获取属性值
# xxx.attrib数据类型 -<class 'lxml.etree._Attrib'>
print("type(inputUiObjInfo.attrib)=%s" % type(inputUiObjInfo.attrib))
# 获取属性值
inputUserName = inputUiObjInfo.attrib['text']  # 请输入用户名
print(inputUserName)

模拟触控操作

通过元素文本属性来点击

# 点击通过元素文本,默认点击元素文本中心
d(text='请输入用户名').click(offset=(1, 1))  # 点击文本右下角
# 如果元素存在,就点击,超时时间默认0s
d(text='验证码登录').click_exists(timeout=10)

滑动操作

# 滑动界面
# 自带封装了swipe(),参数up,down,left,right分别代表上下左右滑动
d(text="Settings").swipe("right")
d(text="Settings").swipe("left", steps=10)
d(text="Settings").swipe("up", steps=20) # 1 steps is about 5ms, so 20 steps is about 0.1s
d(text="Settings").swipe("down", steps=20)
# steps参数:控制滑动时间
d.xpath('//*[@resource-id="com.liinji.billingas.test:id/userName"]').swipe('up')
d(text='验证码登录').swipe("up", steps=20)  # 1 steps is about 5ms, so 20 steps is about 0.1s
# 整个屏幕滑动
d.swipe_ext('right')
# 屏幕右滑,滑动距离为屏幕宽度的90%
d.swipe_ext("right", scale=0.9)

从一个坐标拖拽到另一个坐标

# 从一个坐标拖拽到另一个坐标
d.drag(10, 10, 20, 20)
d.drag(10, 10, 20, 20, duration=0.5)

模拟按下后的连续操作,如九宫格解锁

# 模拟按下后的连续操作,如九宫格解锁
d.touch.down(10, 10)  # 模拟按下
time.sleep(.01)  # down 和 move 之间的延迟,自己控制
d.touch.move(15, 15)  # 模拟移动
d.touch.up()  # 模拟抬起

模拟两指缩放操作

# 缩小
d(text='验证码登录').pinch_in(percent=100, steps=50)
# 放大
d(text='验证码登录').pinch_out(percent=100, steps=50)

硬按键操作

用于模拟用户对手机硬按键或系统按键的操作

模拟按 Home 或 Back 键

d.press("back") 
d.press("home") 

解锁屏幕

d.unlock()

模拟输入,需要光标已经在输入框中才可以

d.set_fastinput_ime(True) # 切换成FastInputIME输入法
d.send_keys("你好123abcEFG") # adb广播输入
d.clear_text() # 清除输入框所有内容(Require android-uiautomator.apk version >= 1.0.7)
d.set_fastinput_ime(False) # 切换成正常的输入法
d.send_action("search") # 模拟输入法的搜索

执行ADB shell命令

直接通过Python来执行ADB shell中的指令,并得到反馈。

执行shell命令,获取输出和exitCode

# 执行shell命令,获取输出和exitCode
output, exit_code = d.shell("ps -A", timeout=60)
# 仅得到输出
output = d.shell("pwd").output
# 仅得到Exitcode
exit_code = d.shell("pwd").exit_code

推送文件到ADB设备中

# push to a folder
d.push("foo.txt", "/sdcard/")
# push and rename
d.push("foo.txt", "/sdcard/bar.txt")
# push fileobj
with open("foo.txt", 'rb') as f:
   d.push(f, "/sdcard/")
# push and change file access mode
d.push("foo.sh", "/data/local/tmp/", mode=0o755)

获取文件到本地

d.pull("/sdcard/tmp.txt", "tmp.txt")

# FileNotFoundError will raise if the file is not found on the device
d.pull("/sdcard/some-file-not-exists.txt", "tmp.txt")

元素操作或Selector

这是Uiautomator2最为关键的核心功能,测试者可以根据界面中的元素来判断当前画面是否符合预期或基于界面元素进行点按滑动等操作

多属性组合,实现元素定位

# Select the object with text 'Clock' and its className is 'android.widget.TextView'
d(text='Clock', className='android.widget.TextView').click()

通过子节的和兄弟节点,实现定位

# children
# get the children or grandchildren
d(className="android.widget.ListView").child(text="Bluetooth")
# siblings
# get siblings
d(text="Google").sibling(className="android.widget.ImageView")

根据子节点的Text或 Description或Instance来定位元素

特别提下下面代码中的这个allow_scroll_search功能,它调用UT2自动滚动直到找到对应元素
根据子节点的Text定位

# get the child matching the condition className="android.widget.LinearLayout"
# and also its children or grandchildren with text "Bluetooth"
d(className="android.widget.ListView", resourceId="android:id/list") \
.child_by_text("Bluetooth", className="android.widget.LinearLayout")

allow_scroll_searc参数实现自动滚动,直到找到对应元素

# get children by allowing scroll search
d(className="android.widget.ListView", resourceId="android:id/list") \
.child_by_text(
   "Bluetooth",
   allow_scroll_search=True,
   className="android.widget.LinearLayout"
 )

等待某个元素出现

# advanced usage
d(text="Settings").exists(timeout=3) # wait Settings appear in 3s, same as .wait(3)

d.xpath("立即开户").wait() # 等待元素,最长等10s(默认)
d.xpath("立即开户").wait(timeout=10) # 修改默认等待时间

输入框的操作

d(text="Settings").get_text()  # get widget text
d(text="Settings").set_text("My text...")  # set the text
d(text="Settings").clear_text()  # clear the text

等待某个元素出现或消失

# wait until the ui object appears
d(text="Settings").wait(timeout=3.0) # return bool
# wait until the ui object gone
d(text="Settings").wait_gone(timeout=1.0)

守护

# 常用写法,注册匿名监控
d.watcher.when("安装").click()

# 注册名为ANR的监控,当出现ANR和Force Close时,点击Force Close
d.watcher("ANR").when(xpath="ANR").when("Force Close").click()

# 其他回调例子
d.watcher.when("抢红包").press("back")
d.watcher.when("//*[@text = 'Out of memory']").call(lambda d: d.shell('am force-stop com.im.qq'))

# 移除ANR的监控
d.watcher.remove("ANR")

# 移除所有的监控
d.watcher.remove()

# 开始后台监控
d.watcher.start()
d.watcher.start(2.0) # 默认监控间隔2.0s

# 强制运行所有监控
d.watcher.run()

# 停止监控
d.watcher.stop()

# 停止并移除所有的监控,常用于初始化
d.watcher.reset()

标签:widget,text,app,测试,自动化,print,uiautomator2,com,android
From: https://www.cnblogs.com/istart/p/16992487.html

相关文章

  • 什么是大数据测试
    大数据行业的诉求高质量,数据处理的正确性;高效率,数据处理的及时性;高可用,数据是否具有可恢复性;高主动性,分析产物是否满足业务需求。 大数据测试要了解的面?测试思维......
  • 基恩士SR710测试笔记 (同时获取两个条码)
    1,条码1配置入库  2,条码2配置入库  3,设置读取数量  4,分隔符设置  5,设置传送:下载至控制器  6,终端测试 ......
  • 登录功能的测试应该分析哪些方面-软件测试知识
    登陆功能是我们最常说的功能,但是这个功能也是相当复杂的功能,我们可以从功能性,安全性,性能,数据统计,4个方面来进行说明: 一、功能上:1、我们可以从登陆的用户名......
  • 软件测试工程师如何定位前端/后端BUG?
    软件测试工程师的职责是发现BUG,此外,如何体现个人价值?那么我们试想,只提出问题而不去解决,问题就永远得不到闭环。所以,一个资深的测试人员的基本功应该是这样的:深挖业务......
  • 如何做人工智能软件的冒烟测试-软件测试知识
    我们在进行普通软件测试的时候,第一步是要进行冒烟测试,那么在测试人工智能软件的时候第一步是要做什么呢?其实同样是要做冒烟测试,本文就通过两步给大家介绍怎样做人工智......
  • 从用户测试中学到的知识
    从客户那里获得良好的反馈是个挑战。用户测试有的时候看起来是一个艰巨而且昂贵的任务。但是用户测试可以带来良好的经验,从而帮助设计更好的产品。那么,从哪里开始呢......
  • 电动自行车出口美国亚马逊UL2849测试报告
    电动自行车安全测试标准UL2849报告电动自行车安全测试标准UL2849报告锂离子电池技术正不断广泛应用在诸如平衡车、电动踏板自行车(电动自行车)、电动摩托车和电动滑板车等个人......
  • 【软件测试】性能测试指标
    性能测试指标在线用户数:一个时间段内保持登录状态的用户数量并发用户数:相对:在同一时间段与服务器保持交互的用户数量绝对:在同一时间点向服务器发起交互的用户数量思......
  • nmap 安全工具简介、测试以及使用方法
    目录第一章.工具简介....3第二章.后门测试....4第三章.沙盒检测....6第四章.检测结论....8 第一章.工具简介官方网站:https://nmap.org/nmap是一个......
  • 验证码是自动化的天敌?看看大神是怎么解决的
     每天进步一点点,关注我们哦,每天分享测试技术文章本文章出自【码同学软件测试】码同学公众号:自动化软件测试,领取资料可加:magetest码同学抖音号:小码哥聊软件测试01验......