Robo 脚本参考指南

本文档提供了有关 Robo 脚本的参考信息,包括结构、功能、用法、记录和操作。Robo 脚本是一些测试,将移动应用的手动质量检查 (QA) 任务自动化,并有助于实现持续集成 (CI) 和发布前测试策略。Robo 脚本是 JSON 文件,用于描述一系列界面 (UI) 和其他操作。

您可以通过以下方式创建 Robo 脚本:

  • 使用 Robo 脚本记录功能。 (仅限 Android 设备)

  • 手动创建 Robo 脚本。 (Android 和 iOS+)

  • 记录 Robo 脚本,然后手动进行修改。 (仅限 Android 设备)

如需详细了解如何使用 Robo 脚本,请参阅运行 Robo 脚本

简介

Robo 脚本会与其他输入(例如被测应用的 Android 应用软件包 [APK] 文件)一起提供给 Robo 测试。

下面是一个让用户登录应用的 Robo 脚本示例,它会在被测应用启动时触发:

[
  {
    "crawlStage": "crawl",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "VIEW_TEXT_CHANGED",
        "replacementText": "user123",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/username"
          }
        ]
      },
      {
        "eventType": "VIEW_TEXT_CHANGED",
        "replacementText": "12345",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/password"
          }
        ]
      },
      {
        "eventType": "VIEW_CLICKED",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/login"
          }
        ]
      }
    ]
  }
]

如果文件中只有一个 Robo 脚本,并且它具有默认的 app_under_test_shown 触发条件(如上例所示),那么您便可以用一种较简单的方式在文件中指定 Robo 脚本,即编写出其操作序列即可:

[
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "user123",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/username"
      }
    ]
  },
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "12345",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/password"
      }
    ]
  },
  {
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/login"
      }
    ]
  }
]

iOS+ 对 Robo 脚本的支持

Robo for iOS+(Beta 版)对 Robo 脚本提供有限支持。适用于 iOS+ 的 Robo 脚本语法与 Android 语法相同,并且受支持的 iOS+ 功能的行为方式也与 Android 对应功能类似。

iOS+ 支持以下操作:

  • 断言
  • 点击
  • 长按
  • 滑动
  • 忽略所有元素
  • 等待
  • 截取屏幕截图
  • 终止抓取

iOS+ 支持元素描述符中的以下标识属性:

  • 类名称
  • 祖先类名称
  • 内容说明(和正则表达式)
  • 文本(和正则表达式)

iOS+ 支持上下文描述符中的以下触发条件

  • 显示被测应用
  • 存在元素
  • 执行非 Robo 脚本操作

结构

Robo 脚本包含多个属性,用于描述 Robo 执行脚本的方式。其中大多数属性都并非必需,并且都具有预定义的默认值:

属性 说明
id 一个整数,有助于在抓取输出中跟踪此 Robo 脚本。 Robo 具有内置的 Robo 脚本,并具有自己的 id。虽然不同 Robo 脚本中的相同 id 不会影响其行为,但在抓取输出中区分这些 Robo 脚本的操作可能具有挑战性。我们建议为您的 Robo 脚本分配唯一的 id1000 或更高版本),以避免任何冲突。
description id 类似,但更具描述性。
crawlStage 指定 Robo 会在哪个抓取阶段应用此 Robo 脚本。默认是主抓取阶段。
priority 此 Robo 脚本相较于其他 Robo 脚本的优先级。默认情况下,所有 Robo 脚本的优先级均为 1
maxNumberOfRuns 指定抓取期间 Robo 可以执行此 Robo 脚本的次数。默认为一次。
contextDescriptor 描述触发此 Robo 脚本的上下文或条件。如果省略该属性,则视为总是满足此 Robo 脚本的触发条件;也就是说,该 Robo 脚本是无条件触发的。
actions 此 Robo 脚本的所有操作。

一个文件可以包含一个或多个 Robo 脚本的集合。

下面的示例展示了一个包含两个无条件 Robo 脚本的文件,其中每个脚本都有一项会在抓取开始时执行一次的操作:

[
  {
    "id": 1000,
    "description": "My first Robo script",
    "actions": [
      {
        "eventType": "DISABLE_KEYBOARD"
      }
    ]
  },
  {
    "id": 1001,
    "description": "My second Robo script",
    "actions": [
      {
        "eventType": "PRESSED_BACK"
      }
    ]
  }
]

上下文描述符

上下文描述符用于定义使用一个属性或使用多个属性的组合触发 Robo 脚本的上下文或条件:

