rtsf-app


Namertsf-app JSON
Version 1.2.4 PyPI version JSON
download
home_pagehttps://github.com/RockFeng0/rtsf-app
Summaryonly for android ui test, base on rtsf
upload_time2023-08-15 09:33:20
maintainer
docs_urlNone
author罗科峰
requires_python>=3.7
licenseMIT
keywords test android ui
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # rtsf-app
基于rtsf测试框架,关键字驱动Android UI,进行自动化的功能测试

1. 基本的使用,参见rtsf项目的 使用入门
2. rtsf-app遵循在rtsf项目高阶用法的约定
3. rtsf-app也就只做了3件事情
    - 设计APP UI自动化测试yaml用例,并重写Runner.run_test的执行规则
    - 封装常用的Appium方法,为用例提供yaml函数
    - 封装grid模式,支持命令行实现分布式部署

[查看rtsf项目用法](https://github.com/RockFeng0/rtsf)

# 环境准备

## window安装 appium.js
1. [下载安装node.js](https://nodejs.org/en)
2. 管理员权限,执行命令,安装cnpm: npm install -g cnpm --registry=https://registry.npm.taobao.org
3. 管理员权限,执行命令,安装appium: cnpm install appium -g
4. 安装完成后,验证appium: appium.cmd --command-timeout 120000 -p 4723 -U DEVICE_ID

> appium.cmd其实就是:  node "%appdata%\npm\node_modules\appium\build\lib\main.js" --command-timeout 120000 -p 4723 -U DEVICE_ID

命令启动,appium-server实例:
![appium-cmd.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/appium-cmd.png)

## 设置ANDROID_HOME环境变量
1. [下载simple_android_home](https://github.com/RockFeng0/rtsf-app/releases/tag/v1.0.39)
2. 解压文件android_home.zip,新增环境变量 ANDROID_HOME,为解压后的根目录的路径
3. 在环境变量path中,追加 %ANDROID_HOME%\platform-tools

> 如果你安装了  android SDK,并设置了 ANDROID_HOME, 确保 adb 和 aapt命令可以被调用

rtsf-app依赖的两个命令,如图:
![android-tools.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/android-tools.png)

## 下载selenium-server-standalone.jar

> 参见[rtsf-web](https://github.com/RockFeng0/rtsf-web)项目,环境准备栏,给出的下载链接

## 安装rtsf-app
pip install rtsf-app 

# 命令介绍

安装完成后,有两个命令用于执行yaml测试用例: 
- aldriver命令,android localhost driver,一般情况下,都是用这个命令执行yaml用例
- ardriver命令,android remote driver, 分布式部署的grid模式下,使用该命令运行yaml用例,它可以指定任意hub中的所有node机器,并在所有这些机器上运行用例。

安装完成后,有两个部署appium服务的命令:
- wrhub命令,开启grid hub,具体参见[rtsf-web](https://github.com/RockFeng0/rtsf-web)
- appserver命令,用于非grid模式下,启动appium server;在grid模式下,用于启动appium node

安装完成后,有一个工具命令:
- ainfo命令, 用于查看PC连接的android设备信息,以及查看待测试apk的报信息

## ainfo
1. 查看设备信息,其中注意关注, device_id 和  android_version(android device platform version)

格式为dict -> {device_id: {...}, device_id: {...}, 。。。} 

```
# PC中,执行ainfo命令,打印该PC连接的所有设备信息及设备属性
> ainfo
{'127.0.0.1:6555': {'ip': None, 'model': 'SAMSUNG-SM-N900A', 'cpu': 'x86', 'pad_version': 'hlteatt-userdebug 4.4.4 tt eng.jenkins.20171226.140228 release-keys', 'android_version': '4.4.4', 'android_api_version': '19', 'linux_version': 'Linux version 3.10.0+ (ttvm@TianTian-Dev) (gcc version 4.6 20120106 (prerelease) (GCC) ) #13 SMP PREEMPT Mon Dec 18 11:26:12 CST 2017'}}
```

2. 查看apk信息,其中主要关注,appPackage和appActivity 

```
# PC中,执行ainfo --apk APK_FILE 命令,查看apk信息
> ainfo --apk C:\ApiDemos-debug.apk
{'platformName': 'Android', 'deviceName': None, 'platformVersion': None, 'app': 'C:\\d_disk\\auto\\buffer\\test\\tools\\android\\ApiDemos-debug.apk', 'appPackage': 'io.appium.android.apis', 'appWaitPackage': 'io.appium.android.apis', 'appActivity': 'io.appium.android.apis.ApiDemos', 'unicodeKeyboard': True, 'resetKeyboard': True, 'newCommandTimeout': 120000}
```

ainfo命令实例:
![ainfo-cmd.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/ainfo-cmd.png)

## wrhub
如果有,并行的测试需求,我们会用到Grid模式,wrhub开启一个grid hub,允许不同测试node节点的接入

具体参见[rtsf-web](https://github.com/RockFeng0/rtsf-web)

## appserver

1. appserver提供简单的命令,为每一个待测试的手机,绑定一个端口,通过该端口,我们的测试用例,可以准确下发测试任务
2. appserver在绑定手机的同时,可以作为grid node接入grid模式

查看帮助: appserver -h
必填:
- 设置绑定设备监听的地址及端口:          e.g. 192.168.1.1:4723

选填:
- 设置绑定设备的device_id:         --device-name DEVICE_NAME
- 设置绑定设备的android_version:   --device-version DEVICE_VERSION
- grid模式,连接的hub iP:         --hub-ip HUB_IP
- grid模式,连接的hub port:       --hub-port HUB_PORT

appserver命令参数
![appserver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/appserver-h.png)

## aldriver
1. aldriver命令执行本地测试,该命令主动连接本地的4723端口,并驱动adb连接的第一个设备进行测试

查看帮助: aldriver -h

选填:
- 指定测试apk的本地路径,该参数会给手机重装app:        --apk APK
- 手机已装app,指定测试app的package名字:          --package PACKAGE
- 手机已装app,指定测试app的activity名字:         --activity ACTIVITY

aldriver命令参数:

![aldriver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/aldriver-h.png)

## ardriver
1. ardriver命令执行grid模式下,远程并行测试

注意:
- aldriver 与 ardriver的区别就在于: ardriver支持 ip和port参数,允许grid模式
- 在使用grid模式的时候, 如果使用 --apk参数,那么 确保该grid hub下的node手机,在该指定的文件路径中,存在这个apk。
- 在使用grid模式的时候,如果使用 --package和--activity参数,那么确保,连接到hub的node的手机,已经装了这个apk

ardriver命令参数:

![ardriver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/ardriver-h.png)

# rtsf-app的约定

依据rtsf的yaml约定模板,我们在steps中,为rtsf-app约定了一个规则,以便识别为Android UI自动化测试, 如下

```
steps:
    - appdriver:
        by: 
        value:
        index:
        timeout:
        action:
    - appdriver:
        action:
    ...
```
> action必填,其他选填
NativeApp的话,支持:("id","xpath","class name",'-android uiautomator'),
WebView的话,支持selenium所用方式

# rtsf-app常用的yaml函数

<!-- 注释, 不建议 使用 SetControl定位元素

###  AppElement methods -- 元素定位相关操作

<table>
    <tr>
        <th>AppElement methods</th>
        <th>参数介绍</th>
        <th>描述</th>
    </tr>
    <tr>
        <td>GetControl()</td>
        <td> </td>
        <td>获取element controls,返回字典,如:{"by":None,"value":None,"index":0,"timeout":10}</td>
    </tr>
    <tr>
        <td rowspan="4">SetControl(by,value,index,timeout)</td>
        <td>by: 指appium的寻找元素的方式:NativeApp支持("id","xpath","class name",'-android uiautomator'),WebView支持selenium所用方式,默认为None</td>
        <td rowspan="4">
                    1.依据app当前context,设置element controls,用于app元素的定位和控制<br/>
                    2. -android uiautomator是appium使用uiautomator中的UiSelector来定位元素,常用来使用文本定位元素,value值如 text("xxxx")
        </td>
    </tr>
    <tr>
        <td>value: 与by配对使用,相应by的值</td>
    </tr>
    <tr>
        <td>index: 索引值,默认为0,即第一个, 如果by,value组合找到很多元素,通过索引index指定一个</td>
    </tr>
    <tr>
       <td>timeout: 超时时间,默认10,即10秒,如果by,value组合寻找元素超过10秒,超时报错</td>
   </tr>    
</table>

-->


## App functions --> android设备-测试相关常用操作

```
LaunchApp()                                     # use current session to launch and active the app        
StartActivity(app_package,app_activity,timeout) # Only support android.  start an activity and focus to it. default timeout is 10 seconds
PageSource()                                    # page source for this activity
Forward()                                       # 类似浏览器的 前进
Back()                                          # 类似浏览器的 后退
Shake()                                         # 模拟设备摇晃 
BackgroundApp(seconds)                          # 应用会被放到后台特定时间,然后应用会重新回到前台 
OpenNotifications()                             # 打开通知栏
RemoveApp(app_package)                          # 卸载app
SwitchToDefaultContext()                        # 切换到默认上下文 
SwitchToNewContext()                            # 切换到新的上下文
Reset()                                         # 重置app, 即先closeApp然后在launchAPP
CloseApp()                                      # only close app . keep the session
QuitApp()                                       # will close the session
```

## AppContext methods --> 用于上下文管理
```
DyAttrData(name,attr)                       # -> 属性-动态存储变量,适用于,保存UI元素属性值。name-变量名称,attr为UI元素的属性名称,**配合SetControl使用**
DyActivityData(name)                        # -> 使用变量,保存当前app activity name
DyPackageData(name)                         # -> 使用变量,保存当前app package name
DyStrData(name, regx, index)                # -> 字符串-动态存储变量,适用于,保存页面html中指定的值。 name-变量名称,regx已编译的正则表达式,index指定索引,默认0


GetVar(name)                                # -> 获取指定变量的值
SetVar(name,value)                          # -> 设置指定变量的值
```

## AppWait methods --> 用于时间的控制
```
TimeSleep(seconds)                   # -> 指定等待时间(秒钟)
WaitForAppearing()                   # -> 等待元素出现(可能是隐藏,不可见的),**配合SetControl使用**
WaitForDisappearing()                # -> 等待元素消失,**配合SetControl使用**
WaitForVisible()                     # -> 等待元素可见,**配合SetControl使用**
```

## AppVerify methods --> 用于验证
```
VerifyVar(name, expect_value)                # -> 验证变量值,是期望的expect_value,返回True,否则返回False
VerifyAppInstalled(app_package)              # -> 验证app package name已经安装
VerifyCurrentActivity(app_activity)          # -> 验证当前app activity name是期望的app_activity
VerifyText(text)                             # -> 验证元素text属性值,为期望的text,**配合SetControl使用**
VerifyElemEnabled()                          # -> 验证元素是enabled,**配合SetControl使用**
VerifyElemNotEnabled()                       # -> 验证元素是Not Enabled, **配合SetControl使用**
VerifyElemVisible()                          # -> 验证元素是可见的, **配合SetControl使用**
VerifyElemNotVisible()                       # -> 验证元素是不可见的,**配合SetControl使用**
VerifyElemAttr(attr_name,expect_value)       # -> 验证元素属性attr_name的值,包含值expect_value,**配合SetControl使用**
VerifyElemCounts(num)                        # -> 验证元素数量为num,**配合SetControl使用**
```

## AppTouchAction methods --> 用于Android触摸操作
```
Tap()                        # -> 在指定元素上,轻触点击 1次,**配合SetControl使用**
LongPress()                  # -> 在指定元素上,长按,**配合SetControl使用**
Press()                      # -> 在指定元素上,按住不释放,**配合SetControl使用**
MoveTo()                     # -> 移动到指定元素上,**配合SetControl使用**
Release()                    # -> 在指定元素上,释放按住的操作,**配合SetControl使用**
Draw()                       # -> 在当前activity中,画画
Swipe(direction, times)      # -> 在当前activity中,滑动.direction滑动方向: up, down, left, right; times滑动次数,默认1次
```

## AppActions methods --> 用于Android常规操作
```
Pinch()                      # -> 在指定元素上缩小,**配合SetControl使用**
Zoom()                       # -> 在指定元素上放大,**配合SetControl使用**
SendKeys(value)              # -> 在指定元素上,输入文本值,**配合SetControl使用**, 继承自selenium,可用于WebView
click()                      # -> 在指定元素上,点击左键一次,**配合SetControl使用**, 继承自selenium,可用于WebView
```

> AppTouchAction和AppActions,封装较少的原因是考虑到Appium继承了selenium,因此有些appium提供的方法中,并不会同时兼容NativeApp和WebviewApp,同时,[rtsf-web](https://github.com/RockFeng0/rtsf-web)项目已经支持了selenium对web ui的测试。

# 自定义,yaml函数和变量

在case同级目录中,创建 preference.py, 该文件所定义的 变量、函数,可以被动态加载和引用, 具体参见rtsf的介绍

# 数据驱动与分层用例

在[rtsf](https://github.com/RockFeng0/rtsf)项目中,已经有了详细的介绍,rtsf-web也适用

# 场景实例

依据rtsf和rtsf-app的约定, 做了几个app ui测试的示例

## 简单实例

1. 编写一个yaml文件

```
# test_case.yaml
- project:
    name: ApiDemos项目
    module: 简单实例

- case:    
    name: android_app_ui_auto_test_demo_1

    glob_regx:
        rex_bar_title: 'Views/Controls/(.*)'

    glob_var:
        app_package: io.appium.android.apis
        app_main_activity: .ApiDemos
        app_view_webview_activity: .view.WebView1
        app_view_button_activity: .view.Buttons1
        app_view_control_activity: .view.Controls1
        app_view_dragdrop_activity: .view.DragAndDropDemo
        app_graphic_paint_activity: .graphics.TouchPaint
        app_animation_activity: .animation.BouncingBalls

    pre_command:
        - ${StartActivity($app_package, $app_view_control_activity)}
        - ${DyStrData(var_bar_title, $rex_bar_title)}
        - ${VerifyVar(var_bar_title, 1. Light Theme)}  

    steps:      

        - appdriver:
            by: id
            value: io.appium.android.apis:id/edit
            index: 0
            timeout: 10
            action: ${SendKeys(你好  -  hello)}

        - appdriver:
            action: ${TimeSleep(1)}

        - appdriver:
            by: -android uiautomator
            value: text("Checkbox 1")
            index: 0
            timeout: 10
            action: ${Tap()}

        - appdriver:
            action: ${VerifyElemAttr(checked, true)}

        - appdriver:
            action: ${Tap()}

        - appdriver:
            action: ${VerifyElemAttr(checked, false)}

        - appdriver:
            action: ${TimeSleep(1)} 

        - appdriver:
            action: ${Swipe(up, 1)}

        - appdriver:
            by: id
            value: android:id/text1            
            action: ${Tap()} 

        - appdriver:
            by: -android uiautomator
            value: 'text("Earth")'
            action: ${Tap()}

    post_command:
        - ${Back()}
        - ${CloseApp()}
```

2. 执行这个文件

```
# Terminal 1 监听本机4723端口
appserver 192.168.1.200:4723 

# Terminal 2 本地执行该用例
aldriver C:\test_case.yaml --apk C:\ApiDemos-debug.apk
```
[下载ApiDemos-debug.apk](https://github.com/RockFeng0/rtsf-app/releases/tag/v1.0.0)

## 详细实例 

### 场景一  本地测试

一般情况下,就是一台PC,连接一台设备的测试场景,步骤如下

1.测试场景假设

```
 apk(待测试的apk): C:\ApiDemos-debug.apk
 case(自动化测试用例): C:\test_case.yaml

 PC_A_IP(本机): 192.168.1.1
 PC_A_Android_Device_ID(天天模拟器): 127.0.0.1:6555
```

2.开启appium server,并绑定待测设备

```
# PC_A监听4723端口,绑定名字是127.0.0.1:6555并且版本为4.4.4的移动设备
# 注意,监听端口+1也会被占用
appserver 192.168.1.1:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4
```

3.aldriver驱动测试


**aldriver命令执行本地测试,该命令主动连接本地PC_A的4723端口,并驱动adb连接的第一个设备进行测试, 这就是为什么,在假设场景中,PC_A使用本机IP和4723端口**

```
# --apk参数会在移动设备中重装
aldriver C:\test_case.yaml --apk C:\ApiDemos-debug.apk
```

**你不想重装apk。通过ainfo获取到 appPackage和appActivity,执行下述命令**

```
# 原理相当于appium中的,start_activity(package, activity)
aldriver C:\test_case.yaml --package io.appium.android.apis --activity io.appium.android.apis.ApiDemos
```

**当然,你可以补全所有参数**

```
aldriver C:\test_case.yaml --apk C:\ApiDemos-debug.apk --package io.appium.android.apis --activity io.appium.android.apis.ApiDemos
```


4.释放端口占用

> ctrl + c 结束端口占用

场景一实例:
![scene-1.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/scene-1.png)

## 场景二 远程控制测试-Selenium Grid Mode

1.测试背景及分析

```
背景: 
    比如,手上有1000条相对独立的测试case,一台PC一台设备的方式完成这些case的验证,效率较低。那么,并行测试是最好的解决办法

分析: 
    1.多台PC连接多台设备的测试场景假设,其原理是基于selenium RC,使用selenium Grid的方式,使得appium server作为node节点,进行分布式测试
    2.可是,即使是分布式测试,它的过程也是一个并发的过程,每台设备分别都要测试1000条case。好比很多车在支路上跑,汇入的主干道却只有一条
    3.需要做的,就是让这1000条case,分配给这些设备,让它们并行测试。解决方法:多重hub
```

多台PC,连接多台设备,并行测试case场景,步骤如下

2.测试场景假设

```
 apk(待测试的apk): C:\ApiDemos-debug.apk
 case1(自动化测试用例): C:\test_case1.yaml
 case2(自动化测试用例): C:\test_case2.yaml
 ...

 PC_Server_IP(Grid Hub端): 192.168.1.254

 PC_A_IP(本机): 192.168.1.1
 PC_A_Android_Device_ID(天天模拟器): 127.0.0.1:6555
 ...

 PC_B_IP(远端机): 192.168.1.2
 PC_B_Android_Device_ID(天天模拟器): 127.0.0.1:6555
 ...

注意: adb.exe最多支持每台pc链接20台设备
并行测试: PC_A连接的所有机器,测试case1;PC_B连接的所有机器,测试case2
```

一个图,三种情况,理解分布式:
![grid-hub.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/grid-hub.png)

3.开启selenium grid hub

命令详解,参见[rtsf-web](https://github.com/RockFeng0/rtsf-web)

```
# PC_Server设置PC_A的hub
wrhub C:\selenium-server-standalone-3.14.0.jar --port 4444

# PC_Server设置PC_B的hub
wrhub C:\selenium-server-standalone-3.14.0.jar --port 5555
```

4.开启appium server node

```
# PC_A  4723端口绑定设备,并注册node节点 
appserver 192.168.1.1:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4 --hub-ip 192.168.1.254 --hub-port 4444

# PC_A  4725端口绑定设备,并注册node节点 
appserver 192.168.1.1:4725 --device-name DEVICE_ID --device-version DEVICE_VERSION --hub-ip 192.168.1.254 --hub-port 4444
...

# PC_B, 同理
appserver 192.168.1.2:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4 --hub-ip 192.168.1.254 --hub-port 5555
...
```

4.ardriver驱动测试

注意:
- 如果使用 --apk参数,那么 确保 PC A 和 PC B,在该指定的文件路径中,存在这个apk。
- 如果使用 --package和--activity参数,那么确保,连接到PC的手机,已经装了这个apk
- aldriver 与 ardriver的区别就在于: ardriver支持 ip和port参数,允许grid模式

```
# ardriver本身是个并发驱动测试,但是,每次使用都会开一个进程,并发的过程,就采用多次执行命令吧
# PC_A执行case1,执行case1的测试验证
> ardriver C:\test_case1.yaml --apk C:\ApiDemos-debug.apk --ip 192.168.1.254 --port 4444

# PC_B的所有设备,执行case2的测试验证
> ardriver C:\test_case2.yaml --apk C:\ApiDemos-debug.apk --ip 192.168.1.254 --port 5555

```

# 获取控件的工具
1. 如果是WebviewApp项目,工具推荐,就参考rtsf-web项目
2. 如果是NativeApp项目,您需要安装Android SDK, tools目录下,两个工具可以用于定位app元素:
    - uiautomatorviewer
    - hierarchyviewer

暂时没有 找到轻量好用的,用于定位的工具,如果您知道,请赐教



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/RockFeng0/rtsf-app",
    "name": "rtsf-app",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "test android ui",
    "author": "\u7f57\u79d1\u5cf0",
    "author_email": "lkf20031988@163.com",
    "download_url": "https://files.pythonhosted.org/packages/d5/35/28391b1a958b53d4ae1b2cdd9e4c59a477b2c6fd8f5f68f93b162f63435b/rtsf-app-1.2.4.tar.gz",
    "platform": null,
    "description": "# rtsf-app\n\u57fa\u4e8ertsf\u6d4b\u8bd5\u6846\u67b6\uff0c\u5173\u952e\u5b57\u9a71\u52a8Android UI,\u8fdb\u884c\u81ea\u52a8\u5316\u7684\u529f\u80fd\u6d4b\u8bd5\n\n1. \u57fa\u672c\u7684\u4f7f\u7528\uff0c\u53c2\u89c1rtsf\u9879\u76ee\u7684 \u4f7f\u7528\u5165\u95e8\n2. rtsf-app\u9075\u5faa\u5728rtsf\u9879\u76ee\u9ad8\u9636\u7528\u6cd5\u7684\u7ea6\u5b9a\n3. rtsf-app\u4e5f\u5c31\u53ea\u505a\u4e863\u4ef6\u4e8b\u60c5\n    - \u8bbe\u8ba1APP UI\u81ea\u52a8\u5316\u6d4b\u8bd5yaml\u7528\u4f8b\uff0c\u5e76\u91cd\u5199Runner.run_test\u7684\u6267\u884c\u89c4\u5219\n    - \u5c01\u88c5\u5e38\u7528\u7684Appium\u65b9\u6cd5\uff0c\u4e3a\u7528\u4f8b\u63d0\u4f9byaml\u51fd\u6570\n    - \u5c01\u88c5grid\u6a21\u5f0f\uff0c\u652f\u6301\u547d\u4ee4\u884c\u5b9e\u73b0\u5206\u5e03\u5f0f\u90e8\u7f72\n\n[\u67e5\u770brtsf\u9879\u76ee\u7528\u6cd5](https://github.com/RockFeng0/rtsf)\n\n# \u73af\u5883\u51c6\u5907\n\n## window\u5b89\u88c5 appium.js\n1. [\u4e0b\u8f7d\u5b89\u88c5node.js](https://nodejs.org/en)\n2. \u7ba1\u7406\u5458\u6743\u9650\uff0c\u6267\u884c\u547d\u4ee4\uff0c\u5b89\u88c5cnpm: npm install -g cnpm --registry=https://registry.npm.taobao.org\n3. \u7ba1\u7406\u5458\u6743\u9650\uff0c\u6267\u884c\u547d\u4ee4\uff0c\u5b89\u88c5appium: cnpm install appium -g\n4. \u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u9a8c\u8bc1appium: appium.cmd --command-timeout 120000 -p 4723 -U DEVICE_ID\n\n> appium.cmd\u5176\u5b9e\u5c31\u662f:  node \"%appdata%\\npm\\node_modules\\appium\\build\\lib\\main.js\" --command-timeout 120000 -p 4723 -U DEVICE_ID\n\n\u547d\u4ee4\u542f\u52a8\uff0cappium-server\u5b9e\u4f8b\uff1a\n![appium-cmd.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/appium-cmd.png)\n\n## \u8bbe\u7f6eANDROID_HOME\u73af\u5883\u53d8\u91cf\n1. [\u4e0b\u8f7dsimple_android_home](https://github.com/RockFeng0/rtsf-app/releases/tag/v1.0.39)\n2. \u89e3\u538b\u6587\u4ef6android_home.zip\uff0c\u65b0\u589e\u73af\u5883\u53d8\u91cf ANDROID_HOME\uff0c\u4e3a\u89e3\u538b\u540e\u7684\u6839\u76ee\u5f55\u7684\u8def\u5f84\n3. \u5728\u73af\u5883\u53d8\u91cfpath\u4e2d\uff0c\u8ffd\u52a0 %ANDROID_HOME%\\platform-tools\n\n> \u5982\u679c\u4f60\u5b89\u88c5\u4e86  android SDK\uff0c\u5e76\u8bbe\u7f6e\u4e86 ANDROID_HOME, \u786e\u4fdd adb \u548c aapt\u547d\u4ee4\u53ef\u4ee5\u88ab\u8c03\u7528\n\nrtsf-app\u4f9d\u8d56\u7684\u4e24\u4e2a\u547d\u4ee4\uff0c\u5982\u56fe\uff1a\n![android-tools.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/android-tools.png)\n\n## \u4e0b\u8f7dselenium-server-standalone.jar\n\n> \u53c2\u89c1[rtsf-web](https://github.com/RockFeng0/rtsf-web)\u9879\u76ee\uff0c\u73af\u5883\u51c6\u5907\u680f\uff0c\u7ed9\u51fa\u7684\u4e0b\u8f7d\u94fe\u63a5\n\n## \u5b89\u88c5rtsf-app\npip install rtsf-app \n\n# \u547d\u4ee4\u4ecb\u7ecd\n\n\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u6709\u4e24\u4e2a\u547d\u4ee4\u7528\u4e8e\u6267\u884cyaml\u6d4b\u8bd5\u7528\u4f8b: \n- aldriver\u547d\u4ee4\uff0candroid localhost driver\uff0c\u4e00\u822c\u60c5\u51b5\u4e0b\uff0c\u90fd\u662f\u7528\u8fd9\u4e2a\u547d\u4ee4\u6267\u884cyaml\u7528\u4f8b\n- ardriver\u547d\u4ee4\uff0candroid remote driver\uff0c \u5206\u5e03\u5f0f\u90e8\u7f72\u7684grid\u6a21\u5f0f\u4e0b\uff0c\u4f7f\u7528\u8be5\u547d\u4ee4\u8fd0\u884cyaml\u7528\u4f8b\uff0c\u5b83\u53ef\u4ee5\u6307\u5b9a\u4efb\u610fhub\u4e2d\u7684\u6240\u6709node\u673a\u5668\uff0c\u5e76\u5728\u6240\u6709\u8fd9\u4e9b\u673a\u5668\u4e0a\u8fd0\u884c\u7528\u4f8b\u3002\n\n\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u6709\u4e24\u4e2a\u90e8\u7f72appium\u670d\u52a1\u7684\u547d\u4ee4\uff1a\n- wrhub\u547d\u4ee4\uff0c\u5f00\u542fgrid hub\uff0c\u5177\u4f53\u53c2\u89c1[rtsf-web](https://github.com/RockFeng0/rtsf-web)\n- appserver\u547d\u4ee4\uff0c\u7528\u4e8e\u975egrid\u6a21\u5f0f\u4e0b\uff0c\u542f\u52a8appium server\uff1b\u5728grid\u6a21\u5f0f\u4e0b\uff0c\u7528\u4e8e\u542f\u52a8appium node\n\n\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u6709\u4e00\u4e2a\u5de5\u5177\u547d\u4ee4\uff1a\n- ainfo\u547d\u4ee4\uff0c \u7528\u4e8e\u67e5\u770bPC\u8fde\u63a5\u7684android\u8bbe\u5907\u4fe1\u606f\uff0c\u4ee5\u53ca\u67e5\u770b\u5f85\u6d4b\u8bd5apk\u7684\u62a5\u4fe1\u606f\n\n## ainfo\n1. \u67e5\u770b\u8bbe\u5907\u4fe1\u606f\uff0c\u5176\u4e2d\u6ce8\u610f\u5173\u6ce8\uff0c device_id \u548c  android_version(android device platform version)\n\n\u683c\u5f0f\u4e3adict -> {device_id: {...}, device_id: {...}, \u3002\u3002\u3002} \n\n```\n# PC\u4e2d\uff0c\u6267\u884cainfo\u547d\u4ee4\uff0c\u6253\u5370\u8be5PC\u8fde\u63a5\u7684\u6240\u6709\u8bbe\u5907\u4fe1\u606f\u53ca\u8bbe\u5907\u5c5e\u6027\n> ainfo\n{'127.0.0.1:6555': {'ip': None, 'model': 'SAMSUNG-SM-N900A', 'cpu': 'x86', 'pad_version': 'hlteatt-userdebug 4.4.4 tt eng.jenkins.20171226.140228 release-keys', 'android_version': '4.4.4', 'android_api_version': '19', 'linux_version': 'Linux version 3.10.0+ (ttvm@TianTian-Dev) (gcc version 4.6 20120106 (prerelease) (GCC) ) #13 SMP PREEMPT Mon Dec 18 11:26:12 CST 2017'}}\n```\n\n2. \u67e5\u770bapk\u4fe1\u606f\uff0c\u5176\u4e2d\u4e3b\u8981\u5173\u6ce8\uff0cappPackage\u548cappActivity \n\n```\n# PC\u4e2d\uff0c\u6267\u884cainfo --apk APK_FILE \u547d\u4ee4\uff0c\u67e5\u770bapk\u4fe1\u606f\n> ainfo --apk C:\\ApiDemos-debug.apk\n{'platformName': 'Android', 'deviceName': None, 'platformVersion': None, 'app': 'C:\\\\d_disk\\\\auto\\\\buffer\\\\test\\\\tools\\\\android\\\\ApiDemos-debug.apk', 'appPackage': 'io.appium.android.apis', 'appWaitPackage': 'io.appium.android.apis', 'appActivity': 'io.appium.android.apis.ApiDemos', 'unicodeKeyboard': True, 'resetKeyboard': True, 'newCommandTimeout': 120000}\n```\n\nainfo\u547d\u4ee4\u5b9e\u4f8b\uff1a\n![ainfo-cmd.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/ainfo-cmd.png)\n\n## wrhub\n\u5982\u679c\u6709\uff0c\u5e76\u884c\u7684\u6d4b\u8bd5\u9700\u6c42\uff0c\u6211\u4eec\u4f1a\u7528\u5230Grid\u6a21\u5f0f\uff0cwrhub\u5f00\u542f\u4e00\u4e2agrid hub\uff0c\u5141\u8bb8\u4e0d\u540c\u6d4b\u8bd5node\u8282\u70b9\u7684\u63a5\u5165\n\n\u5177\u4f53\u53c2\u89c1[rtsf-web](https://github.com/RockFeng0/rtsf-web)\n\n## appserver\n\n1. appserver\u63d0\u4f9b\u7b80\u5355\u7684\u547d\u4ee4\uff0c\u4e3a\u6bcf\u4e00\u4e2a\u5f85\u6d4b\u8bd5\u7684\u624b\u673a\uff0c\u7ed1\u5b9a\u4e00\u4e2a\u7aef\u53e3\uff0c\u901a\u8fc7\u8be5\u7aef\u53e3\uff0c\u6211\u4eec\u7684\u6d4b\u8bd5\u7528\u4f8b\uff0c\u53ef\u4ee5\u51c6\u786e\u4e0b\u53d1\u6d4b\u8bd5\u4efb\u52a1\n2. appserver\u5728\u7ed1\u5b9a\u624b\u673a\u7684\u540c\u65f6\uff0c\u53ef\u4ee5\u4f5c\u4e3agrid node\u63a5\u5165grid\u6a21\u5f0f\n\n\u67e5\u770b\u5e2e\u52a9: appserver -h\n\u5fc5\u586b:\n- \u8bbe\u7f6e\u7ed1\u5b9a\u8bbe\u5907\u76d1\u542c\u7684\u5730\u5740\u53ca\u7aef\u53e3\uff1a          e.g. 192.168.1.1:4723\n\n\u9009\u586b\uff1a\n- \u8bbe\u7f6e\u7ed1\u5b9a\u8bbe\u5907\u7684device_id:         --device-name DEVICE_NAME\n- \u8bbe\u7f6e\u7ed1\u5b9a\u8bbe\u5907\u7684android_version:   --device-version DEVICE_VERSION\n- grid\u6a21\u5f0f\uff0c\u8fde\u63a5\u7684hub iP:         --hub-ip HUB_IP\n- grid\u6a21\u5f0f\uff0c\u8fde\u63a5\u7684hub port:       --hub-port HUB_PORT\n\nappserver\u547d\u4ee4\u53c2\u6570\n![appserver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/appserver-h.png)\n\n## aldriver\n1. aldriver\u547d\u4ee4\u6267\u884c\u672c\u5730\u6d4b\u8bd5\uff0c\u8be5\u547d\u4ee4\u4e3b\u52a8\u8fde\u63a5\u672c\u5730\u76844723\u7aef\u53e3\uff0c\u5e76\u9a71\u52a8adb\u8fde\u63a5\u7684\u7b2c\u4e00\u4e2a\u8bbe\u5907\u8fdb\u884c\u6d4b\u8bd5\n\n\u67e5\u770b\u5e2e\u52a9: aldriver -h\n\n\u9009\u586b\uff1a\n- \u6307\u5b9a\u6d4b\u8bd5apk\u7684\u672c\u5730\u8def\u5f84\uff0c\u8be5\u53c2\u6570\u4f1a\u7ed9\u624b\u673a\u91cd\u88c5app:        --apk APK\n- \u624b\u673a\u5df2\u88c5app\uff0c\u6307\u5b9a\u6d4b\u8bd5app\u7684package\u540d\u5b57:          --package PACKAGE\n- \u624b\u673a\u5df2\u88c5app\uff0c\u6307\u5b9a\u6d4b\u8bd5app\u7684activity\u540d\u5b57:         --activity ACTIVITY\n\naldriver\u547d\u4ee4\u53c2\u6570:\n\n![aldriver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/aldriver-h.png)\n\n## ardriver\n1. ardriver\u547d\u4ee4\u6267\u884cgrid\u6a21\u5f0f\u4e0b\uff0c\u8fdc\u7a0b\u5e76\u884c\u6d4b\u8bd5\n\n\u6ce8\u610f:\n- aldriver \u4e0e ardriver\u7684\u533a\u522b\u5c31\u5728\u4e8e: ardriver\u652f\u6301 ip\u548cport\u53c2\u6570\uff0c\u5141\u8bb8grid\u6a21\u5f0f\n- \u5728\u4f7f\u7528grid\u6a21\u5f0f\u7684\u65f6\u5019\uff0c \u5982\u679c\u4f7f\u7528 --apk\u53c2\u6570\uff0c\u90a3\u4e48 \u786e\u4fdd\u8be5grid hub\u4e0b\u7684node\u624b\u673a,\u5728\u8be5\u6307\u5b9a\u7684\u6587\u4ef6\u8def\u5f84\u4e2d\uff0c\u5b58\u5728\u8fd9\u4e2aapk\u3002\n- \u5728\u4f7f\u7528grid\u6a21\u5f0f\u7684\u65f6\u5019\uff0c\u5982\u679c\u4f7f\u7528 --package\u548c--activity\u53c2\u6570\uff0c\u90a3\u4e48\u786e\u4fdd\uff0c\u8fde\u63a5\u5230hub\u7684node\u7684\u624b\u673a\uff0c\u5df2\u7ecf\u88c5\u4e86\u8fd9\u4e2aapk\n\nardriver\u547d\u4ee4\u53c2\u6570:\n\n![ardriver-h.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/ardriver-h.png)\n\n# rtsf-app\u7684\u7ea6\u5b9a\n\n\u4f9d\u636ertsf\u7684yaml\u7ea6\u5b9a\u6a21\u677f\uff0c\u6211\u4eec\u5728steps\u4e2d\uff0c\u4e3artsf-app\u7ea6\u5b9a\u4e86\u4e00\u4e2a\u89c4\u5219\uff0c\u4ee5\u4fbf\u8bc6\u522b\u4e3aAndroid UI\u81ea\u52a8\u5316\u6d4b\u8bd5\uff0c \u5982\u4e0b\n\n```\nsteps:\n    - appdriver:\n        by: \n        value:\n        index:\n        timeout:\n        action:\n    - appdriver:\n        action:\n    ...\n```\n> action\u5fc5\u586b\uff0c\u5176\u4ed6\u9009\u586b\nNativeApp\u7684\u8bdd\uff0c\u652f\u6301\uff1a(\"id\",\"xpath\",\"class name\",'-android uiautomator')\uff0c\nWebView\u7684\u8bdd\uff0c\u652f\u6301selenium\u6240\u7528\u65b9\u5f0f\n\n# rtsf-app\u5e38\u7528\u7684yaml\u51fd\u6570\n\n<!-- \u6ce8\u91ca\uff0c \u4e0d\u5efa\u8bae \u4f7f\u7528 SetControl\u5b9a\u4f4d\u5143\u7d20\n\n###  AppElement methods -- \u5143\u7d20\u5b9a\u4f4d\u76f8\u5173\u64cd\u4f5c\n\n<table>\n    <tr>\n        <th>AppElement methods</th>\n        <th>\u53c2\u6570\u4ecb\u7ecd</th>\n        <th>\u63cf\u8ff0</th>\n    </tr>\n    <tr>\n        <td>GetControl()</td>\n        <td> </td>\n        <td>\u83b7\u53d6element controls,\u8fd4\u56de\u5b57\u5178\uff0c\u5982\uff1a{\"by\":None,\"value\":None,\"index\":0,\"timeout\":10}</td>\n    </tr>\n    <tr>\n        <td rowspan=\"4\">SetControl(by,value,index,timeout)</td>\n        <td>by: \u6307appium\u7684\u5bfb\u627e\u5143\u7d20\u7684\u65b9\u5f0f:NativeApp\u652f\u6301(\"id\",\"xpath\",\"class name\",'-android uiautomator')\uff0cWebView\u652f\u6301selenium\u6240\u7528\u65b9\u5f0f\uff0c\u9ed8\u8ba4\u4e3aNone</td>\n        <td rowspan=\"4\">\n                    1.\u4f9d\u636eapp\u5f53\u524dcontext\uff0c\u8bbe\u7f6eelement controls\uff0c\u7528\u4e8eapp\u5143\u7d20\u7684\u5b9a\u4f4d\u548c\u63a7\u5236<br/>\n                    2. -android uiautomator\u662fappium\u4f7f\u7528uiautomator\u4e2d\u7684UiSelector\u6765\u5b9a\u4f4d\u5143\u7d20\uff0c\u5e38\u7528\u6765\u4f7f\u7528\u6587\u672c\u5b9a\u4f4d\u5143\u7d20\uff0cvalue\u503c\u5982 text(\"xxxx\")\n        </td>\n    </tr>\n    <tr>\n        <td>value: \u4e0eby\u914d\u5bf9\u4f7f\u7528\uff0c\u76f8\u5e94by\u7684\u503c</td>\n    </tr>\n    <tr>\n        <td>index: \u7d22\u5f15\u503c\uff0c\u9ed8\u8ba4\u4e3a0\uff0c\u5373\u7b2c\u4e00\u4e2a\uff0c \u5982\u679cby,value\u7ec4\u5408\u627e\u5230\u5f88\u591a\u5143\u7d20\uff0c\u901a\u8fc7\u7d22\u5f15index\u6307\u5b9a\u4e00\u4e2a</td>\n    </tr>\n    <tr>\n       <td>timeout: \u8d85\u65f6\u65f6\u95f4\uff0c\u9ed8\u8ba410\uff0c\u537310\u79d2\uff0c\u5982\u679cby,value\u7ec4\u5408\u5bfb\u627e\u5143\u7d20\u8d85\u8fc710\u79d2\uff0c\u8d85\u65f6\u62a5\u9519</td>\n   </tr>    \n</table>\n\n-->\n\n\n## App functions --> android\u8bbe\u5907-\u6d4b\u8bd5\u76f8\u5173\u5e38\u7528\u64cd\u4f5c\n\n```\nLaunchApp()                                     # use current session to launch and active the app        \nStartActivity(app_package,app_activity,timeout) # Only support android.  start an activity and focus to it. default timeout is 10 seconds\nPageSource()                                    # page source for this activity\nForward()                                       # \u7c7b\u4f3c\u6d4f\u89c8\u5668\u7684 \u524d\u8fdb\nBack()                                          # \u7c7b\u4f3c\u6d4f\u89c8\u5668\u7684 \u540e\u9000\nShake()                                         # \u6a21\u62df\u8bbe\u5907\u6447\u6643 \nBackgroundApp(seconds)                          # \u5e94\u7528\u4f1a\u88ab\u653e\u5230\u540e\u53f0\u7279\u5b9a\u65f6\u95f4,\u7136\u540e\u5e94\u7528\u4f1a\u91cd\u65b0\u56de\u5230\u524d\u53f0 \nOpenNotifications()                             # \u6253\u5f00\u901a\u77e5\u680f\nRemoveApp(app_package)                          # \u5378\u8f7dapp\nSwitchToDefaultContext()                        # \u5207\u6362\u5230\u9ed8\u8ba4\u4e0a\u4e0b\u6587 \nSwitchToNewContext()                            # \u5207\u6362\u5230\u65b0\u7684\u4e0a\u4e0b\u6587\nReset()                                         # \u91cd\u7f6eapp, \u5373\u5148closeApp\u7136\u540e\u5728launchAPP\nCloseApp()                                      # only close app . keep the session\nQuitApp()                                       # will close the session\n```\n\n## AppContext methods --> \u7528\u4e8e\u4e0a\u4e0b\u6587\u7ba1\u7406\n```\nDyAttrData(name,attr)                       # -> \u5c5e\u6027-\u52a8\u6001\u5b58\u50a8\u53d8\u91cf\uff0c\u9002\u7528\u4e8e\uff0c\u4fdd\u5b58UI\u5143\u7d20\u5c5e\u6027\u503c\u3002name-\u53d8\u91cf\u540d\u79f0\uff0cattr\u4e3aUI\u5143\u7d20\u7684\u5c5e\u6027\u540d\u79f0\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nDyActivityData(name)                        # -> \u4f7f\u7528\u53d8\u91cf,\u4fdd\u5b58\u5f53\u524dapp activity name\nDyPackageData(name)                         # -> \u4f7f\u7528\u53d8\u91cf,\u4fdd\u5b58\u5f53\u524dapp package name\nDyStrData(name, regx, index)                # -> \u5b57\u7b26\u4e32-\u52a8\u6001\u5b58\u50a8\u53d8\u91cf\uff0c\u9002\u7528\u4e8e\uff0c\u4fdd\u5b58\u9875\u9762html\u4e2d\u6307\u5b9a\u7684\u503c\u3002 name-\u53d8\u91cf\u540d\u79f0\uff0cregx\u5df2\u7f16\u8bd1\u7684\u6b63\u5219\u8868\u8fbe\u5f0f\uff0cindex\u6307\u5b9a\u7d22\u5f15\uff0c\u9ed8\u8ba40\n\n\nGetVar(name)                                # -> \u83b7\u53d6\u6307\u5b9a\u53d8\u91cf\u7684\u503c\nSetVar(name,value)                          # -> \u8bbe\u7f6e\u6307\u5b9a\u53d8\u91cf\u7684\u503c\n```\n\n## AppWait methods --> \u7528\u4e8e\u65f6\u95f4\u7684\u63a7\u5236\n```\nTimeSleep(seconds)                   # -> \u6307\u5b9a\u7b49\u5f85\u65f6\u95f4(\u79d2\u949f)\nWaitForAppearing()                   # -> \u7b49\u5f85\u5143\u7d20\u51fa\u73b0(\u53ef\u80fd\u662f\u9690\u85cf\uff0c\u4e0d\u53ef\u89c1\u7684)\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nWaitForDisappearing()                # -> \u7b49\u5f85\u5143\u7d20\u6d88\u5931\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nWaitForVisible()                     # -> \u7b49\u5f85\u5143\u7d20\u53ef\u89c1\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\n```\n\n## AppVerify methods --> \u7528\u4e8e\u9a8c\u8bc1\n```\nVerifyVar(name, expect_value)                # -> \u9a8c\u8bc1\u53d8\u91cf\u503c\uff0c\u662f\u671f\u671b\u7684expect_value\uff0c\u8fd4\u56deTrue\uff0c\u5426\u5219\u8fd4\u56deFalse\nVerifyAppInstalled(app_package)              # -> \u9a8c\u8bc1app package name\u5df2\u7ecf\u5b89\u88c5\nVerifyCurrentActivity(app_activity)          # -> \u9a8c\u8bc1\u5f53\u524dapp activity name\u662f\u671f\u671b\u7684app_activity\nVerifyText(text)                             # -> \u9a8c\u8bc1\u5143\u7d20text\u5c5e\u6027\u503c\uff0c\u4e3a\u671f\u671b\u7684text,**\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemEnabled()                          # -> \u9a8c\u8bc1\u5143\u7d20\u662fenabled\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemNotEnabled()                       # -> \u9a8c\u8bc1\u5143\u7d20\u662fNot Enabled, **\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemVisible()                          # -> \u9a8c\u8bc1\u5143\u7d20\u662f\u53ef\u89c1\u7684\uff0c **\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemNotVisible()                       # -> \u9a8c\u8bc1\u5143\u7d20\u662f\u4e0d\u53ef\u89c1\u7684\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemAttr(attr_name,expect_value)       # -> \u9a8c\u8bc1\u5143\u7d20\u5c5e\u6027attr_name\u7684\u503c\uff0c\u5305\u542b\u503cexpect_value,**\u914d\u5408SetControl\u4f7f\u7528**\nVerifyElemCounts(num)                        # -> \u9a8c\u8bc1\u5143\u7d20\u6570\u91cf\u4e3anum,**\u914d\u5408SetControl\u4f7f\u7528**\n```\n\n## AppTouchAction methods --> \u7528\u4e8eAndroid\u89e6\u6478\u64cd\u4f5c\n```\nTap()                        # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c\u8f7b\u89e6\u70b9\u51fb 1\u6b21\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nLongPress()                  # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c\u957f\u6309\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nPress()                      # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c\u6309\u4f4f\u4e0d\u91ca\u653e\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nMoveTo()                     # -> \u79fb\u52a8\u5230\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nRelease()                    # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c\u91ca\u653e\u6309\u4f4f\u7684\u64cd\u4f5c\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nDraw()                       # -> \u5728\u5f53\u524dactivity\u4e2d\uff0c\u753b\u753b\nSwipe(direction, times)      # -> \u5728\u5f53\u524dactivity\u4e2d\uff0c\u6ed1\u52a8.direction\u6ed1\u52a8\u65b9\u5411: up, down, left, right; times\u6ed1\u52a8\u6b21\u6570\uff0c\u9ed8\u8ba41\u6b21\n```\n\n## AppActions methods --> \u7528\u4e8eAndroid\u5e38\u89c4\u64cd\u4f5c\n```\nPinch()                      # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\u7f29\u5c0f\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nZoom()                       # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\u653e\u5927\uff0c**\u914d\u5408SetControl\u4f7f\u7528**\nSendKeys(value)              # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a,\u8f93\u5165\u6587\u672c\u503c\uff0c**\u914d\u5408SetControl\u4f7f\u7528**, \u7ee7\u627f\u81easelenium\uff0c\u53ef\u7528\u4e8eWebView\nclick()                      # -> \u5728\u6307\u5b9a\u5143\u7d20\u4e0a\uff0c\u70b9\u51fb\u5de6\u952e\u4e00\u6b21\uff0c**\u914d\u5408SetControl\u4f7f\u7528**, \u7ee7\u627f\u81easelenium,\u53ef\u7528\u4e8eWebView\n```\n\n> AppTouchAction\u548cAppActions\uff0c\u5c01\u88c5\u8f83\u5c11\u7684\u539f\u56e0\u662f\u8003\u8651\u5230Appium\u7ee7\u627f\u4e86selenium,\u56e0\u6b64\u6709\u4e9bappium\u63d0\u4f9b\u7684\u65b9\u6cd5\u4e2d,\u5e76\u4e0d\u4f1a\u540c\u65f6\u517c\u5bb9NativeApp\u548cWebviewApp\uff0c\u540c\u65f6\uff0c[rtsf-web](https://github.com/RockFeng0/rtsf-web)\u9879\u76ee\u5df2\u7ecf\u652f\u6301\u4e86selenium\u5bf9web ui\u7684\u6d4b\u8bd5\u3002\n\n# \u81ea\u5b9a\u4e49\uff0cyaml\u51fd\u6570\u548c\u53d8\u91cf\n\n\u5728case\u540c\u7ea7\u76ee\u5f55\u4e2d\uff0c\u521b\u5efa preference.py, \u8be5\u6587\u4ef6\u6240\u5b9a\u4e49\u7684 \u53d8\u91cf\u3001\u51fd\u6570\uff0c\u53ef\u4ee5\u88ab\u52a8\u6001\u52a0\u8f7d\u548c\u5f15\u7528\uff0c \u5177\u4f53\u53c2\u89c1rtsf\u7684\u4ecb\u7ecd\n\n# \u6570\u636e\u9a71\u52a8\u4e0e\u5206\u5c42\u7528\u4f8b\n\n\u5728[rtsf](https://github.com/RockFeng0/rtsf)\u9879\u76ee\u4e2d\uff0c\u5df2\u7ecf\u6709\u4e86\u8be6\u7ec6\u7684\u4ecb\u7ecd\uff0crtsf-web\u4e5f\u9002\u7528\n\n# \u573a\u666f\u5b9e\u4f8b\n\n\u4f9d\u636ertsf\u548crtsf-app\u7684\u7ea6\u5b9a\uff0c \u505a\u4e86\u51e0\u4e2aapp ui\u6d4b\u8bd5\u7684\u793a\u4f8b\n\n## \u7b80\u5355\u5b9e\u4f8b\n\n1. \u7f16\u5199\u4e00\u4e2ayaml\u6587\u4ef6\n\n```\n# test_case.yaml\n- project:\n    name: ApiDemos\u9879\u76ee\n    module: \u7b80\u5355\u5b9e\u4f8b\n\n- case:    \n    name: android_app_ui_auto_test_demo_1\n\n    glob_regx:\n        rex_bar_title: 'Views/Controls/(.*)'\n\n    glob_var:\n        app_package: io.appium.android.apis\n        app_main_activity: .ApiDemos\n        app_view_webview_activity: .view.WebView1\n        app_view_button_activity: .view.Buttons1\n        app_view_control_activity: .view.Controls1\n        app_view_dragdrop_activity: .view.DragAndDropDemo\n        app_graphic_paint_activity: .graphics.TouchPaint\n        app_animation_activity: .animation.BouncingBalls\n\n    pre_command:\n        - ${StartActivity($app_package, $app_view_control_activity)}\n        - ${DyStrData(var_bar_title, $rex_bar_title)}\n        - ${VerifyVar(var_bar_title, 1. Light Theme)}  \n\n    steps:      \n\n        - appdriver:\n            by: id\n            value: io.appium.android.apis:id/edit\n            index: 0\n            timeout: 10\n            action: ${SendKeys(\u4f60\u597d  -  hello)}\n\n        - appdriver:\n            action: ${TimeSleep(1)}\n\n        - appdriver:\n            by: -android uiautomator\n            value: text(\"Checkbox 1\")\n            index: 0\n            timeout: 10\n            action: ${Tap()}\n\n        - appdriver:\n            action: ${VerifyElemAttr(checked, true)}\n\n        - appdriver:\n            action: ${Tap()}\n\n        - appdriver:\n            action: ${VerifyElemAttr(checked, false)}\n\n        - appdriver:\n            action: ${TimeSleep(1)} \n\n        - appdriver:\n            action: ${Swipe(up, 1)}\n\n        - appdriver:\n            by: id\n            value: android:id/text1            \n            action: ${Tap()} \n\n        - appdriver:\n            by: -android uiautomator\n            value: 'text(\"Earth\")'\n            action: ${Tap()}\n\n    post_command:\n        - ${Back()}\n        - ${CloseApp()}\n```\n\n2. \u6267\u884c\u8fd9\u4e2a\u6587\u4ef6\n\n```\n# Terminal 1 \u76d1\u542c\u672c\u673a4723\u7aef\u53e3\nappserver 192.168.1.200:4723 \n\n# Terminal 2 \u672c\u5730\u6267\u884c\u8be5\u7528\u4f8b\naldriver C:\\test_case.yaml --apk C:\\ApiDemos-debug.apk\n```\n[\u4e0b\u8f7dApiDemos-debug.apk](https://github.com/RockFeng0/rtsf-app/releases/tag/v1.0.0)\n\n## \u8be6\u7ec6\u5b9e\u4f8b \n\n### \u573a\u666f\u4e00  \u672c\u5730\u6d4b\u8bd5\n\n\u4e00\u822c\u60c5\u51b5\u4e0b\uff0c\u5c31\u662f\u4e00\u53f0PC\uff0c\u8fde\u63a5\u4e00\u53f0\u8bbe\u5907\u7684\u6d4b\u8bd5\u573a\u666f\uff0c\u6b65\u9aa4\u5982\u4e0b\n\n1.\u6d4b\u8bd5\u573a\u666f\u5047\u8bbe\n\n```\n apk(\u5f85\u6d4b\u8bd5\u7684apk): C:\\ApiDemos-debug.apk\n case(\u81ea\u52a8\u5316\u6d4b\u8bd5\u7528\u4f8b): C:\\test_case.yaml\n\n PC_A_IP(\u672c\u673a): 192.168.1.1\n PC_A_Android_Device_ID(\u5929\u5929\u6a21\u62df\u5668): 127.0.0.1:6555\n```\n\n2.\u5f00\u542fappium server,\u5e76\u7ed1\u5b9a\u5f85\u6d4b\u8bbe\u5907\n\n```\n# PC_A\u76d1\u542c4723\u7aef\u53e3\uff0c\u7ed1\u5b9a\u540d\u5b57\u662f127.0.0.1:6555\u5e76\u4e14\u7248\u672c\u4e3a4.4.4\u7684\u79fb\u52a8\u8bbe\u5907\n# \u6ce8\u610f\uff0c\u76d1\u542c\u7aef\u53e3+1\u4e5f\u4f1a\u88ab\u5360\u7528\nappserver 192.168.1.1:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4\n```\n\n3.aldriver\u9a71\u52a8\u6d4b\u8bd5\n\n\n**aldriver\u547d\u4ee4\u6267\u884c\u672c\u5730\u6d4b\u8bd5\uff0c\u8be5\u547d\u4ee4\u4e3b\u52a8\u8fde\u63a5\u672c\u5730PC_A\u76844723\u7aef\u53e3\uff0c\u5e76\u9a71\u52a8adb\u8fde\u63a5\u7684\u7b2c\u4e00\u4e2a\u8bbe\u5907\u8fdb\u884c\u6d4b\u8bd5, \u8fd9\u5c31\u662f\u4e3a\u4ec0\u4e48\uff0c\u5728\u5047\u8bbe\u573a\u666f\u4e2d\uff0cPC_A\u4f7f\u7528\u672c\u673aIP\u548c4723\u7aef\u53e3**\n\n```\n# --apk\u53c2\u6570\u4f1a\u5728\u79fb\u52a8\u8bbe\u5907\u4e2d\u91cd\u88c5\naldriver C:\\test_case.yaml --apk C:\\ApiDemos-debug.apk\n```\n\n**\u4f60\u4e0d\u60f3\u91cd\u88c5apk\u3002\u901a\u8fc7ainfo\u83b7\u53d6\u5230 appPackage\u548cappActivity,\u6267\u884c\u4e0b\u8ff0\u547d\u4ee4**\n\n```\n# \u539f\u7406\u76f8\u5f53\u4e8eappium\u4e2d\u7684\uff0cstart_activity(package, activity)\naldriver C:\\test_case.yaml --package io.appium.android.apis --activity io.appium.android.apis.ApiDemos\n```\n\n**\u5f53\u7136\uff0c\u4f60\u53ef\u4ee5\u8865\u5168\u6240\u6709\u53c2\u6570**\n\n```\naldriver C:\\test_case.yaml --apk C:\\ApiDemos-debug.apk --package io.appium.android.apis --activity io.appium.android.apis.ApiDemos\n```\n\n\n4.\u91ca\u653e\u7aef\u53e3\u5360\u7528\n\n> ctrl + c \u7ed3\u675f\u7aef\u53e3\u5360\u7528\n\n\u573a\u666f\u4e00\u5b9e\u4f8b:\n![scene-1.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/scene-1.png)\n\n## \u573a\u666f\u4e8c \u8fdc\u7a0b\u63a7\u5236\u6d4b\u8bd5-Selenium Grid Mode\n\n1.\u6d4b\u8bd5\u80cc\u666f\u53ca\u5206\u6790\n\n```\n\u80cc\u666f: \n    \u6bd4\u5982\uff0c\u624b\u4e0a\u67091000\u6761\u76f8\u5bf9\u72ec\u7acb\u7684\u6d4b\u8bd5case\uff0c\u4e00\u53f0PC\u4e00\u53f0\u8bbe\u5907\u7684\u65b9\u5f0f\u5b8c\u6210\u8fd9\u4e9bcase\u7684\u9a8c\u8bc1\uff0c\u6548\u7387\u8f83\u4f4e\u3002\u90a3\u4e48\uff0c\u5e76\u884c\u6d4b\u8bd5\u662f\u6700\u597d\u7684\u89e3\u51b3\u529e\u6cd5\n\n\u5206\u6790: \n    1.\u591a\u53f0PC\u8fde\u63a5\u591a\u53f0\u8bbe\u5907\u7684\u6d4b\u8bd5\u573a\u666f\u5047\u8bbe,\u5176\u539f\u7406\u662f\u57fa\u4e8eselenium RC\uff0c\u4f7f\u7528selenium Grid\u7684\u65b9\u5f0f\uff0c\u4f7f\u5f97appium server\u4f5c\u4e3anode\u8282\u70b9\uff0c\u8fdb\u884c\u5206\u5e03\u5f0f\u6d4b\u8bd5\n    2.\u53ef\u662f\uff0c\u5373\u4f7f\u662f\u5206\u5e03\u5f0f\u6d4b\u8bd5\uff0c\u5b83\u7684\u8fc7\u7a0b\u4e5f\u662f\u4e00\u4e2a\u5e76\u53d1\u7684\u8fc7\u7a0b\uff0c\u6bcf\u53f0\u8bbe\u5907\u5206\u522b\u90fd\u8981\u6d4b\u8bd51000\u6761case\u3002\u597d\u6bd4\u5f88\u591a\u8f66\u5728\u652f\u8def\u4e0a\u8dd1\uff0c\u6c47\u5165\u7684\u4e3b\u5e72\u9053\u5374\u53ea\u6709\u4e00\u6761\n    3.\u9700\u8981\u505a\u7684\uff0c\u5c31\u662f\u8ba9\u8fd91000\u6761case\uff0c\u5206\u914d\u7ed9\u8fd9\u4e9b\u8bbe\u5907\uff0c\u8ba9\u5b83\u4eec\u5e76\u884c\u6d4b\u8bd5\u3002\u89e3\u51b3\u65b9\u6cd5\uff1a\u591a\u91cdhub\n```\n\n\u591a\u53f0PC,\u8fde\u63a5\u591a\u53f0\u8bbe\u5907,\u5e76\u884c\u6d4b\u8bd5case\u573a\u666f\uff0c\u6b65\u9aa4\u5982\u4e0b\n\n2.\u6d4b\u8bd5\u573a\u666f\u5047\u8bbe\n\n```\n apk(\u5f85\u6d4b\u8bd5\u7684apk): C:\\ApiDemos-debug.apk\n case1(\u81ea\u52a8\u5316\u6d4b\u8bd5\u7528\u4f8b): C:\\test_case1.yaml\n case2(\u81ea\u52a8\u5316\u6d4b\u8bd5\u7528\u4f8b): C:\\test_case2.yaml\n ...\n\n PC_Server_IP(Grid Hub\u7aef): 192.168.1.254\n\n PC_A_IP(\u672c\u673a): 192.168.1.1\n PC_A_Android_Device_ID(\u5929\u5929\u6a21\u62df\u5668): 127.0.0.1:6555\n ...\n\n PC_B_IP(\u8fdc\u7aef\u673a): 192.168.1.2\n PC_B_Android_Device_ID(\u5929\u5929\u6a21\u62df\u5668): 127.0.0.1:6555\n ...\n\n\u6ce8\u610f: adb.exe\u6700\u591a\u652f\u6301\u6bcf\u53f0pc\u94fe\u63a520\u53f0\u8bbe\u5907\n\u5e76\u884c\u6d4b\u8bd5: PC_A\u8fde\u63a5\u7684\u6240\u6709\u673a\u5668\uff0c\u6d4b\u8bd5case1;PC_B\u8fde\u63a5\u7684\u6240\u6709\u673a\u5668,\u6d4b\u8bd5case2\n```\n\n\u4e00\u4e2a\u56fe\uff0c\u4e09\u79cd\u60c5\u51b5\uff0c\u7406\u89e3\u5206\u5e03\u5f0f:\n![grid-hub.png](https://raw.githubusercontent.com/RockFeng0/img-folder/master/rtsf-app-img/grid-hub.png)\n\n3.\u5f00\u542fselenium grid hub\n\n\u547d\u4ee4\u8be6\u89e3\uff0c\u53c2\u89c1[rtsf-web](https://github.com/RockFeng0/rtsf-web)\n\n```\n# PC_Server\u8bbe\u7f6ePC_A\u7684hub\nwrhub C:\\selenium-server-standalone-3.14.0.jar --port 4444\n\n# PC_Server\u8bbe\u7f6ePC_B\u7684hub\nwrhub C:\\selenium-server-standalone-3.14.0.jar --port 5555\n```\n\n4.\u5f00\u542fappium server node\n\n```\n# PC_A  4723\u7aef\u53e3\u7ed1\u5b9a\u8bbe\u5907\uff0c\u5e76\u6ce8\u518cnode\u8282\u70b9 \nappserver 192.168.1.1:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4 --hub-ip 192.168.1.254 --hub-port 4444\n\n# PC_A  4725\u7aef\u53e3\u7ed1\u5b9a\u8bbe\u5907\uff0c\u5e76\u6ce8\u518cnode\u8282\u70b9 \nappserver 192.168.1.1:4725 --device-name DEVICE_ID --device-version DEVICE_VERSION --hub-ip 192.168.1.254 --hub-port 4444\n...\n\n# PC_B, \u540c\u7406\nappserver 192.168.1.2:4723 --device-name 127.0.0.1:6555 --device-version 4.4.4 --hub-ip 192.168.1.254 --hub-port 5555\n...\n```\n\n4.ardriver\u9a71\u52a8\u6d4b\u8bd5\n\n\u6ce8\u610f:\n- \u5982\u679c\u4f7f\u7528 --apk\u53c2\u6570\uff0c\u90a3\u4e48 \u786e\u4fdd PC A \u548c PC B,\u5728\u8be5\u6307\u5b9a\u7684\u6587\u4ef6\u8def\u5f84\u4e2d\uff0c\u5b58\u5728\u8fd9\u4e2aapk\u3002\n- \u5982\u679c\u4f7f\u7528 --package\u548c--activity\u53c2\u6570\uff0c\u90a3\u4e48\u786e\u4fdd\uff0c\u8fde\u63a5\u5230PC\u7684\u624b\u673a\uff0c\u5df2\u7ecf\u88c5\u4e86\u8fd9\u4e2aapk\n- aldriver \u4e0e ardriver\u7684\u533a\u522b\u5c31\u5728\u4e8e: ardriver\u652f\u6301 ip\u548cport\u53c2\u6570\uff0c\u5141\u8bb8grid\u6a21\u5f0f\n\n```\n# ardriver\u672c\u8eab\u662f\u4e2a\u5e76\u53d1\u9a71\u52a8\u6d4b\u8bd5\uff0c\u4f46\u662f\uff0c\u6bcf\u6b21\u4f7f\u7528\u90fd\u4f1a\u5f00\u4e00\u4e2a\u8fdb\u7a0b\uff0c\u5e76\u53d1\u7684\u8fc7\u7a0b\uff0c\u5c31\u91c7\u7528\u591a\u6b21\u6267\u884c\u547d\u4ee4\u5427\n# PC_A\u6267\u884ccase1\uff0c\u6267\u884ccase1\u7684\u6d4b\u8bd5\u9a8c\u8bc1\n> ardriver C:\\test_case1.yaml --apk C:\\ApiDemos-debug.apk --ip 192.168.1.254 --port 4444\n\n# PC_B\u7684\u6240\u6709\u8bbe\u5907\uff0c\u6267\u884ccase2\u7684\u6d4b\u8bd5\u9a8c\u8bc1\n> ardriver C:\\test_case2.yaml --apk C:\\ApiDemos-debug.apk --ip 192.168.1.254 --port 5555\n\n```\n\n# \u83b7\u53d6\u63a7\u4ef6\u7684\u5de5\u5177\n1. \u5982\u679c\u662fWebviewApp\u9879\u76ee\uff0c\u5de5\u5177\u63a8\u8350\uff0c\u5c31\u53c2\u8003rtsf-web\u9879\u76ee\n2. \u5982\u679c\u662fNativeApp\u9879\u76ee\uff0c\u60a8\u9700\u8981\u5b89\u88c5Android SDK, tools\u76ee\u5f55\u4e0b\uff0c\u4e24\u4e2a\u5de5\u5177\u53ef\u4ee5\u7528\u4e8e\u5b9a\u4f4dapp\u5143\u7d20:\n    - uiautomatorviewer\n    - hierarchyviewer\n\n\u6682\u65f6\u6ca1\u6709 \u627e\u5230\u8f7b\u91cf\u597d\u7528\u7684\uff0c\u7528\u4e8e\u5b9a\u4f4d\u7684\u5de5\u5177\uff0c\u5982\u679c\u60a8\u77e5\u9053\uff0c\u8bf7\u8d50\u6559\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "only for android ui test, base on rtsf",
    "version": "1.2.4",
    "project_urls": {
        "Homepage": "https://github.com/RockFeng0/rtsf-app"
    },
    "split_keywords": [
        "test",
        "android",
        "ui"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "007c77ec1fe748f14758cb2f9dafb39d9e5934441151b175c64c738ae52fa22d",
                "md5": "a8c79375c1b6b7e759ae6c52a4605fb2",
                "sha256": "f09c1f6a23c8d15c85b0970230ea3fe790ec33340f23c9147cb5b44c637c2584"
            },
            "downloads": -1,
            "filename": "rtsf_app-1.2.4-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a8c79375c1b6b7e759ae6c52a4605fb2",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.7",
            "size": 28222,
            "upload_time": "2023-08-15T09:33:19",
            "upload_time_iso_8601": "2023-08-15T09:33:19.188097Z",
            "url": "https://files.pythonhosted.org/packages/00/7c/77ec1fe748f14758cb2f9dafb39d9e5934441151b175c64c738ae52fa22d/rtsf_app-1.2.4-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d53528391b1a958b53d4ae1b2cdd9e4c59a477b2c6fd8f5f68f93b162f63435b",
                "md5": "9df7f21135b91f274e1c02b9d4aaf9e1",
                "sha256": "2b50f3931293e33afe66724ef79f3b164028e359420f522d3941500c1be33be7"
            },
            "downloads": -1,
            "filename": "rtsf-app-1.2.4.tar.gz",
            "has_sig": false,
            "md5_digest": "9df7f21135b91f274e1c02b9d4aaf9e1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 31862,
            "upload_time": "2023-08-15T09:33:20",
            "upload_time_iso_8601": "2023-08-15T09:33:20.993601Z",
            "url": "https://files.pythonhosted.org/packages/d5/35/28391b1a958b53d4ae1b2cdd9e4c59a477b2c6fd8f5f68f93b162f63435b/rtsf-app-1.2.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-15 09:33:20",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "RockFeng0",
    "github_project": "rtsf-app",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "rtsf-app"
}
        
Elapsed time: 0.11058s