Skip to main content

AppiumBoot: make an easy way (yaml) to app automation testing

Project description

GitHub | Gitee

English document

AppiumBoot - yaml驱动Appium测试

概述

Appium是移动端的自动化测试工具,但是要写python代码;

考虑到部分测试伙伴python能力不足,因此扩展Appium,支持通过yaml配置测试步骤;

框架通过编写简单的yaml, 就可以执行一系列复杂的 App 操作步骤, 如点击/输入/拖拽/上下滑/左右滑/放大缩小/提取变量/打印变量等,极大的简化了伙伴编写自动化测试脚本的工作量与工作难度,大幅提高人效;

框架通过提供类似pythonfor/if/break语义的步骤动作,赋予伙伴极大的开发能力与灵活性,能适用于广泛的测试场景。

框架提供include机制,用来加载并执行其他的步骤yaml,一方面是功能解耦,方便分工,一方面是功能复用,提高效率与质量,从而推进测试整体的工程化。

特性

  1. 基于 Appium 的webdriver
  2. 支持通过yaml来配置执行的步骤,简化了自动化测试开发: 每个步骤可以有多个动作,但单个步骤中动作名不能相同(yaml语法要求); 动作代表webdriver上的一种操作,如tap/swipe/scoll等等;
  3. 支持复杂的手势: 拖拽/上下滑/左右滑/放大缩小/多个点组成的移动轨迹等;
  4. 支持提取器
  5. 支持校验器
  6. 支持识别验证码(使用有道ocr)
  7. 支持类似pythonfor/if/break语义的步骤动作,灵活适应各种场景
  8. 支持include引用其他的yaml配置文件,以便解耦与复用

todo

  1. 支持更多的动作

安装

pip3 install AppiumBoot

安装后会生成命令AppiumBoot;

注: 对于深度deepin-linux系统,生成的命令放在目录~/.local/bin,建议将该目录添加到环境变量PATH中,如

export PATH="$PATH:/home/shi/.local/bin"

使用

  1. 先启动 appium

  2. 修改配置文件(yml)中的 init_driver 动作的参数, 如平台、app包等

  3. 使用

# 1 执行单个文件
AppiumBoot 步骤配置文件.yml

# 2 执行多个文件
AppiumBoot 步骤配置文件1.yml 步骤配置文件2.yml ...

# 3 执行单个目录, 即执行该目录下所有的yml文件
AppiumBoot 步骤配置目录