属性 说明
"condition": "always" 始终触发 Robo 脚本。
"condition": "element_present" 检查屏幕上是否存在与 elementDescriptorsvisionText 指定的文本匹配的界面 widget。
"condition": "element_disabled" 检查屏幕上是否存在与 elementDescriptors 匹配的界面 widget 并且无法与其进行交互。
"condition": "element_checked" 检查屏幕上是否存在与 elementDescriptors 匹配的界面 widget 并且已被选中。
"condition": "app_under_test_shown" 检查被测应用是否正在前台运行。
"condition": "default_launcher_shown" 检查设备的主屏幕是否显示;如果显示,则意味着没有任何应用正在前台运行。
"condition": "non_roboscript_action_performed" 检查 Robo 测试所执行的最后 nonRoboscriptActionCount 项连续操作是否非 Robo 脚本操作。
negateCondition 如果设置为 true,则否定 condition。例如,您可以使用此特性来检查屏幕上是否没有某个界面 widget,或者被测应用是否未在前台运行。
elementDescriptors 标识屏幕上的界面 widget 的一个或多个元素描述符。它与 element_presentelement_disabledelement_checked 条件结合使用。它与 visionText 相互排斥。如需了解详情,请参阅元素描述符
visionText 使用 Optical Character Recognition (OCR) API 来检测屏幕上的文本。visionTextelement_present 条件结合使用。它与 elementDescriptors 相互排斥。
nonRoboscriptActionCount 之前执行的连续非 Robo 脚本操作的次数。它与 non_roboscript_action_performed 条件结合使用,以便在每次执行 nonRoboscriptActionCount Robo 操作后触发 Robo 脚本。默认为 1

下面的 Robo 脚本示例由屏幕上显示的资源 ID 为 "my.app.package:id/page_header" 的界面 widget 触发:

{
  "id": 1000,
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/page_header"
      }
    ]
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "elementDescriptors": [
        {
          "text": "Settings"
        }
      ]
    }
  ]
}

下面的 Robo 脚本示例由光学字符识别 (OCR) 组件检测到的 "Privacy Policy" 触发:

{
  "id": 1000,
  "description": "Vision text Robo script",
  "contextDescriptor": {
    "condition": "element_present",
    "visionText": "Privacy Policy"
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "visionText": "Privacy Policy"
    }
  ]
}

下面的 Robo 脚本示例会在每次非脚本 Robo 操作后等待 5 秒:

{
  "contextDescriptor": {
    "condition": "non_roboscript_action_performed"
  },
  "maxNumberOfRuns" : 1000,
  "actions" : [
    {
      "eventType" : "DELAYED_MESSAGE_POSTED",
      "delayTime" : 5000
    }]
}

操作

Robo 脚本中的每项操作表示为一个或多个“属性-值”对的集合,如下表所述:

属性 说明
eventType 指定操作的类型,例如点击、文本编辑等。对于每项操作都是必需的。
elementDescriptors 用于标识界面 widget 的描述符。对于具有目标界面 widget 的所有操作(例如点击特定按钮)都是必需的。
optional 如果设置为 true,则系统会在无法执行此操作时跳过操作。例如,当此操作无法在屏幕上找到其目标界面 widget 时,系统将跳过此操作,而不会使相应的 Robo 脚本失败。默认情况下,值为 false
replacementText 需要输入到目标界面 widget 的文本。对于文本编辑操作是必需的。
swipeDirection 指定滑动方向。对于滑动操作是必需的。
delayTime 指定等待时长(以毫秒为单位)。对于等待操作是必需的。
pointTapXCoordinatepointTapYCoordinate 点按位置的像素 X 和 Y 坐标。与 pointTapXPercentpointTapYPercent 相互排斥。对于点按操作是必需的。
pointTapXPercentpointTapYPercent 点按位置的百分比 X 和 Y 坐标。与 pointTapXCoordinatepointTapYCoordinate 相互排斥。对于点按操作是必需的。

下面的 Robo 脚本示例包含两项操作但没有目标界面 widget(这意味着这些操作不会在特定界面 widget 上执行):

[
  {
    "eventType": "DELAYED_MESSAGE_POSTED",
    "delayTime": 3000
  },
  {
    "eventType": "PRESSED_BACK"
  }
]

元素描述符

元素描述符使用以下一个或多个标识特性来标识界面 widget:

属性 说明
className -
ancestorClassName 元素的界面层次结构祖先实体的类名。祖先实体是元素界面层次结构中的任何父级节点,包括元素本身。
resourceId -
resourceIdRegex 用于匹配 resourceId 的 Java 正则表达式。
contentDescription -
contentDescriptionRegex 用于匹配 contentDescription 的 Java 正则表达式。
text(显示在屏幕上) -
textRegex 用于匹配 text 的 Java 正则表达式。
groupViewChildPositionrecyclerViewChildPositionadapterViewChildPosition 根据界面 widget 的父级 widget 的种类来表示其子级位置。

这些特性通常是未定义的,例如,按钮可能没有文本和内容说明。即使存在某些特性值,这些值在给定应用屏幕上也可能不是唯一的(包括 resourceId)。

例如,通常只能通过在父级 widget 中使用不同的子级位置来区分列表项。这意味着,只使用一个元素描述符来标识界面 widget 通常是不够的。因此,操作的 elementDescriptors 属性包含一系列元素描述符,这些描述符按以下顺序排列:第一个描述符对应于目标界面 widget,第二个描述符对应于目标界面 widget 的父级 widget,依此类推。如果某项操作的所有元素描述符都与相应的界面 widget 子层次结构相匹配,则系统会匹配该操作的目标界面 widget。

下面是包含文本更改和点击操作的 Robo 脚本示例,这两项操作都要求您使用提供的元素描述符来标识目标界面 widget:

[
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "John",
    "elementDescriptors": [
      {
        "className": "android.support.v7.widget.AppCompatEditText",
        "groupViewChildPosition": 0,
        "resourceId": "com.google.samples.apps.topeka:id/first_name"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 0
      },
      {
        "className": "android.support.design.widget.TextInputLayout",
        "groupViewChildPosition": 1
      }
    ]
  },
  {
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "className": "android.support.design.widget.FloatingActionButton",
        "groupViewChildPosition": 1,
        "resourceId": "com.google.samples.apps.topeka:id/done"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 1,
        "resourceId": "com.google.samples.apps.topeka:id/content"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 0,
        "resourceId": "com.google.samples.apps.topeka:id/sign_in_content"
      }
    ]
  }
]

执行选项

您可以选择为 Robo 脚本中的操作列表添加一个 JSON 对象前缀,用于指定该 Robo 脚本的执行选项。此配置标头以 roboscript 关键字开头,后跟所需执行选项的 JSON 表示法。

Robo 脚本支持以下执行选项:

  • executionMode - Robo 脚本运行时采用的执行选项:
    • strict - 如果设置为 true,则 Robo 脚本不会采用部分匹配、跳过当前操作和暂停这三种方法。也就是说,Robo 脚本将作为常规插桩测试执行,只要它的任何操作无法执行,它就会失败。 默认为 false
    • dismiss_popups - 如果设置为 true,则 Robo 测试会在执行 Robo 脚本时关闭任何意外对话框,即使在 strict 模式下也是如此。在非 strict 模式下,此选项无效。默认为 false
    • notify - 如果设置为 false,则 Robo 脚本不会在其执行开始和结束时显示屏幕通知。默认为 true
  • postscript - Robo 脚本运行完成后采用的执行选项:
    • terminate - 如果设置为 true,则 Robo 测试会在 Robo 脚本完成后停止抓取操作。 默认为 false

下面是一个在 strict 模式下执行的 Robo 脚本的示例,它在不显示屏幕通知的情况下休眠 3 秒,之后抓取操作会停止:

"roboscript": {
  "executionMode": {
    "strict": true,
    "notify": false
  },
  "postscript": {
    "terminate": true
  }
}
[
  {
    "eventType": "DELAYED_MESSAGE_POSTED",
    "delayTime": 3000
  }
]

模板参数

模板参数是 Robo 脚本中的占位符,该占位符会在 Robo 测试加载该 Robo 脚本以便执行时替换为实际值。模板参数以双下划线开头,后接一个百分号,其末尾依次是一个百分号和双下划线。

Robo 脚本支持以下模板参数:

  • __%APP_PACKAGE_NAME%__ - 被测应用的软件包名称。

下面是停止被测应用进程的 Robo 脚本示例:

[
  {
    "eventType": "ADB_SHELL_COMMAND",
    "command": "am force-stop __%APP_PACKAGE_NAME%__"
  }
]

注释

Robo 脚本可以包含注释行,也就是以 #// 开头的行。

下面是一个包含几条注释的 Robo 脚本示例:

# Confirm a user account.
[
  {
    // Click the DONE button.
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "resourceId": "com.google.samples.apps.topeka:id/done"
      }
    ]
  }
]

功能

默认情况下,在 Robo 脚本的所有操作完成(或至少尝试过)之前,Robo 脚本会保持活跃状态。每当挑选一个要执行的操作时,Robo 测试都会不断尝试匹配 Robo 脚本操作。Robo 脚本采用以下方法来提高稳健性:

方法 说明
部分匹配 如果系统无法完全匹配当前的 Robo 脚本操作,则会放宽匹配条件,然后重新尝试匹配。在匹配 Robo 脚本操作的目标界面 widget 时,部分匹配不会考虑最外层的元素描述符。

如果部分匹配成功,则系统会照常执行相应的 Robo 脚本操作。此方法支持应用结构发生变化的场景,例如,在应用版本之间以及重新排列屏幕元素时。

跳过当前操作 如果系统无法完全或部分匹配当前的 Robo 脚本操作,则 Robo 会尝试匹配后续的 Robo 脚本操作。如果后续操作完全或部分匹配,则 Robo 测试会跳过(并且永远不会返回)当前的 Robo 脚本操作,然后执行后续操作。

此方法支持应用行为在版本之间发生变化或不稳定的场景,例如,在记录与重放 Robo 脚本期间,间歇性对话框可能出现在不同屏幕时的场景。

暂停 如果当前和后续的 Robo 脚本操作都不能完全或部分匹配,则 Robo 脚本会暂停,并且 Robo 测试会使用其他策略选择要执行的操作。完成此操作后,Robo 测试将继续执行 Robo 脚本。

只要当前或后续的 Robo 脚本操作无法匹配,Robo 脚本就会对任意数量的操作保持暂停状态。因此,Robo 脚本不一定需要成为 Robo 测试的开头,您可以穿插执行 Robo 脚本操作和标准的 Robo 测试操作。此方法支持应用行为不稳定的场景,也支持以下场景:应用版本之间的变化足够大,以至于 Robo 测试需要使用其标准操作“填补空白”。

