../_images/logo-no-padding.png

Poco ポコ

Documentation Status chat on slack

A cross-engine UI automation framework. Unity3D/cocos2dx-*/Android native APP/(Other engines SDK)/…

示例(Example)

首先请把usb线连上Android手机并打开 adb调试模式

../_images/overview1.gif
# coding=utf-8

import time
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

poco('btn_start').click()
time.sleep(1.5)

shell = poco('shell').focus('center')
for star in poco('star'):
    star.drag_to(shell)
time.sleep(1)

assert poco('scoreVal').get_text() == "100", "score correct."
poco('btn_back', type='Button').click()

更多的例子请移步 More examples. 网易自研引擎项目请参考 网易游戏项目测试脚本标准模板 来开始建立回归测试工程。

编写脚本专用工具(Tools)

获取界面中的UI元素和开始编写脚本,请使用我们专门为您打造的 AirtestIDE 或轻量级的检视器 PocoHierarchyViewer网易自研引擎 请直接使用 Hunter内嵌inspector .

../_images/hunter-inspector.png

安装(Installation)

在电脑上安装poco,并把poco-sdk集成到游戏里

直接pip安装即可

pip install pocoui

SDK集成(Integration)

把sdk集成到游戏里请参考 Integration Guide网易自研引擎 无需嵌入sdk,请在hunter上直接配置,见 Integration Guide for NetEase

特性(Features)

  • 支持包括Unity3D, cocos2dx-*等主流引擎,也支持android原生应用
  • 在游戏运行时获取UI元素和层次结构(hierarchy)
  • 超快,不影响游戏性能
  • 5分钟完成就可以安装集成好
  • provides powerful APIs that are engine independent
  • 支持多点触控 fling/pinch/…
  • 支持gps、加速度感应器等模拟输入
  • 可以很方便地扩找到私有引擎中使用
  • python 2/3兼容

文档(Documentation)

Online docs.

如何使用(How to use Poco)

根据你所使用的引擎,按对应的方式初始化Poco实例。请先将Android手机连上电脑并启动好游戏/App,或者直接启动windows版并且不要让游戏后台运行。

下面例子列出几种常见引擎的初始化方式

  • Unity3D.
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()
# for windows
# poco = UnityPoco(('localhost', 5001), unity_editor=True)

ui = poco('...')
ui.click()
  • Android native APP
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

poco = AndroidUiautomationPoco()
poco.device.wake()
poco(text='Clock').click()
from poco.drivers.netease.internal import NeteasePoco
from airtest.core.api import connect_device

# 先连上android设备
connect_device('Android:///')

# windows的话这样
# connect_device('Windows:///?title_re=^.*errors and.*$')  # 填写可以识别出的窗口标题栏正则表达式,无需urlencode

poco = NeteasePoco('g37')  # hunter上的项目代号
ui = poco('...')
ui.click()

如果你PC/mac上同时连接了多个设备,请见 Poco drivers 这一章节

使用Poco选择UI对象

基本选择器(Basic Selector)

在poco实例后加一对括号就可以进行UI选择了。选择器会遍历所有UI,将满足给定条件的UI都选出来并返回。

括号里的参数就是所给定的条件,用属性名值对表示,其中第一个参数固定表示 节点名 其余可选参数均表示节点的属性及预期的属性值。下面的例子中可以感受一下选择表达式究竟是怎么样的。更详细的使用请参考 API Reference selecting UI

# select by node name
poco('bg_mission')

# select by name and other properties
poco('bg_mission', type='Button')
poco(textMatches='^据点.*$', type='Button', enable=True)
../_images/hunter-poco-select-simple.png

相对选择器(Relative Selector)

直接用节点属性没法选出你所想要的UI时,还可以通过UI之间的渲染层级关系进行选择,例如父子关系、兄弟关系、祖先后代关系。

# select by direct child/offspring
poco('main_node').child('list_item').offspring('item')
../_images/hunter-poco-select-relative.png

空间顺序选择器(Sequence Selector)

按照序号(顺序)进行选择总是按照空间排布顺序,先从左往右,再像之前那样一行一行从上到下,如下图中的数字标号,就是索引选择的序号。索引选择有个特例,一旦进行选择后,如果UI的位置发生了变化,那么下标序号仍然是按照选择的那一瞬间所确定的值。即,如果选择时1号UI现在去到了6号的位置,那么还是要用 poco(...)[1] 来访问,而不是6.如果选择了之后,某个UI消失了(从界面中移除或者隐藏了),那么如果再访问那个UI则可能会发生异常,其余的UI仍可继续访问。