# 4 执行单个目录下的指定模式的文件
AppiumBoot 步骤配置目录/step-*.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/step-material.yml
handle action: init_driver={'executor': 'http://localhost:4723/wd/hub', 'desired_caps': {'platformName': 'Android', 'platformVersion': '9', 'deviceName': 'f978cc97', 'appPackage': 'io.material.catalog', 'appActy': 'io.material.catalog.main.MainActivity', 'automationName': 'UiAutomator2', 'noReset': True}}
handle action: include=material/comp1.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/comp1.yml
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/cat_demo_landing_row_root'}
handle action: swipe_up=None
handle action: sleep=1
handle action: swipe_down=None
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/end'}
handle action: sleep=2
handle action: click_by={'id': 'io.material.catalog:id/center'}
handle action: sleep=2
handle action: click_by={'id': 'io.material.catalog:id/attach_toggle'}
handle action: sleep=2
handle action: click_by={'id': 'io.material.catalog:id/center'}
handle action: include=material/back.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/back.yml
handle action: sleep=1
handle action: back=None
handle action: sleep=1
handle action: back=None
handle action: include=material/comp2.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/comp2.yml
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[2]/android.widget.LinearLayout'}
handle action: sleep=1
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/android.widget.ScrollView/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout'}
handle action: click_by={'aid': 'Page 2'}
handle action: click_by={'aid': 'Theme Switcher'}
handle action: sleep=1
handle action: click_by={'xpath': '(//android.widget.RadioButton[@content-desc="Green"])[1]'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/apply_button'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/add_button'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/remove_button'}
handle action: sleep=2
handle action: back=None
handle action: sleep=1
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout[3]'}
handle action: sleep=1
handle action: click_by={'aid': 'Alarm'}
handle action: sleep=1
handle action: click_by={'aid': 'Clock'}
handle action: sleep=1
handle action: click_by={'aid': 'Timer'}
handle action: sleep=1
handle action: click_by={'aid': 'Stopwatch'}
handle action: include=material/back.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/back.yml
handle action: sleep=1
handle action: back=None
handle action: sleep=1
handle action: back=None
handle action: include=material/comp3.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/comp3.yml
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[3]/android.widget.LinearLayout'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/cat_demo_landing_row_root'}
handle action: print=非全屏的上拉
非全屏的上拉
handle action: sleep=1
handle action: swipe_up=None
handle action: sleep=1
handle action: swipe_vertical=0.55,0.8
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/cat_fullscreen_switch'}
handle action: sleep=1
handle action: print=全屏的上拉
全屏的上拉
handle action: swipe_up=None
handle action: sleep=1
handle action: swipe_down=None
handle action: include=material/back.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/back.yml
handle action: sleep=1
handle action: back=None
handle action: sleep=1
handle action: back=None
handle action: include=material/comp4.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/material/comp4.yml
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[4]/android.widget.LinearLayout'}
handle action: sleep=1
handle action: click_by={'xpath': '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/aid.view.ViewGroup/android.widget.ScrollView/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout'}
handle action: click_by={'aid': 'Theme Switcher'}
handle action: sleep=1
handle action: click_by={'xpath': '(//android.widget.RadioButton[@content-desc="Yellow"])[1]'}
handle action: sleep=1
handle action: click_by={'id': 'io.material.catalog:id/apply_button'}
handle action: click_by={'id': 'io.material.catalog:id/material_button'}
handle action: sleep=1
handle action: back=None
......

命令会自动打开android material组件demo app,并按照步骤配置文件的描述来执行动作,如下拉、上拉、左滑、点击按钮等,一个个组件页面去操作

  • 如执行 AppiumBoot example/step-zhs.yml: 你要先安装众划算app; 输出如下:
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/step-zhs.yml
handle action: init_driver={'executor': 'http://localhost:4723/wd/hub', 'desired_caps': {'platformName': 'Android', 'platformVersion': '9', 'deviceName': 'f978cc97', 'appPackage': 'com.zhs.zhonghuasuanapp', 'apivity': 'com.zhs.activity.StartActivity', 'automationName': 'UiAutomator2', 'noReset': True}}
handle action: sleep=7
handle action: click_by_if_exist={'id': 'com.zhs.zhonghuasuanapp:id/img_start'}
handle action: start_recording_screen=None
handle action: swipe_up=None
handle action: include=zhs/login.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/zhs/login.yml
handle action: click_by={'id': 'com.zhs.zhonghuasuanapp:id/tab_my_image'}
handle action: sleep=2
handle action: once=[{'moveon_if': "boot.exist_by('id', 'com.zhs.zhonghuasuanapp:id/tv_account_login')", 'click_by': {'id': 'com.zhs.zhonghuasuanapp:id/tv_account_login'}, 'sleep': 2}, {'input_by_id': {'com.zhsnghuasuanapp:id/edit_login_username': 'shigebeyond', 'com.zhs.zhonghuasuanapp:id/edit_login_password': 'shige123'}}, {'click_by': {'id': 'com.zhs.zhonghuasuanapp:id/box_user_agreement'}}, {'click_by': {'id': 'com.zhs.zhonghuasuanapp:id/login_submit'}, 'sleep': 4}, {'click_by_if_exist': {'id': 'com.zhs.zhonghuasuanapp:id/btn_i_know'}}, {'click_by_if_exist': {'id': 'com.zhs.zhonghuasuanapp:id/tv_hid_guid'}}]
-- For loop start: for(1) -- 
第1次迭代
handle action: moveon_if=boot.exist_by('id', 'com.zhs.zhonghuasuanapp:id/tv_account_login')
-- For loop break: for(1), break condition: not (boot.exist_by('id', 'com.zhs.zhonghuasuanapp:id/tv_account_login')) -- 
handle action: sleep=2
handle action: include=zhs/apply.yml
Load and run step file: /ohome/shi/code/python/AppiumBoot/example/zhs/apply.yml
handle action: click_by={'id': 'com.zhs.zhonghuasuanapp:id/tab_new_image'}
handle action: sleep=4
handle action: swipe_up=None
handle action: sleep=2
handle action: swipe_down=None
......