优先级

如果 Robo 脚本达到其 maxNumberOfRuns,则在给定抓取期间将无法再触发该脚本。如果当前上下文可触发多个 Robo 脚本,则可以通过按以下顺序选择相应的 Robo 脚本来指定这些脚本的优先级:

  1. 具有 contextDescriptor 属性。
  2. 具有最高 priority(默认情况下,所有 Robo 脚本的执行 priority 都是 1)。
  3. 如果这些 Robo 脚本的优先级相同,则选择 Robo 脚本列表中最早出现的脚本。

下面的示例文件包含三个 Robo 脚本,这些脚本执行相同的操作并且由同一条件触发,即在被测应用处于前台时触发:

[
  {
    "id": 1000,
    "description": "Robo script 1",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  },
  {
    "id": 1001,
    "description": "Robo script 2",
    "priority": "2",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  },
  {
    "id": 1002,
    "description": "Robo script 3",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  }
]

当被测应用处于前台时,Robo 将按下面的顺序触发脚本:

  1. "Robo script 2",因为它具有最高优先级。
  2. "Robo script 1",因为它在优先级相同的其余适用 Robo 脚本中是最早出现的。
  3. "Robo script 3",它是最后一个适用的 Robo 脚本。

重复运行

默认情况下,在抓取期间,一个 Robo 脚本最多会触发一次;不过,可以通过 maxNumberOfRuns 属性调整此行为。

下面的 Robo 脚本示例会将被测应用置于后台多达 10 次:

{
  "id": 1000,
  "maxNumberOfRuns": 10,
  "contextDescriptor": {
    "condition": "app_under_test_shown"
  },
  "actions": [
    {
      "eventType": "GO_HOME"
    }
  ]
}

抓取阶段

Robo 脚本可用在给定 Robo 抓取的各个阶段:

抓取阶段 说明
pre_crawl 在 Robo 启动并开始抓取被测应用之前。
post_crawl 在 Robo 完成抓取被测应用之后。 post_crawl Robo 脚本的持续时间不得超过 15 秒,否则抓取可能会因超时而终止。
crawl 主抓取阶段,即 Robo 抓取被测应用期间。
close_screen 当 Robo 尝试从给定屏幕返回(回溯)时,如果此屏幕上的所有可能操作均已探索完毕,Robo 默认会按“返回”,但这在某些情况下是不可取的。

如果未指定 Robo 脚本的 crawlStage 属性,则默认为 crawl

下面的 Robo 脚本示例会在 Robo 开始抓取被测应用之前清除被测应用的用户数据:

{
  "id": 1000,
  "crawlStage": "pre_crawl",
  "actions": [
    {
      "eventType": "ADB_SHELL_COMMAND",
      "command": "pm clear __%APP_PACKAGE_NAME%__"
    }
  ]
}

下面的 Robo 脚本示例会指示 Robo 在每次尝试从确认对话框返回(回溯)时点击 "Cancel"

{
  "id": 1000,
  "crawlStage": "close_screen",
  "maxNumberOfRuns": 999,
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/confirmation_dialog"
      }
    ]
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "elementDescriptors": [
        {
          "text": "Cancel"
        }
      ]
    }
  ]
}

基于条件的操作

Robo 脚本可以包含基于条件的操作。基于条件的操作有另外三个属性,用于描述 Robo 如何执行这些操作:

属性 说明
priority 此基于条件的操作相较于其所在 Robo 脚本内的其他基于条件的操作的优先级。默认情况下,所有基于条件的操作的优先级均为 1
maxNumberOfRuns 指定在此基于条件的操作所在的 Robo 脚本的一次执行过程中,可以执行此基于条件的操作的次数。默认情况下,在基于条件的操作所在的 Robo 脚本的单次执行过程中,所有基于条件的操作都是最多只能执行一次。
contextDescriptor 触发此基于条件的操作的上下文/条件。它采用与 Robo 脚本的 contextDescriptor 相同的结构并提供类似功能

Robo 脚本被触发后,它会按显示的顺序逐个执行无条件操作。不过,如果 Robo 脚本包含基于条件的操作,则 Robo 脚本会在每次选取要执行的非条件操作之前考虑这些基于条件的操作。如果系统根据优先级和剩余运行次数触发并选取了任何基于条件的操作,Robo 脚本便会执行此基于条件的操作。否则,Robo 脚本会执行以下无条件操作。Robo 脚本必须包含至少一个无条件操作才有效。

下面的无条件 Robo 脚本示例包含一个基于条件的操作,该操作会关闭在 Robo 脚本执行过程中任何时间点出现的弹出式对话框:

{
  "id": 1000,
  "actions": [
    {
      "description": "Dismiss popup",
      "maxNumberOfRuns": 100,
      "contextDescriptor": {
        "condition": "default_launcher_shown",
        "negateCondition": true
      },
      "eventType": "GO_HOME"
    },
    {
      "description": "Screen off",
      "eventType": "ADB_SHELL_COMMAND",
      "command": "input keyevent 26"
    },
    {
      "description": "Wait for 10 seconds",
      "eventType": "DELAYED_MESSAGE_POSTED",
      "delayTime": 10000
    },
    {
      "description": "Screen on",
      "eventType": "ADB_SHELL_COMMAND",
      "command": "input keyevent 82"
    },
    {
      "description": "Wait for 10 seconds",
      "eventType": "DELAYED_MESSAGE_POSTED",
      "delayTime": 10000
    }
}

忽略操作

Robo 脚本可以包含指示 Robo 忽略特定界面 widget 或特定屏幕上的所有界面 widget 的指令。这些指令通过相应的 eventType ELEMENT_IGNOREDALL_ELEMENTS_IGNORED 来指示忽略“操作”。

每当包含忽略操作的 Robo 脚本的 contextDescriptor 属性与给定屏幕匹配时,Robo 就不会与忽略操作定位的任何界面 widget 交互(除非有其他 Robo 脚本操作指示 Robo 对某个被忽略的界面 widget 执行操作)。

Robo 脚本可以混合包含忽略操作、基于条件的操作和无条件操作。忽略操作与其他 Robo 脚本操作不同,只要其所在的 Robo 脚本的 contextDescriptor 在 Robo 抓取期间与某个屏幕匹配,系统就会应用忽略操作,而不会考虑 prioritymaxNumberOfRuns 属性的值。

下面的示例文件包含两个 Robo 脚本。第一个 Robo 脚本指示 Robo 忽略包含资源 ID 为 "my.app.package:id/ignored_screen" 的界面 widget 的屏幕上的所有界面 widget。第二个 Robo 脚本指示 Robo 忽略包含资源 ID 为 "my.app.package:id/main_screen" 的界面 widget 的屏幕上,资源 ID 与 Java 正则表达式 ".*:id/done" 匹配的界面 widget:

[
  {
    "id": 1000,
    "contextDescriptor": {
      "condition": "element_present",
      "elementDescriptors": [
        {
          "resourceId": "my.app.package:id/ignored_screen"
        }
      ]
    },
    "actions": [
      {
        "eventType": "ALL_ELEMENTS_IGNORED"
      }
    ]
  },
  {
    "id": 1001,
    "contextDescriptor": {
      "condition": "element_present",
      "elementDescriptors": [
        {
          "resourceId": "my.app.package:id/main_screen"
        }
      ]
    },
    "actions": [
      {
        "eventType": "ELEMENT_IGNORED",
        "elementDescriptors": [
          {
            "resourceIdRegex": ".*:id/done"
          }
        ]
      }
    ]
  }
]

RecyclerView 和 AdapterView 支持

RecyclerView 和 AdapterView widget 的子级会动态加载,并且可能会在远离当前屏幕的多次滑动后显示。由于屏幕的大小以及到达此子级所需的滑动次数对于不同的设备外形规格而有所不同,因此依靠子级的数据位置(绝对位置)要稳健得多。依靠将此子级带到屏幕然后使用其屏幕位置所需的滑动次数是一种不太稳健的方法。

因此,Robo 脚本会将 RecyclerView 子级(Robo 脚本操作的目标)的绝对数据位置捕获为 recyclerViewChildPosition。Robo 脚本还会将 AdapterView 子级(Robo 脚本操作的目标)的绝对数据位置捕获为 adapterViewChildPosition

对 RecyclerView 和 AdapterView 子级的操作按以下步骤执行:

  1. Robo 测试通过对包含子级的 RecyclerView 或 AdapterView 执行定位操作,确保该子级显示在屏幕上。

  2. Robo 测试会直接在子级元素上执行记录的操作,因为它已经显示在屏幕上。

下面是针对 AdapterView (android.widget.GridView) 子级的点击操作的示例:

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "className": "com.google.samples.apps.topeka.widget.AvatarView",
      "adapterViewChildPosition": 5,
      "resourceId": "com.google.samples.apps.topeka:id/avatar",
      "contentDescription": "Avatar 6"
    },
    {
      "className": "android.widget.GridView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/avatars"
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 1
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 0
    }
  ]
}

下面是针对 RecyclerView (android.support.v7.widget.RecyclerView) 子级的点击操作的示例:

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "className": "android.support.v7.widget.AppCompatTextView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/category_title"
    },
    {
      "className": "android.widget.FrameLayout",
      "recyclerViewChildPosition": 8,
      "resourceId": "com.google.samples.apps.topeka:id/category_item"
    },
    {
      "className": "android.support.v7.widget.RecyclerView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/categories"
    },
    {
      "className": "android.widget.FrameLayout",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/category_container"
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 0
    }
  ]
}

在 Android Studio 中记录 Robo 脚本并在 Test Lab 中运行

您可以在 Android Studio 中创建 Robo 脚本,Android Studio 会将此脚本保存为 JSON 文件。然后,您可以将 JSON 文件随应用一起上传到 Firebase Test Lab,并相应地运行测试。

当您运行附加了脚本的 Robo 测试时,Robo 测试会先逐步执行您预先编写了脚本的操作,然后像平常那样探索该应用。

如需在 Android Studio 中创建 Robo 脚本 JSON 文件,请按照在 Android Studio 中使用 Test Lab 记录 Robo 脚本中的步骤操作。

