51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 922|回复: 0
打印 上一主题 下一主题

[原创] 自动化测试框架UIAutomator的介绍

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:05
  • 签到天数: 1023 天

    连续签到: 2 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-12-21 16:35:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    父子节点和兄弟节点选择
      父子节点
      # 后去子节点或孙子节点
      d(className="android.widget.RelativeLayout").child(text="蓝牙")

      d(className="android.widget.FrameLayout", resourceId="android:id/content") \
       .child_by_text("蓝牙", className="android.widget.TextView").click()


      # 通过允许滚动搜索获取子节点
      d(className="android.widget.FrameLayout", resourceId="android:id/content") \
       .child_by_text(
          "安全",
          allow_scroll_search=True,
          className="android.widget.TextView"
        ).click()


      兄弟节点
      d(text="无线和网络").sibling(className="android.view.ViewGroup")

      相对定位
      我们还可以使用相对定位方法来获取元素 left, right, top, bottom.
      ·d(A).left(B), 在A的左侧选择B
      · d(A).right(B), 在A的右侧选择B.
      · d(A).up(B), 在A的上部选择B
      · d(A).down(B), 在A是下部选择B
      so:
      d(text="更多").up(text="蓝牙").click()

      多元素(实例)选择
      有时屏幕可能包含多个具有相同属性的元素,例如文本,使用选择器中的“instance”属性来选择一个符合条件的实例。
      d(resourceId="com.android.settings:id/title",instance=0).click()

      此外,uiautomator2提供了一个类似列表的方法:
      print(d(resourceId="com.android.settings:id/title").count)  # 获取元素个数,也可以用 len(d(text="Add new")) 代替
      # 通过下标选择第几个元素
      d(resourceId="com.android.settings:id/title")[0].click()
      d(resourceId="com.android.settings:id/title")[1].click()

      for view in d(resourceId="com.android.settings:id/title"):
          print(view.info)


      判断元素状态及信息
      判断元素是否存在
      print(d(text="蓝牙").exists)  # True
      print(d.exists(text="蓝牙"))  # True  两种方法一样

      print(d(text="蓝牙").exists(timeout=3))  # True   设置超时时间


      获取特定UI对象的信息
      print(d(text="蓝牙").info)
      # {'bounds': {'bottom': 640, 'left': 187, 'right': 271, 'top': 583}, 'childCount': 0, 'className': 'android.widget.TextView', 'contentDescription': None, 'packageName': 'com.android.settings', 'resourceName': 'com.android.settings:id/title', 'text': '蓝牙', 'visibleBounds': {'bottom': 640, 'left': 187, 'right': 271, 'top': 583}, 'checkable': False, 'checked': False, 'clickable': False, 'enabled': True, 'focusable': False, 'focused': False, 'longClickable': False, 'scrollable': False, 'selected': False}


      获取/设置/清除可编辑字段的文本
      print(d(text="Settings").get_text()) # 获取可编辑字段值
      d(text="Settings").set_text("My text...")  # 设置/修改值
      d(text="My text...").clear_text()  # 清除值


      获取元素位置
      # 获取元素中心点
      x, y = d(text="蓝牙").center()
      print(x, y)   # 239.0 135.0
      x, y = d(text="蓝牙").center(offset=(0, 0)) # 左上角坐标值
      print(x, y)   # 187 583
      # 对获取到的元素截图
      im = d(text="蓝牙").screenshot()
      im.save("蓝牙.jpg")


      扩展选择器 XPath
      [url=]Java[/url] uiautoamtor中默认是不支持xpath的,所以这里属于扩展的一个功能。速度不是这么的快。
      常见用法:
      # wait exists 10s
      d.xpath("//android.widget.TextView").wait(10.0)
      # find and click
      d.xpath("//*[@content-desc='分享']").click()
      # check exists
      if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
          print("exists")
      # get all text-view text, attrib and center point
      for elem in d.xpath("//android.widget.TextView").all():
          print("Text:", elem.text)
          # Dictionary eg:
          # {'index': '1', 'text': '999+', 'resource-id': 'com.netease.cloudmusic:id/qb', 'package': 'com.netease.cloudmusic', 'content-desc': '', 'checkable': 'false', 'checked': 'false', 'clickable': 'false', 'enabled': 'true', 'focusable': 'false', 'focused': 'false','scrollable': 'false', 'long-clickable': 'false', 'password': 'false', 'selected': 'false', 'visible-to-user': 'true', 'bounds': '[661,1444][718,1478]'}
          print("Attrib:", elem.attrib)
          # Coordinate eg: (100, 200)
          print("Position:", elem.center())


      元素操作
      # 单击
      d(text="蓝牙").click()

      # 单击,等待超时时间为10s
      d(text="蓝牙").click(timeout=10)

      # 点击偏移量(x_offset, y_offset)
      # click_x = x_offset * width + x_left_top
      # click_y = y_offset * height + y_left_top
      d(text="蓝牙").click(offset=(0.5, 0.5)) # 默认值为中间
      d(text="蓝牙").click(offset=(0, 0)) # 点击左上角
      d(text="蓝牙").click(offset=(1, 1)) # 点击右下角

      # 单击,元素不存在不报错,返回布尔值,默认超时时间为0秒
      clicked = d(text='蓝牙').click_exists(timeout=10.0)
      print(clicked)

      # 点击直到元素消失,返回布尔值
      is_gone = d(text="蓝牙").click_gone(maxretry=10, interval=1.0) # 最大重试默认值为10,间隔默认值为1.0
      # 长按
      d(text="蓝牙").long_click()


      针对特定UI对象的手势操作
      元素拖拽
      # # 拖拽特定元素到相应坐标,拖动时间0.5s
      # d(text="蓝牙").drag_to(988, 610, duration=0.5)
      # # 拖拽特定元素到另一个元素位置,拖拽时间0.25s
      # d(text="蓝牙").drag_to(text="更多", duration=0.25)


      元素移动
      # # 元素滑动
      # d(text="蓝牙").swipe("right") # 元素右滑
      # d(text="蓝牙").swipe("left", steps=10)  # 元素左滑
      # d(text="蓝牙").swipe("up", steps=20) # 元素上滑滑 1 steps 大约 5ms, 因此 20 steps 大约 0.1s
      # d(text="蓝牙").swipe("down", steps=20) # 元素下滑
      #

      # 元素手势
      # 从一个点到另一个点的两点手势
      # d(text="蓝牙").gesture((988, 410), (988, 910), (588, 610), (288, 310))   # 注:[url=]测试[/url]中发现 text="蓝牙" 不起作用
      # d().gesture(startPoint1, startPoint2, endPoint1, endPoint2, steps)

      # # 元素移动
      # # 从元素的边缘到用心,移动百分之50,一定时间10ms
      # d(text="无线和网络").pinch_in(percent=50, steps=10)
      # # 从元素中心移到边缘
      # d(text="无线和网络").pinch_out()


      元素等待
      # # 查找等待元素,等待超时时间为3s(默认20s),返回布尔值
      # print(d(text="蓝牙").wait(timeout=3.0))  # True
      # # 等待元素消失,等待时间1s
      # print(d(text="蓝牙").wait_gone(timeout=1.0))  # False


      在特定的ui对象上滑动页面
      # # 页面滑动(手指滑动后松开)
      # # 垂直向前(手指向上)滑动
      # d(scrollable=True).fling()
      # # # 水平向前(手指向左)滑动
      # d(scrollable=True).fling.horiz.forward()
      # # 垂直向后(手指像下)滑动
      # d(scrollable=True).fling.vert.backward()
      # # 水平滑动到最开始的地方
      # d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
      # # 垂直滑动到尾部
      # d(scrollable=True).fling.toEnd()


      在特定的ui对象上滚动页面
      # # 页面滚动(手指一直在屏幕上)
      # # 垂直向前(手指向上)滚动
      # d(scrollable=True).scroll(steps=10)
      # # 水平向前(手指向左)滚动
      # d(scrollable=True).scroll.horiz.forward(steps=100)
      # # 垂直向后(手指像下)滚动
      # d(scrollable=True).scroll.vert.backward()
      # # 水平滚动到最开始的地方
      # d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)
      # # 垂直滚动到尾部
      # d(scrollable=True).scroll.toEnd()
      # # 垂直向前滚动,直到特定的UI对象出现
      # d(scrollable=True).scroll.to(text="Security")


      上下文监控
      with d.watch_context() as ctx:
          ctx.when("^立即(下载|更新)").when("取消").click()  # 当同时出现 (立即安装 或 立即取消)和 取消 按钮的时候,点击取消
          ctx.when("同意").click()
          ctx.when("确定").click()
          # 上面三行代码是立即执行完的,不会有什么等待

          ctx.wait_stable()  # 开启弹窗监控,并等待界面稳定(两个弹窗检查周期内没有弹窗代表稳定)

          # 使用call函数来触发函数回调
          # call 支持两个参数,d和el,不区分参数位置,可以不传参,如果传参变量名不能写错
          # eg: 当有元素匹配仲夏之夜,点击返回按钮
          ctx.when("仲夏之夜").call(lambda d: d.press("back"))
          ctx.when("确定").call(lambda el: el.click())

          # 其他操作

      # 为了方便也可以使用代码中默认的弹窗监控逻辑
      # 下面是目前内置的默认逻辑,可以加群at群主,增加新的逻辑,或者直接提pr
      # when("继续使用").click()
      # when("移入管控").when("取消").click()
      # when("^立即(下载|更新)").when("取消").click()
      # when("同意").click()
      # when("^(好的|确定)").click()
      with d.watch_context(builtin=True) as ctx:
          # 在已有的基础上增加
          ctx.when("@tb:id/jview_view").when('//*[@content-desc="图片"]').click()

          # 其他脚本逻辑


      全局设置
      d.HTTP_TIMEOUT = 60 # 默认值60s, http默认请求超时时间

      # 当设备掉线时,等待设备在线时长,仅当TMQ=true时有效,支持通过环境变量 WAIT_FOR_DEVICE_TIMEOUT 设置
      d.WAIT_FOR_DEVICE_TIMEOUT = 70


      其他的配置,目前已大部分集中到 d.settings 中。
      # 配置点击前延时0.5s,点击后延时1s
      d.settings['operation_delay'] = (.5, 1)
      # 修改延迟生效的方法
      # 其中 double_click, long_click 都对应click
      d.settings['operation_delay_methods'] = ['click', 'swipe', 'drag', 'press']

      d.settings['xpath_debug'] = True # 开启xpath插件的调试日志
      d.settings['wait_timeout'] = 20.0 # 默认控件等待时间(原生操作,xpath插件的等待时间)

      print(d.settings)


      [url=]UiAutomator[/url]中的超时设置(隐藏方法)
      print( d.jsonrpc.getConfigurator() )
      d.jsonrpc.setConfigurator({"waitForIdleTimeout": 100})


      Input method (没测试过)
      这种方法通常用于不知道控件的情况下的输入。第一步需要切换输入法,然后发送adb广播命令,具体使用方法如下:
      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") # 模拟输入法的搜索


      send_action 说明
      该函数可以使用的参数有 go search send next done previous
      什么时候该使用这个函数呢?
      有些时候在EditText中输入完内容之后,调用press("search") or press("enter")发现并没有什么反应。 这个时候就需要send_action函数了,这里用到了只有输入法才能用的IME_ACTION_CODE。 send_action先broadcast命令发送给输入法操作IME_ACTION_CODE,由输入法完成后续跟EditText的通信。
      消息提示框toast
      显示toast
      print(d.toast.show("Hello world"))   # True
      d.toast.show("Hello world", 5.0) # 显示5s,默认1s


      获取 toast
      # (Args)
      # 5.0:最大等待超时时间。默认的10.0
      # 10.0:缓存时间。如果toast已经在最近10秒内显示,则返回缓存toast。默认10.0(可能在将来更改)
      # "默认消息":如果最终没有得到toast则返回。默认没有
      print(d.toast.get_message(5.0, 10.0, "default message"))


      一般用法
      assert "Short message" in d.toast.get_message(5.0, default="")

      清除缓存的toast
      d.toast.reset()  # Now d.toast.get_message(0) is None

      视频录制
      这里没有使用[url=]手机[/url]中自带的screenrecord命令,是通过获取手机图片合成视频的方法,所以需要安装一些其他的依赖,如imageio, imageio-ffmpeg, numpy等 因为有些依赖比较大,推荐使用镜像安装。直接运行下面的命令即可。
      pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple

      使用方法:
      d.screenrecord('output.mp4')

      time.sleep(10)
      # or do something else

      d.screenrecord.stop() # 停止录制后,output.mp4文件才能打开


      图像匹配
      安装依赖:
      pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple

      imdata = "home.jpg" # 也可以是URL, PIL.Image或OpenCV打开的图像

      d.image.match(imdata)
      # 匹配待查找的图片,立刻返回一个结果
      # 返回一个dict, eg: {"similarity": 0.9, "point": [200, 300]}

      d.image.click(imdata, timeout=20.0)
      # 在20s的时间内调用match轮询查找图片,当similarity>0.9时,执行点击操作


      Stop UiAutomator
      停止UiAutomator守护服务。
      因为有atx-agent的存在,Uiautomator会被一直守护着,如果退出了就会被重新启动起来。但是Uiautomator又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的uiautomatorviewer 就都不能用了,除非关掉该框架本身的uiautomator。下面就说下两种关闭方法:
      d.uiautomator.stop()

      # d.uiautomator.start() # 启动
      print(d.uiautomator.running()) # 是否在运行



    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-9-25 04:39 , Processed in 0.070611 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表