命令会自动打开众划算app,并按照步骤配置文件的描述来执行动作,如下拉、上拉、左滑、点击按钮等

步骤配置文件及demo

用于指定多个步骤, 示例见源码 example 目录下的文件;

顶级的元素是步骤;

每个步骤里有多个动作(如sleep),如果动作有重名,就另外新开一个步骤写动作,这是由yaml语法限制导致的,但不影响步骤执行。

demo

demo视频

查找元素的方法

  1. id: 根据 id 属性值来查找, 对应By.ID
  2. sid: 根据 accessibility_id 属性值来查找, 对应By.ACCESSIBILITY_ID
  3. class: 根据类名来查找, 对应By.CLASS_NAME
  4. xpath: 根据 xpath 来查找, 对应By.XPATH

配置详解

支持通过yaml来配置执行的步骤;

每个步骤可以有多个动作,但单个步骤中动作名不能相同(yaml语法要求);

动作代表webdriver上的一种操作,如tap/swipe/scoll等等;

下面详细介绍每个动作:

  1. init_driver: 初始化driver
init_driver:
    executor: http://localhost:4723/wd/hub
    desired_caps:
      platformName: Android
      platformVersion: '9'
      deviceName: f978cc97
      appPackage: io.material.catalog
      appActivity: io.material.catalog.main.MainActivity
      automationName: UiAutomator2
      noReset: true
  1. close_driver: 关闭driver
close_driver:
  1. sleep: 线程睡眠;
sleep: 2 # 线程睡眠2秒
  1. print: 打印, 支持输出变量/函数;
# 调试打印
print: "总申请数=${dyn_data.total_apply}, 剩余份数=${dyn_data.quantity_remain}"

变量格式:

$msg 一级变量, 以$为前缀
${data.msg} 多级变量, 用 ${ 与 } 包含

函数格式:

${random_str(6)} 支持调用函数,目前仅支持以下几个函数: random_str/random_int/random_element/incr

函数罗列:

random_str(n): 随机字符串,参数n是字符个数
random_int(n): 随机数字,参数n是数字个数
random_element(var): 从list中随机挑选一个元素,参数var是list类型的变量名
incr(key): 自增值,从1开始,参数key表示不同的自增值,不同key会独立自增
  1. input_by_id: 填充 id 指定的输入框;
input_by_id:
  # 输入框id: 填充的值(支持写变量)
  'io.material.catalog:id/cat_demo_input': '18877310999'
  1. input_by_aid: 填充 accessibility_id 指定的输入框;
input_by_aid:
  # 输入框accessibility_id: 填充的值(支持写变量)
  'Input name': '18877310999'
  1. input_by_class: 填充 指定类名的输入框;
input_by_class:
  # 输入框类名: 填充的值(支持写变量)
  'android.widget.EditText': '18877310999'
  1. input_by_xpath: 填充 xpath 指定的输入框;
input_by_xpath:
  # 输入框xpath路径: 填充的值(支持写变量)
  '/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.EditText': aaa
  1. hide_keyboard: 隐藏键盘
hide_keyboard:
  1. swipe: 屏幕横扫(传坐标)