Robo 脚本操作

以下常见的可选特性适用于所有操作:

  • description - 帮助跟踪 Robo 测试输出中此 Robo 脚本操作的执行情况。

断言

如果断言条件为 true,则 Robo 脚本会继续执行下一项操作,该操作可能是其他断言。否则,Robo 脚本的执行会因为断言失败而停止。

下表列出了必需的属性:

属性 说明
"eventType": "ASSERTION" --
contextDescriptor 描述断言的上下文或条件。它采用与 Robo 脚本的 contextDescriptor 相同的结构并提供类似功能。

下面是一个 Robo 脚本断言示例,该断言检查被测应用是否在前台:

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "app_under_test_shown"
  }
}

下面是一个 Robo 脚本断言示例,该断言检查屏幕上是否存在资源 ID 为 "com.google.samples.apps.topeka:id/done" 的界面 widget:

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "com.google.samples.apps.topeka:id/done"
      }
    ]
  }
}

下面是一个 Robo 脚本断言示例,该断言检查系统是否没有在屏幕上(使用 OCR)检测到 "Settings"

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "element_present",
    "negateCondition": true,
    "visionText": "Settings"
  }
}

点击

下表列出了必需的特性:

特性 说明
eventType 指定 Robo 脚本操作的类型。
"eventType": "VIEW_CLICKED" 点击被测应用的目标元素。
"eventType": "SOFT_KEYBOARD_CLICK" 点击软键盘的目标元素。
"eventType": "SOFT_KEYBOARD_RANDOM_CLICK" 点击软键盘的随机元素,最多 maxNumberOfRuns 次。
"eventType": "LIST_ITEM_CLICKED" 供 Android Studio 中的 Robo 脚本记录器用来点击列表项。
elementDescriptors 使用 Android 界面层次结构识别点击的界面 widget。它与 visionText 相互排斥。
visionText 使用 OCR 识别点击的元素。它与 elementDescriptors 相互排斥。
matchIndex 使用 visionText 标识目标元素时,指定匹配目标元素出现位置的索引。如果是 0,Robo 脚本操作会选择第一个匹配的元素;如果是 1,Robo 脚本操作会选择第二个匹配的元素,依此类推。排序是按从左到右、从上到下的方式确定的。默认值为 0(选择第一个匹配项)。
maxNumberOfRuns 指定点击软键盘随机元素的次数(如果 eventTypeSOFT_KEYBOARD_RANDOM_CLICK)。默认值为 1

下面是一个 Robo 脚本操作示例,该操作点击资源 ID 为 "com.google.samples.apps.topeka:id/done" 的按钮:

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/done"
    }
  ]
}

下面举例说明了一个 Robo 脚本操作,该操作点击系统使用 OCR 在屏幕上检测到的 "Search" 字词的第二个出现位置:

{
  "eventType": "VIEW_CLICKED",
  "visionText": "Search",
  "matchIndex": 1
}

下面是一个 Robo 脚本操作示例,该操作点击内容说明为 "Emoji button" 的软键盘元素:

{
  "eventType": "SOFT_KEYBOARD_CLICK",
  "elementDescriptors": [
    {
      "contentDescription": "Emoji button"
    }
  ]
}

下面是一个 Robo 脚本操作示例,该操作点击随机软键盘元素(最多 5 次):

{
  "eventType": "SOFT_KEYBOARD_RANDOM_CLICK",
  "maxNumberOfRuns": 5
}

停用软键盘

下表列出了必需的特性:

属性 说明
"eventType": "DISABLE_KEYBOARD" --

下面是一个 Robo 脚本操作示例,该操作停用软键盘:

{
  "eventType": "DISABLE_KEYBOARD"
}

执行 adb shell 命令

下表列出了必需的特性:

属性 说明
"eventType": "ADB_SHELL_COMMAND" --
command 需要执行的 Android 调试桥 (adb) shell 命令。

以下特性是可选的:

  • expectedOutputRegex - Java 正则表达式形式的命令预期输出。如果输出不匹配,则 Robo 脚本操作将会失败。默认情况下,它是一个空字符串,表示不检查输出。

下面是一个 Robo 脚本操作示例,该操作清除被测应用的用户数据:

{
  "eventType": "ADB_SHELL_COMMAND",
  "command": "pm clear __%APP_PACKAGE_NAME%__"
}

授予权限

此操作由 Android Studio 中的 Robo 脚本记录器记录,目的是向后兼容 Espresso 测试记录器。Robo 测试会在每次抓取开始时向被测应用授予所有权限,因此这项操作实际上不执行任何功能。请勿在 Robo 脚本中使用此操作。

下表列出了必需的特性:

属性 说明
"eventType": "PERMISSIONS_REQUEST" --

忽略屏幕上的所有元素

此操作会指示 Robo 忽略触发其所在 Robo 脚本的任何屏幕上的所有元素。

下表列出了必需的属性:

属性 说明
"eventType": "ALL_ELEMENTS_IGNORED" --

下面是一个 Robo 脚本操作示例,该操作会指示 Robo 忽略屏幕上的所有元素:

{
  "eventType": "ALL_ELEMENTS_IGNORED"
}

忽略元素

此操作会指示 Robo 忽略与指定的 elementDescriptors 匹配的一个或多个元素。

下表列出了必需的属性:

属性 说明
"eventType": "ELEMENT_IGNORED" --
elementDescriptors 使用 Android 界面层次结构识别忽略的界面 widget。

以下特性是可选的:

  • ignoreChildren - 如果设置为 true,则 Robo 还会忽略被忽略的界面 widget 的所有后代。默认为 false

下面是一个 Robo 脚本操作示例,该操作会指示 Robo 忽略内容说明以 "Avatar" 开头的所有元素:

{
  "eventType": "ELEMENT_IGNORED",
  "elementDescriptors": [
    {
      "contentDescriptionRegex": "Avatar.*"
    }
  ]
}

输入文本

下表列出了必需的特性:

属性 说明
eventType 指定 Robo 脚本操作的类型。
"eventType": "VIEW_TEXT_CHANGED" 在目标界面 widget 中输入给定文本。
"eventType": "ENTER_TEXT" 在目标界面 widget 中输入给定文本,然后向此界面 widget 发送 KEYCODE_ENTER 事件。
elementDescriptors 使用 Android 界面层次结构识别目标界面 widget。
replacementText 需要输入到目标界面 widget 的文本。

下面是一个 Robo 脚本操作示例,该操作在资源 ID 为 "com.google.samples.apps.topeka:id/first_name" 的界面 widget 中输入 "John"

{
  "eventType": "VIEW_TEXT_CHANGED",
  "replacementText": "John",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/first_name"
    }
  ]
}

长按

下表列出了必需的特性:

属性 说明
"eventType": "VIEW_LONG_CLICKED" --
elementDescriptors 使用 Android 界面层次结构识别目标界面 widget。 它与 visionText 相互排斥。
visionText 使用 OCR 识别点击的长元素。它与 elementDescriptors 相互排斥。
matchIndex 使用 visionText 标识目标元素时,指定匹配目标元素出现位置的索引。如果是 0,Robo 脚本操作会选择第一个匹配的元素;如果是 1,Robo 脚本操作会选择第二个匹配的元素,依此类推。排序是按从左到右、从上到下的方式确定的。默认值为 0(选择第一个匹配项)。

以下特性是可选的:

  • delayTime - 指定长按持续的时长(以毫秒为单位)。

下面是一个 Robo 脚本操作示例,该操作对内容说明为 "Avatar 8" 的界面 widget 执行 5 秒钟长按:

{
  "eventType": "VIEW_LONG_CLICKED",
  "elementDescriptors": [
    {
      "contentDescription": "Avatar 8"
    }
  ],
  "delayTime": 5000
}

执行单点手势

下表列出了必需的特性:

属性 说明
"eventType": "ONE_POINT_GESTURE" --
coordinates 单点手势的两个坐标,格式为“(x1,y1)->(x2,y2)”(以百分比或像素表示)。

以下特性是可选的:

  • dragAndDrop - 如果设为 true,单点手势会执行拖放操作。默认为 false

下面是一个 Robo 脚本的单点手势操作示例,该操作执行向下滑动:

{
  "eventType": "ONE_POINT_GESTURE",
  "coordinates": "(50%,25%)->(50%,75%)"
}

执行两点手势

下表列出了必需的特性:

属性 说明
"eventType": "TWO_POINT_GESTURE" --
coordinates 两点手势的四个坐标,格式为“(x1,y1)->(x2,y2),(x3,y3)->(x4,y4)”(以百分比或像素表示)。

下面是一个 Robo 脚本操作示例,该操作执行双指张开手势:

{
  "eventType": "TWO_POINT_GESTURE",
  "coordinates": "(50%,50%)->(25%,50%),(50%,50%)->(75%,50%)"
}

执行 IME 操作

此操作针对指定的目标界面 widget 按下输入法 (IME) 上的当前操作按钮,例如“下一页”“完成”和“搜索”。

下表列出了必需的特性:

属性 说明
"eventType": "PRESSED_EDITOR_ACTION" --
elementDescriptors 使用 Android 界面层次结构识别目标界面 widget。

下面是一个 Robo 脚本操作示例,该操作对资源 ID 为 "com.google.samples.apps.topeka:id/first_name" 的界面 widget 执行 IME 操作:

{
  "eventType": "PRESSED_EDITOR_ACTION",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/first_name"
    }
  ]
}

按“返回”

下表列出了必需的特性:

属性 说明
eventType 指定 Robo 脚本操作的类型。
"eventType": "PRESSED_BACK" 向设备发送 KEYCODE_BACK 事件。
"eventType": "PRESSED_BACK_EMULATOR_28" 供 Android Studio 中的 Robo 脚本记录器用来在模拟器 API 28 上按“返回”。

下面是一个 Robo 脚本操作示例,该操作按“返回”:

{
  "eventType": "PRESSED_BACK"
}

按“主屏幕”

此操作向设备发送 KEYCODE_HOME 事件。

下表列出了必需的特性:

属性 说明
"eventType": "GO_HOME" --