items = poco('main_node').child('list_item').offspring('item')
print(items[0].child('material_name').get_text())
print(items[1].child('material_name').get_text())
../_images/hunter-poco-select-sequence.png

迭代一组对象(Iterate over a collection of objects)

下面代码片段展示如何迭代遍历一组UI

# traverse through every item
items = poco('main_node').child('list_item').offspring('item')
for item in items:
    item.child('icn_item')
../_images/hunter-poco-iteration.png

读取属性(Get object properties)

下面的例子展示如何通过代码获取UI的各种属性

mission_btn = poco('bg_mission')
print(mission_btn.attr('type'))  # 'Button'
print(mission_btn.get_text())  # '据点支援'
print(mission_btn.attr('text'))  # '据点支援' equivalent to .get_text()
print(mission_btn.exists())  # True/False, exists in the screen or not

全局操作(Global Operation)

在没有选定或指定UI的情况下也可以进行操作(模拟输入),也叫全局操作。

点击(click)

poco.click([0.5, 0.5])  # click the center of screen
poco.long_click([0.5, 0.5], duration=3)

滑动(swipe)

# swipe from A to B
point_a = [0.1, 0.1]
center = [0.5, 0.5]
poco.swipe(point_a, center)

# swipe from A by given direction
direction = [0.1, 0]
poco.swipe(point_a, direction=direction)

截屏(snapshot)

截屏幕并以base64编码返回。截图的格式(png, jpg, …)由对应的sdk实现决定,大多数情况下是png。详见 ScreenInterface.getScreen

Note: snapshot is not supported in some engine implementation of poco.

from base64 import b64decode

b64img, fmt = poco.snapshot(width=720)
open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))

异常处理(Exceptions)

本节说明如何处理异常

PocoTargetTimeout

from poco.exceptions import PocoTargetTimeout

try:
    poco('guide_panel', type='ImageView').wait_for_appearance()
except PocoTargetTimeout:
    # bugs here as the panel not shown
    raise

PocoNoSuchNodeException

from poco.exceptions import PocoNoSuchNodeException

img = poco('guide_panel', type='ImageView')
try:
    if not img.exists():
        img.click()
except PocoNoSuchNodeException:
    # If attempt to operate inexistent nodes, an exception will be thrown
    pass

单元测试(Unit Test)

Poco是自动化测试框架,不负责单元测试部分。如果想要进行系统地管理你的测试或编写更高级的测试代码,请参考我们的单元测试部分 PocoUnit. PocoUnit是一个提供了完善设施的专门为游戏和应用设计的单元测试框架,用法与python标准库 unittest 完全兼容。

Tutorial of PocoUnit.

术语解释(Some Concepts)

文档中可能会用到以下术语,在这里事先定义一下,避免误解和歧义。

  • Target device: test devices where the apps or games run on, it usually refers to mobile phone devices
  • UI proxy: proxy objects within Poco framework, they represent zero (none), one or multiple in-game UI elements
  • Node/UI element: UI element instances or nodes in app/game
  • 选择表达式 :除非你需要扩展或自定义更底层的Poco的行为,一般无需关心此。选择表达式是一种框架和sdk交互的底层数据结构,用在 Selector 类中。

下面的几个图形象地展示在Poco中层次结构(hierarchy)和UI属性的特征,从Inspector里也能很容易地预览当前界面的UI控件。

../_images/hunter-inspector.png ../_images/hunter-inspector-text-attribute.png ../_images/hunter-inspector-hierarchy-relations.png

坐标系与度量空间定义

归一化坐标系(Normalized Coordinate System)

归一化坐标系就是将屏幕宽和高按照单位一来算,这样UI在poco中的宽和高其实就是相对于屏幕的百分比大小了,好处就是不同分辨率设备之间,同一个UI的归一化坐标系下的位置和尺寸是一样的,有助于编写跨设备测试用例。

归一化坐标系的空间是均匀的,屏幕正中央一定是(0.5, 0.5),其他标量和向量的计算方法同欧式空间。

../_images/hunter-poco-coordinate-system.png

局部坐标系(Local Coordinate System (local positioning))

引入局部坐标系是为了表示相对于某UI的坐标。局部坐标系以UI包围盒左上角为原点,向右为x轴,向下为y轴,包围盒宽和高均为单位一。其余的定义和归一化坐标系类似。

局部坐标系可以更灵活地定位UI内或外的位置,例如(0.5, 0.5)就代表UI的正中央,超过1或小于0的坐标值则表示UI的外面。

Join to discuss!

chat on slack

Contributions

Any pull requests are welcomed! We will have the code checked carefully. Please make sure the codes are compatible with python 2/3 and have the same coding style.