swipe: 
    from: 100,100 # 起点坐标
    to: 200,200 # 终点坐标
    duration: 2 # 耗时秒数, 可省
  1. swipe_up: 上滑(传比例)
swipe_up: 0.55 # 移动幅度比例(占屏幕高度的比例)
swipe_up: # 默认移动幅度比例为0.5
  1. swipe_down: 下滑(传比例)
swipe_down: 0.55 # 移动幅度比例(占屏幕高度的比例)
swipe_down: # 默认移动幅度比例为0.5
  1. swipe_left: 左滑(传y坐标)
swipe_left: 100 # y坐标
swipe_left: # 默认y坐标为中间
  1. swipe_right: 右滑(传y坐标)
swipe_right: 100 # y坐标
swipe_right: # 默认y坐标为中间
  1. swipe_vertical: 垂直方向(上下)滑动(传比例)
swipe_vertical: 0.2,0.7 # y轴起点/终点位置在屏幕的比例,如 0.2,0.7,即y轴上从屏幕0.2比例处滑到0.7比例处
  1. swipe_horizontal: 水平方向(左右)滑动(传比例)
swipe_horizontal: 0.2,0.7 # x轴起点/终点位置在屏幕的比例,如 0.2,0.7,即x轴上从屏幕0.2比例处滑到0.7比例处
  1. move_track: 移动轨迹(传坐标序列)
move_track: '800,1600;100,1600;100,600;800,600;360,600;360,1100' # 坐标序列,坐标之间使用;分割,如x1,y1;x2,y2
  1. drag_and_drop_by: 拖拽(传元素): 从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
drag_and_drop_by: 
    by: xpath # 元素查找方式: id/sid/class/xpath    
    from: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[4]/android.widget.LinearLayout
    to: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[2]/android.widget.LinearLayout
  1. scroll_by: 滚动(传元素): 从一个元素滚动到另一元素,直到页面自动停止(有惯性)
scroll_by: 
    by: xpath # 元素查找方式: id/sid/class/xpath    
    from: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[4]/android.widget.LinearLayout
    to: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[2]/android.widget.LinearLayout
  1. move_by: 移动(传元素): 从一个元素移动到另一元素,无惯性
move_by: 
    by: xpath # 元素查找方式: id/sid/class/xpath    
    from: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[4]/android.widget.LinearLayout
    to: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[2]/android.widget.LinearLayout
  1. zoom_in: 放大
zoom_in: 
  1. zoom_out: 缩小
zoom_out: 
  1. tap: 敲击屏幕(传坐标)
tap: 200,200
  1. tap_by: 敲击元素
tap_by:
    # 元素查找方式(id/sid/class/xpath) : 查找的值
    #id: io.material.catalog:id/cat_demo_landing_row_root
    xpath: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout # 按钮的xpath路径
    # 耗时秒数, 可省, 可用于模拟长按
    duration: 10
  1. click_by/click_by_if_exist: 点击元素;
click_by:
  # 元素查找方式(id/sid/class/xpath) : 查找的值
  #id: io.material.catalog:id/cat_demo_landing_row_root
  xpath: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout # 按钮的xpath路径

如果点击之前要先判断元素是否存在,则换用 click_by_if_exist

  1. shake: 摇一摇
shake:
  1. set_orientation: 设置屏幕方向
set_orientation: true # 是否竖屏, 否则横屏
  1. set_location: 设置地理位置
set_location: 49,123 # 纬度,经度
set_location: 49,123,10 # 纬度,经度,海拔高度
  1. screenshot: 整个窗口截图存为png;
screenshot:
    save_dir: downloads # 保存的目录,默认为 downloads
    save_file: test.png # 保存的文件名,默认为:时间戳.png
  1. screenshot_element_by: 对某个标签截图存为png;