下面是一个 Robo 脚本操作示例,该操作按“主屏幕”:

{
  "eventType": "GO_HOME"
}

将元素滚动到视图中

此操作使 Robo 测试向前滚动与指定的 elementDescriptors 匹配的界面 widget,直到屏幕上显示与指定的 childElementDescriptors 匹配的界面 widget,或者滚动的 widget 无法再滚动,或者滚动次数已达到上限(50 次)。

下表列出了必需的特性:

属性 说明
"eventType": "ELEMENT_SCROLL_INTO_VIEW" --
elementDescriptors 使用 Android 界面层次结构识别滚动的界面 widget。
childElementDescriptors 使用 Android 界面层次结构识别要滚动到的界面 widget。

下面是一个 Robo 脚本操作示例,该操作滚动资源 ID 为 "my.app.package:id/scrollable_card_container" 的界面 widget,直到屏幕上显示文本为 "Orange" 的界面 widget(或者无法再滚动,或者滚动次数已达到上限 [50 次]):

{
  "eventType": "ELEMENT_SCROLL_INTO_VIEW",
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/scrollable_card_container"
    }
  ],
  "childElementDescriptors": [
    {
      "text": "Orange"
    }
  ]
}

滑动

下表列出了必需的特性:

属性 说明
"eventType": "VIEW_SWIPED" --
swipeDirection 指定滑动方向:
  • Left
  • Right
  • Up
  • Down
  • Forward - DownRight,具体取决于目标界面 widget 的垂直或水平可滚动性。
  • Backward - UpLeft,具体取决于目标界面 widget 的垂直或水平可滚动性。
elementDescriptors 使用 Android 界面层次结构识别目标界面 widget。

下面是一个 Robo 脚本操作示例,该操作向上滑动资源 ID 为 "my.app.package:id/custom_content" 的界面 widget:

{
  "eventType": "VIEW_SWIPED",
  "swipeDirection": "Up",
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/custom_content"
    }
  ]
}

截取屏幕截图

下表列出了必需的特性:

属性 说明
"eventType": "TAKE_SCREENSHOT" --
screenshotName 指定屏幕截图文件名。

下面是一个 Robo 脚本操作示例,该操作截取屏幕截图:

{
  "eventType": "TAKE_SCREENSHOT",
  "screenshotName": "my_screenshot"
}

点按屏幕上的某个点

下表列出了必需的特性:

属性 说明
"eventType": "POINT_TAP" --
pointTapXCoordinate 点按位置的像素 X 坐标。与 pointTapXPercentpointTapYPercent 相互排斥。
pointTapYCoordinate 点按位置的像素 Y 坐标。与 pointTapXPercentpointTapYPercent 相互排斥。
pointTapXPercent 点按位置的百分比 X 坐标。与 pointTapXCoordinatepointTapYCoordinate 相互排斥。
pointTapYPercent 点按位置的百分比 Y 坐标。与 pointTapXCoordinatepointTapYCoordinate 相互排斥。

下面是一个 Robo 脚本操作示例,该操作点按屏幕中间:

{
  "eventType": "POINT_TAP",
  "pointTapXPercent": 50,
  "pointTapYPercent": 50
}

点按元素中的某个点

下表列出了必需的特性:

属性 说明
"eventType": "POINT_TAP_ELEMENT" --
pointTapXPercent 目标元素中的百分比 X 坐标。
pointTapYPercent 目标元素中的百分比 Y 坐标。
elementDescriptors 使用 Android 界面层次结构识别目标界面 widget。

下面是一个 Robo 脚本操作示例,该操作将拖动条的滑块向右移动:

{
  "eventType": "POINT_TAP_ELEMENT",
  "pointTapXPercent": 80,
  "pointTapYPercent": 50,
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/my_seekbar"
    }
  ]
}

终止抓取

此操作停止 Robo 测试。

下表列出了必需的特性:

属性 说明
"eventType": "TERMINATE_CRAWL" --

下面是一个 Robo 脚本操作示例,该操作停止 Robo 测试:

{
  "eventType": "TERMINATE_CRAWL"
}

等待

下表列出了必需的特性:

属性 说明
"eventType": "DELAYED_MESSAGE_POSTED" --
delayTime 指定等待时长(以毫秒为单位)。

下面是一个 Robo 脚本操作示例,该操作等待 3 秒钟:

{
  "eventType": "DELAYED_MESSAGE_POSTED",
  "delayTime": 3000
}

等待元素

此操作使 Robo 测试等待某个元素出现在屏幕上,直到指定的超时。

下表列出了必需的特性:

属性 说明
"eventType": "WAIT_FOR_ELEMENT" --
delayTime 指定等待超时(以毫秒为单位)。
elementDescriptors 使用 Android 界面层次结构识别等待的界面 widget。

下面是一个 Robo 脚本操作示例,该操作等待资源 ID 为 "my.app.package:id/confirmation_button" 的界面 widget 出现在屏幕上,但最多等待 30 秒:

{
  "eventType": "WAIT_FOR_ELEMENT",
  "delayTime": 30000,
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/confirmation_button"
    }
  ]
}

后续步骤