screenshot_element_by
    # 元素查找方式(id/sid/class/xpath) : 查找的值
    #id: io.material.catalog:id/cat_demo_landing_row_root
    xpath: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout

    save_dir: downloads # 保存的目录,默认为 downloads
    save_file: test.png # 保存的文件名,默认为:时间戳.png
  1. execute_js: 执行js;
execute_js: alert('hello world')
  1. back: 返回键;
back: 
  1. keyevent: 模拟系统键;
keyevent: '4'
  1. open_notifications: 打开手机的通知栏;
open_notifications: 
  1. get_clipboard: 读剪切板内容;
get_clipboard: name # 参数为记录剪切板内容的变量名
  1. set_clipboard: 写剪切板内容;
set_clipboard: hello world $name # 参数是写入内容,可带参数
  1. push_file:推文件到手机上, 即写手机上文件;
push_file:
    to: /storage/emulated/0/documents/test/a.txt # 写入的手机上的文件
    content: helloworld # 写入的内容, content与to只能二选一
    #from: a.txt # 写入内容的本地来源文件, content与to只能二选一
  1. pull_file:从手机中拉文件, 即读手机上的文件;
pull_file:
    from: /storage/emulated/0/documents/test/a.txt # 读取的手机上的文件
    to: a.txt # 写入的本地文件, 可省
    var: content # 记录文件内容的变量, 可省
print: $content
  1. send_sms:发送短信;
send_sms:
    phone: 13475556022
    content: hello $name
  1. print_performance:打印性能信息;
print_performance:
  1. start_recording_screen:开始录屏; start_recording_screen 与 stop_recording_screen 配合使用(start在前,stop在后)
start_recording_screen:
  1. stop_recording_screen:结束录屏,并存为视频文件; start_recording_screen 与 stop_recording_screen 配合使用(start在前,stop在后), 如果两者之间的执行发生异常, 则系统会主动调用后续第一个stop_recording_screen动作, 以便记录好异常的全过程
stop_recording_screen: # 默认视频文件路径为 `record-时间.mp4`
stop_recording_screen: a.mp4 # 视频文件路径
  1. alert_accept: 点击弹框的确定按钮, 如授权弹框的允许;
alert_accept: 
  1. alert_dismiss: 取消弹框, 如授权弹框的禁止;
alert_dismiss: 
  1. for: 循环; for动作下包含一系列子步骤,表示循环执行这系列子步骤;变量for_i记录是第几次迭代(从1开始),变量for_v记录是每次迭代的元素值(仅当是list类型的变量迭代时有效)
# 循环3次
for(3) :
  # 每次迭代要执行的子步骤
  - swipe_down:
    sleep: 2

# 循环list类型的变量values
for(values) :
  # 每次迭代要执行的子步骤
  - swipe_down:
    sleep: 2
    
# 无限循环,直到遇到跳出动作
# 有变量for_i记录是第几次迭代(从1开始)
for:
  # 每次迭代要执行的子步骤
  - break_if: for_i>2 # 满足条件则跳出循环
    swipe_down:
    sleep: 2
  1. once: 只执行一次,等价于 for(1); once 结合 moveon_if,可以模拟 python 的 if 语法效果
once:
  # 每次迭代要执行的子步骤
  - moveon_if: for_i<=2 # 满足条件则往下走,否则跳出循环
    swipe_down:
    sleep: 2
  1. break_if: 满足条件则跳出循环; 只能定义在for/once循环的子步骤中
break_if: for_i>2 # 条件表达式,python语法
  1. moveon_if: 满足条件则往下走,否则跳出循环; 只能定义在for/once循环的子步骤中
moveon_if: for_i<=2 # 条件表达式,python语法
  1. moveon_if_exist_by: 如果检查元素存在 则往下走,否则跳出循环; 只能定义在for/once循环的子步骤中
moveon_if_exist_by:
    id: com.shikee.shikeeapp:id/button1
  1. break_if_exist_by: 如果检查元素存在 则跳出循环,否则往下走; 只能定义在for/once循环的子步骤中
break_if_exist_by:
    id: button1
  1. include: 包含其他步骤文件,如记录公共的步骤,或记录配置数据(如用户名密码);
include: part-common.yml
  1. set_vars: 设置变量;
set_vars:
  name: shi
  password: 123456
  birthday: 5-27
  1. print_vars: 打印所有变量;
print_vars:
  1. base_url: 设置基础url
base_url: https://www.taobao.com/
  1. get: 发get请求, 但无跳转;
get:
    url: $dyn_data_url # url,支持写变量
    extract_by_eval:
      dyn_data: "json.loads(response.text[16:-1])" # 变量response是响应对象
  1. post: 发post请求, 但无跳转;
post:
    url: http://admin.jym1.com/store/add_store # url,支持写变量
    is_ajax: true
    data: # post的参数
      # 参数名:参数值
      store_name: teststore-${random_str(6)}
      store_logo_url: '$img'
  1. upload: 上传文件;
upload: # 上传文件/图片
    url: http://admin.jym1.com/upload/common_upload_img/store_img
    files: # 上传的多个文件
      # 参数名:文件本地路径
      file: /home/shi/fruit.jpeg
    extract_by_jsonpath:
      img: $.data.url
  1. download: 下载文件; 变量download_file记录最新下载的单个文件
download:
    url: https://img.alicdn.com/tfscom/TB1t84NPuL2gK0jSZPhXXahvXXa.jpg_q90.jpg
    save_dir: downloads # 保存的目录,默认为 downloads
    save_file: test.jpg # 保存的文件名,默认为url中最后一级的文件名
  1. recognize_captcha: 识别验证码; 参数同 download 动作, 因为内部就是调用 download; 而变量captcha记录识别出来的验证码
recognize_captcha:
    url: http://admin.jym1.com/login/verify_image
    # save_dir: downloads # 保存的目录,默认为 downloads
    # save_file: test.jpg # 保存的文件名,默认为url中最后一级的文件名
  1. recognize_captcha_element: 识别验证码标签中的验证码; 参数同 screenshot_element_by 动作, 因为内部就是调用 screenshot_element_by; 而变量captcha记录识别出来的验证码
recognize_captcha_element:
    # 元素查找方式(id/sid/class/xpath) : 查找的值
    #id: io.material.catalog:id/cat_demo_landing_row_root
    xpath: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout

    #save_dir: downloads # 保存的目录,默认为 downloads
    #save_file: test.jpg # 保存的文件名,默认为url中最后一级的文件名
  1. exec: 执行命令, 可用于执行 HttpBoot/SeleniumBoot/AppiumBoot/MiniumBoot 等命令,以便打通多端的用例流程
exec: ls
exec: SeleniumBoot test.yml

校验器

主要是为了校验页面或响应的内容, 根据不同场景有2种写法

1. 针对当前页面, 那么校验器作为普通动作来写
2. 针对 get/post/upload 有发送http请求的动作, 那么校验器在动作内作为普通属性来写

不同校验器适用于不同场景

校验器 当前页面场景 http请求场景
validate_by_id Y N
validate_by_aid Y N
validate_by_class Y N
validate_by_xpath Y Y
validate_by_css N Y
validate_by_jsonpath N Y
  1. validate_by_id: 从当前页面中校验 id 对应的元素的值
validate_by_id:
  "io.material.catalog:id/cat_demo_text": # 元素的id
    '=': 'Hello world' # 校验符号或函数: 校验的值
  1. validate_by_aid: 从当前页面中校验 accessibility_id 对应的元素的值
validate_by_aid:
  "Timer": # 元素的accessibility_id
    '>': '2022-07-06 12:00:00' # 校验符号或函数: 校验的值
  1. validate_by_class: 从当前页面中校验类名对应的元素的值
validate_by_class:
  "android.widget.TextView": # 元素的类名
    '=': 'Hello world' # 校验符号或函数: 校验的值
  1. validate_by_xpath: 从当前页面或html响应中校验 xpath 路径对应的元素的值
validate_by_xpath:
  "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout": # 元素的xpath路径
    '>': 0 # 校验符号或函数: 校验的值, 即 id 元素的值>0
  "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[2]/android.widget.LinearLayout":
    contains: 衬衫 # 即 title 元素的值包含'衬衫'
  1. validate_by_css: 从html响应中校验类名对应的元素的值
validate_by_css:
  '#id': # 元素的css selector 模式
    '>': 0 # 校验符号或函数: 校验的值, 即 id 元素的值>0
  '#goods_title':
    contains: 衬衫 # 即 title 元素的值包含'衬衫'
  1. validate_by_jsonpath: 从json响应中校验 多层属性 的值
validate_by_jsonpath:
  '$.data.goods_id':
     '>': 0 # 校验符号或函数: 校验的值, 即 id 元素的值>0
  '$.data.goods_title':
    contains: 衬衫 # 即 title 元素的值包含'衬衫'

校验符号或函数

  1. =: 相同
  2. >: 大于
  3. <: 小于
  4. >=: 大于等于
  5. <=: 小于等于
  6. contains: 包含子串
  7. startswith: 以子串开头
  8. endswith: 以子串结尾
  9. regex_match: 正则匹配
  10. exist: 元素存在
  11. not_exist: 元素不存在

提取器

主要是为了从页面或响应中提取变量, 根据不同场景有2种写法

1. 针对当前页面, 那么提取器作为普通动作来写
2. 针对 get/post/upload 有发送http请求的动作, 那么提取器在动作内作为普通属性来写

不同校验器适用于不同场景

校验器 页面场景 http请求场景
extract_by_id Y N
extract_by_aid Y N
extract_by_class Y N
extract_by_xpath Y Y
extract_by_jsonpath N Y
extract_by_css N Y
extract_by_eval Y Y
  1. extract_by_id: 从当前页面中解析 id 对应的元素的值
extract_by_id:
  # 变量名: 元素id
  goods_id: "io.material.catalog:id/cat_demo_text"
  1. extract_by_aid: 从当前页面中解析 accessibility_id 对应的元素的值
extract_by_aid:
  # 变量名: 元素的accessibility_id
  update_time: "Timer"
  1. extract_by_class: 从当前页面中解析类名对应的元素的值
extract_by_class:
  # 变量名: 元素的accessibility_id
  name: "android.widget.TextView"
  1. extract_by_xpath: 从当前页面或html响应中解析 xpath 路径指定的元素的值
extract_by_xpath:
  # 变量名: xpath路径
  goods_id: /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.widget.FrameLayout[1]/android.widget.LinearLayout
  # 获得元素的属性
  goods_img_element: /hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.RelativeLayout[1]/android.widget.FrameLayout/android.widget.RelativeLayout/androidx.viewpager.widget.ViewPager/android.widget.ImageView/@class
  1. extract_by_css: 从html响应中解析 css selector 模式指定的元素的值
extract_by_css:
  # 变量名: css selector 模式
  goods_id: table>tbody>tr:nth-child(1)>td:nth-child(1) # 第一行第一列
  url: //*[@id="1"]/div/div/h3/a/@href # 获得<a>的href属性
  1. extract_by_jsonpath: 从json响应中解析 多层属性 的值
extract_by_jsonpath:
  # 变量名: json响应的多层属性
  img: $.data.url
  1. extract_by_eval: 使用 eval(表达式) 执行表达式, 并将执行结果记录到变量中
extract_by_eval:
    # 变量名: 表达式(python语法)
    dyn_data: "json.loads(response.text[16:-1])" # 变量response是响应对象

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

AppiumBoot-1.0.5.tar.gz (35.8 kB view hashes)

Uploaded Source

Built Distribution

AppiumBoot-1.0.5-py3-none-any.whl (28.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page