Robot Framework教程

Robot Framework教程

Yiuhang Chan

Robot Framework 简介

Robot Framework是一个通用的开源自动化框架。它可用于测试自动化和机器人流程自动化(RPA)。

Robot Framework受Robot Framework基金会支持。许多行业领先的公司在其软件开发中使用该工具。

Robot Framework是开放且可扩展的。Robot Framework可以与几乎任何其他工具集成,以创建强大且灵活的自动化解决方案。Robot Framework可以免费使用,无需许可费用。

Robot Framework具有易于理解的语法,利用人类可读的关键字。其功能可以通过使用Python、Java或许多其他编程语言实现的库来扩展。Robot Framework周围有一个丰富的生态系统,包括作为单独项目开发的库和工具。

Robot Framework的开发由非营利性的Robot Framework基金会资助。它由希望确保Robot Framework现在和将来持续发展的公司和组织组成。Robot Framework仍然完全免费使用,并且在积极进行维护和开发。基金会还覆盖其他相关费用,如开发此网站和组织各种聚会。

基金会成员:

为什么选择 Robot Framework?

  • 提供了易于使用的表格语法,以统一的方式创建测试用例。
  • 允许从现有关键字创建可重用的高级关键字。
  • 提供易于阅读的以 HTML 格式的结果报告和日志。
  • 平台和应用程序无关。
  • 提供了一个简单的库 API,用于创建可以使用 Python 原生实现的定制测试库。
  • 提供了命令行界面和基于 XML 的输出文件,用于集成到现有的构建基础设施(连续集成系统)中。
  • 支持测试 Web 应用程序、REST API、移动应用程序、运行的进程、通过 Telnet 或 SSH 连接到远程系统等。
  • 支持创建数据驱动的测试用例。
  • 具有内置的变量支持,特别适用于在不同环境中进行测试。
  • 提供标记以对测试用例进行分类和选择执行。
  • 可以与源代码控制轻松集成:测试套件只是可以与生产代码一起进行版本控制的文件和目录。
  • 提供了测试用例和测试套件级别的设置和拆卸。
  • 模块化架构支持为具有多个不同接口的应用程序创建测试。

高级架构

Robot Framework 是一个通用的、与应用程序和技术无关的框架。它具有高度模块化的架构,如下图所示:

测试数据以简单、易于编辑的表格格式存在。当启动 Robot Framework 时,它会处理数据,执行测试用例,并生成日志和报告。核心框架对被测试对象一无所知,与其交互由库处理。库可以直接使用应用程序接口,也可以使用较低级别的测试工具作为驱动程序。

截屏

以下截屏展示了测试数据以及生成的报告和日志的示例。

测试用例文件:

报告和日志:

获取更多信息

项目页面

有关 Robot Framework 及其丰富生态系统的更多信息,首要去处是 http://robotframework.org 。Robot Framework 本身托管在 GitHub 上。

邮件列表

有几个 Robot Framework 邮件列表可供询问和搜索更多信息。邮件列表存档对所有人(包括搜索引擎)开放,每个人也可以自由加入这些列表。但只有列表成员才能发送邮件,为了防止垃圾邮件,新用户需要经过审核,这意味着的第一条消息可能需要一些时间才能通过。不要害怕向邮件列表发送问题,但请记住如何聪明地提问。

  • robotframework-users(Robot Framework 用户):关于所有与 Robot Framework 相关问题的一般讨论。可以将问题和问题发送到此列表。也用于向所有用户共享信息。
  • robotframework-announce(Robot Framework 公告):仅供公告的邮件列表,只有管理员才能发送消息。所有公告也发送到 robotframework-users 邮件列表,因此没有必要加入两个列表。
  • robotframework-devel(Robot Framework 开发):关于 Robot Framework 开发的讨论。

版权和许可证

Robot Framework 是一款开源软件,采用 Apache License 2.0 提供。Robot Framework 的文档,例如本用户指南,使用 Creative Commons Attribution 3.0 Unported 许可证。围绕该框架的更大生态系统中的大多数库和工具也是开源的,但它们可能使用不同的许可证。

完整的 Robot Framework 版权声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Copyright 2008-2015 Nokia Networks
Copyright 2016- Robot Framework Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

根据 Apache 许可证第 2.0 版(“许可证”)许可;除非符合许可证的规定,否则不得使用此文件。可以在以下网址获取许可证的副本:http://www.apache.org/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据许可证分发的软件以“原样”分发,不附带任何明示或暗示的保证或条件。请参阅许可证以了解特定语言下的权限和限制。

安装指南

这些指南涵盖了在不同操作系统上安装Robot Framework及其前置条件的步骤。如果已经安装了Python,可以使用标准的软件包管理器pip来安装Robot Framework:

1
pip install robotframework

Python 安装

Robot Framework 是使用 Python 实现的,安装它的前提条件是已经安装了 Python 或其替代实现 PyPy。另一个推荐的前提条件是有 pip 包管理器可用。

Robot Framework 需要 Python 3.8 或更新版本。支持 Python 3.63.7 的最新版本是 Robot Framework 6.1.1。如果需要使用 Python 2JythonIronPython,可以使用 Robot Framework 4.1.3

在 Linux 上安装 Python

在 Linux 上,默认应该已经安装了适合的 Python 版本和 pip。如果没有,需要查阅发行版的文档来了解如何安装它们。如果想使用默认提供的 Python 版本之外的其他版本,这同样适用。

要检查已安装的 Python 版本,可以在终端运行 python --version 命令:

1
2
$ python --version
Python 3.10.13

注意,如果的发行版也提供了较旧的 Python 2,运行 python 可能会使用那个。要使用 Python 3,可以使用 python3 命令,或者更具体的版本命令,如 python3.8。如果安装了多个 Python 3 版本,也需要使用这些具体版本的变体来确定使用哪一个:

1
2
3
4
$ python3.11 --version
Python 3.11.7
$ python3.12 --version
Python 3.12.1

直接在系统提供的 Python 下安装 Robot Framework 存在风险,可能的问题会影响到操作系统本身也在使用的整个 Python 安装。现在的 Linux 发行版通常默认使用用户安装来避免此类问题,但用户也可以自己决定使用虚拟环境。

Windows 上的 Python 安装

  • Windows 默认不提供 Python,但安装起来很简单。推荐使用官方 Windows 安装程序,可在 http://python.org 获取。
  • 安装 Python 时,建议将 Python 添加到 PATH,以便从命令行更容易执行 Python 及其工具,如 pip 和 Robot Framework。使用官方安装程序时,只需在第一个对话框中选择 Add Python 3.x to PATH 复选框。

验证 Python 安装是否成功并已添加到 PATH:

1
2
C:\>python --version
Python 3.10.9
  • 如果在 Windows 上安装了多个 Python 版本,执行 python 时使用的是 PATH 中排在首位的版本。要使用其他版本,最简单的方法是使用 py 启动器:
1
2
3
4
C:\>py --version
Python 3.10.9
C:\>py -3.12 --version
Python 3.12.1

macOS 上的 Python 安装

  • macOS 默认不提供与 Python 3 兼容的 Python 版本,需要单独安装。推荐使用官方 macOS 安装程序,可在 http://python.org 获取。如果使用包管理器如 Homebrew,也可以通过它安装 Python。

PyPy 安装

  • PyPy 是 Python 的替代实现。与标准 Python 实现相比,其主要优势是可能更快且使用更少内存,但这取决于使用的上下文。如果执行速度重要,至少测试 PyPy 是个好主意。

PyPy 安装是一个简单的过程,可以在 http://pypy.org 找到安装程序和安装说明。验证 PyPy 安装是否成功:

1
2
$ pypy --version
$ pypy3 --version

注意:仅在 Linux 上官方支持使用 PyPy 的 Robot Framework。

配置 PATH

  • PATH 环境变量列出了系统中执行命令时搜索的目录。为了便于从命令行使用 Python、pip 和 Robot Framework,建议将 Python 安装目录以及 pip 和 robot 等命令安装的目录添加到 PATH。
  • 在 Linux 或 macOS 上使用 Python 时,Python 及其安装的工具应自动在 PATH 中。如果仍需更新 PATH,则通常需要编辑某些系统范围或用户特定的配置文件。具体编辑哪个文件以及如何编辑取决于操作系统,需要查阅其文档了解更多详情。
  • 在 Windows 上,确保 PATH 配置正确的最简单方法是在运行安装程序时勾选 Add Python 3.x to PATH 复选框。手动修改 Windows 上的 PATH,请按照以下步骤操作:
  1. 在设置中找到环境变量。有影响整个系统的变量和仅影响当前用户的变量。修改前者将需要管理员权限,但通常只需修改后者。
  2. 选择 PATH(通常写作 Path)并点击编辑。如果正在编辑用户变量且 PATH 不存在,则点击新建。
  3. 将 Python 安装目录和安装目录下的 Scripts 目录都添加到 PATH 中。
  4. 点击确定退出对话框以保存更改。
  5. 启动新的命令提示符以使更改生效。

使用 pip 安装

pip 命令的运行

  • 在 Linux 上,可能需要使用 pip3 或更具体的 Python 版本命令,如 pip3.8
  • 在 Windows 上,当执行 python 时,PATH 中首先找到的版本将被使用。如果需要使用其他版本,可以运行 py 启动器。

验证 pip 是否可用

  • Linux 示例:
1
2
3
4
$ pip --version
pip 23.2.1 from ... (python 3.10)
$ python3.12 -m pip --version
pip 23.3.1 from ... (python 3.12)
  • Windows 示例:
1
2
3
4
C:\> pip --version
pip 23.2.1 from ... (python 3.10)
C:\> py -m 3.12 -m pip --version
pip 23.3.2 from ... (python 3.12)

安装与卸载 Robot Framework

  • 从 Python 包索引 (PyPI) 安装和下载包是 pip 的常用方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 安装最新版本(不升级)
pip install robotframework

# 升级到最新稳定版本
pip install --upgrade robotframework

# 升级到最新版本,即使它是预发布版本
pip install --upgrade --pre robotframework

# 安装特定版本
pip install robotframework==7.0

# 安装单独下载的包(无需网络连接)
pip install robotframework-7.0-py3-none-any.whl

# 直接从 GitHub 安装最新(可能未发布)的代码
pip install https://github.com/robotframework/robotframework/archive/master.zip

# 卸载
pip uninstall robotframework

从源代码安装

如果没有pip可用,可以通过下载PyPI上的zip包并解压,或者克隆GitHub仓库并检出所需的发布标签来获取Robot Framework源代码。

安装源代码的命令如下:

1
python setup.py install

setup.py 脚本接受多个参数,例如允许安装到不需要管理员权限的非默认位置。此外,该脚本也用于创建不同的发行包。运行 python setup.py --help 可以获取更多详情。

验证安装

为了确保安装了正确版本的Robot Framework,可以运行以下命令:

1
$ robot --version

如果出现命令未找到或无法识别的消息,首先应该检查PATH配置。

如果在多个Python版本下安装了Robot Framework,运行 robot 将执行PATH中的第一个。要显式选择,可以运行 python -m robot 并用正确的Python版本替换 python

例如:

1
$ python3.12 -m robot --version

或在Windows上:

1
C:\>py -3.11 -m robot --version

虚拟环境

Python虚拟环境允许将Python包安装在特定系统或应用程序的隔离位置,而不是将所有包安装在同一全局位置。主要有两个用途:

  • 为不同项目将所需包安装在各自的环境中,避免如果项目需要不同版本的同一包时发生冲突。
  • 避免在全局Python安装下安装所有内容。这在Linux上尤其重要,因为全局Python安装可能被发行版本身使用,搞乱它可能会导致严重问题。

设置集成开发环境

IDE

  • Visual Studio Code
    • 代码编辑。重新定义。免费。基于开源。无处不在。
  • PyCharm
    • 专业开发人员的Python集成开发环境

安装IDE和扩展

许多IDE和代码编辑器支持开发Robot Framework测试。

支持范围从简单的代码高亮、代码完成到测试执行和调试。

这里列出了几个IDE和扩展,但还有很多其他的选择。

Visual Studio Code

由Microsoft开发的开源轻量级代码编辑器。它提供了许多不同语言和技术的扩展。

VS Code

针对Robot Framework的常用扩展:

  • Robot Code
  • Robot Framework Language Server

安装Visual Studio Code

查看VS Code设置指南以获取更多信息。

  1. 从Visual Studio Code下载Windows安装程序
  2. 使用默认设置运行安装程序
  3. 启动Visual Studio Code,方式有:
    • 按下 Windows键 + R,键入 code,然后按 Enter
    • 打开开始菜单,搜索 Visual Studio Code并单击打开

Visual Studio Code扩展

注意

确保仅安装了一个Robot Framework扩展。

RobotCode是Visual Studio Code的一个Robot Framework扩展。

  1. 打开Visual Studio Code
  2. 点击扩展图标或按 Ctrl + Shift + X
  3. 搜索 RobotCode并点击安装
  4. 点击安装

Robot Framework Language Server是Visual Studio Code的一个Robot Framework扩展。

  1. 打开Visual Studio Code
  2. 点击扩展图标或按 Ctrl + Shift + X
  3. 搜索 Robot Framework Language Server并点击安装
  4. 点击安装

PyCharm

PyCharm是一个免费开源的Python集成开发环境。

针对Robot Framework的常用扩展:

  • Robot Framework Language Server

安装PyCharm

查看PyCharm安装指南以获取更多信息。

有两种安装方法:

  1. 推荐使用JetBrains的Toolbox App。
  2. 替代的独立安装方法

PyCharm扩展

注意

确保仅安装了一个Robot Framework扩展。

Robot Framework Language Server

  1. Ctrl + Alt + S打开设置对话框
  2. 选择 插件
  3. 选择 市场选项卡
  4. 搜索 Robot Framework Language Server并点击安装
  5. 为Robot Framework添加调试配置以运行当前测试套件
  1. 为Robot Framework添加调试配置以运行当前测试用例(通过选择的文本)

注意

如果控制台输出问号或者乱码则在环境变量(Environment variables) 添加编码变量PYTHONIOENCODING=UTF-8

运行和调试Robot Framework测试

使用Visual Studio Code和RobotCode

运行

调试

  • 在测试套件或资源文件中点击行的左侧添加断点。
  • 右键单击播放按钮
  • 从上下文菜单中选择“调试测试”,测试执行将开始,并在断点处停止。
  • 使用调试工具栏选择操作,如单步执行、进入、跳出、继续、暂停等。
  • 在调试控制台中调试时运行Robot Framework命令。
  • 命令需要以!开头。

使用PyCharm和Robot Framework Language Server

运行

  • 运行当前测试套件
  • 运行当前测试用例(通过选择的文本)

调试

  • 调试测试

使用Poetry管理依赖

Poetry是Python的一个依赖管理和打包工具。

它允许声明项目所依赖的库,并会为管理(安装/更新)它们。在开始任何新的Python或Robot Framework项目时,都可以使用它。

按照官方指南安装Poetry。

在PowerShell中运行以下命令:

1
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

如果通过Microsoft Store安装了Python,将上述命令中的py替换为python。

运行一次poetry以检查是否已安装。

1
poetry --version

虚拟环境文件夹

Poetry会在 .poetry/envs文件夹中保存虚拟环境。可以更改该设置,并通过运行以下命令将虚拟环境存储在项目文件夹中:

1
poetry config virtualenvs.in-project true

初始化新项目

要初始化新项目,在终端中运行以下命令:

1
poetry init

Poetry会询问一些关于项目的问题。可以通过按Enter键留下默认值,或者输入自己的值。

示例:

此命令将引导创建 pyproject.toml配置。

1
2
3
4
5
6
7
8
9
Package name [your-project-name]:
Version [0.1.0]:
Description []:
Author [Your Name <[email protected]>, n to skip]:
License []:
Compatible Python versions [^3.10]:

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no

初始化后,会在项目文件夹中看到一个名为 pyproject.toml的新文件。此文件包含有关项目和依赖项的所有信息。

添加依赖项

要添加依赖项,在终端中运行以下命令:

1
poetry add robotframework

也可以一次添加多个依赖项:

1
poetry add robotframework robotframework-browser robotframework-requests

在首次运行时,poetry将为项目创建一个虚拟环境并安装依赖项。

在虚拟环境中运行命令

要在虚拟环境中运行命令,可以使用 poetry run命令。

1
poetry run robot --version

例如,要安装 robotframework-browser的playwright依赖项,可以运行以下命令:

1
poetry run rfbrowser init

还可以使用 poetry shell命令在虚拟环境中打开一个shell。 在此shell中运行的所有命令都将在虚拟环境中执行。

1
2
poetry shell
(.venv) robot --version

演示

有几个演示项目介绍了Robot Framework并帮助开始使用它:

创建测试数据

测试数据语法

此部分涵盖Robot Framework的整体测试数据语法。以下各节将解释如何实际创建测试用例、测试套件等。尽管本节主要使用测试一词,但创建任务时也适用相同规则。

文件和目录

安排测试用例的层次结构建立如下:

  • 测试用例在套件文件中创建。
  • 测试用例文件自动创建一个包含该文件中测试用例的测试套件。
  • 包含测试用例文件的目录形成一个更高级别的测试套件。这样的套件目录拥有从测试用例文件创建的子测试套件。
  • 测试套件目录也可以包含其他测试套件目录,这种层次结构可以根据需要深入嵌套。
  • 测试套件目录可以有一个特殊的初始化文件,用于配置创建的测试套件。

此外,还包括:

  • 包含最低级关键字的测试库。
  • 带有变量和更高级用户关键字的资源文件。
  • 提供比资源文件更灵活的创建变量方式的变量文件。

测试用例文件、测试套件初始化文件和资源文件都使用Robot Framework测试数据语法创建。测试库和变量文件使用“真实”编程语言创建,通常是Python。

测试数据部分

Robot Framework数据在不同部分定义,这些部分通常也称为表格,如下所列:

部分 用途
Settings 1) 导入测试库、资源文件和变量文件。
2) 定义测试套件和测试用例的元数据。
Variables 定义可以在测试数据中其他地方使用的变量。
Test Cases 从可用关键字创建测试用例。
Tasks 使用可用关键字创建任务。单个文件只能包含测试或任务。
Keywords 从现有的低级关键字创建用户关键字。
Comments 额外的评论或数据。被Robot Framework忽略。

不同部分通过其标题行被识别。推荐的标题格式是 *** Settings***,但标题不区分大小写,周围的空格是可选的,星号字符的数量可以变化,只要开头至少有一个星号。例如,*Settings也会被识别为部分标题。

Robot Framework也支持单数标题,如 *** Setting***,但该支持在Robot Framework 6.0中已被弃用。从Robot Framework 7.0开始会有可见的弃用警告,最终单数标题将完全不被支持。

标题行也可以包含实际部分标题以外的其他数据。额外的数据必须使用数据格式依赖的分隔符与部分标题分开,通常是两个或更多空格。这些额外的标题在解析时会被忽略,但它们可以用于文档记录目的。在使用数据驱动风格创建测试用例时,这特别有用。

第一部分之前的可能数据被忽略。

注意

部分标题可以本地化。有关支持的翻译,请参阅翻译。

支持的文件格式

创建Robot Framework数据的最常见方法是使用空格分隔的格式,其中数据的各个部分,例如关键字及其参数,用两个或更多空格分隔。

另一种方法是使用管道分隔的格式,其中分隔符是用空格括起来的管道字符 |

套件文件通常使用 .robot扩展名,但可以配置解析哪些文件。资源文件也可以使用 .robot扩展名,但建议使用专用的 .resource扩展名,并且在将来可能会被强制执行。

包含非ASCII字符的文件必须使用UTF-8编码保存。

Robot Framework还支持reStructuredText文件,以便将普通的Robot Framework数据嵌入到代码块中。默认情况下,仅解析具有 .robot.rst扩展名的文件。如果更喜欢使用 .rst.rest扩展名,那么需要单独配置。

Robot Framework数据还可以以JSON格式创建,该格式更针对工具开发人员而不是普通的Robot Framework用户。默认情况下,仅解析具有自定义 .rbt扩展名的JSON文件。

较早的Robot Framework版本还支持HTML和TSV格式的数据。如果数据与空格分隔格式兼容,则TSV格式仍然有效,但完全删除了对HTML格式的支持。如果遇到此类数据文件,需要将其转换为纯文本格式,以便在Robot Framework 3.2或更新版本中使用。最简单的方法是使用Tidy工具,但必须使用Robot Framework 3.1附带的版本,因为更新版本根本不理解HTML格式。

空格分隔格式

当Robot Framework解析数据时,它首先将数据拆分为行,然后将行拆分为诸如关键字和参数之类的标记。在使用空格分隔格式时,标记之间的分隔符是两个或更多空格,或者是一个或多个制表符。除了正常的ASCII空格之外,任何被视为空格的Unicode字符(例如不间断空格)都可以作为分隔符。分隔标记所使用的空格数量可以变化,只要至少有两个,这样在设置和其他地方对齐数据时就可以使数据更易于理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*** Settings ***
Documentation 使用空格分隔格式的示例。
Library OperatingSystem

*** Variables ***
${MESSAGE} Hello, world!

*** Test Cases ***
My Test
[Documentation] Example test.
Log ${MESSAGE}
My Keyword ${CURDIR}

Another Test
Should Be Equal ${MESSAGE} Hello, world!

*** Keywords ***
My Keyword
[Arguments] ${path}
Directory Should Exist ${path}

在这个示例中:

  • *** Settings ***:定义了测试或任务的设置。
  • Documentation:提供了对测试或任务的描述。
  • Library:指定了测试或任务中将使用的库。
  • *** Variables ***:声明了在测试中使用的变量。
  • ${MESSAGE}:变量的名称,其值为“好,世界!”。
  • *** Test Cases ***:标记了测试用例的开始。
  • 的测试另一个测试:测试用例的名称。
  • [Documentation]:为测试用例提供了额外的描述。
  • Log:一个关键字,用于记录信息。
  • My Keyword:自定义关键字的名称。
  • Should Be Equal:一个内置关键字,用于断言两个值是否相等。
  • *** Keywords ***:标记了自定义关键字的开始。
  • [Arguments]:定义了关键字的参数。
  • Directory Should Exist:一个内置关键字,用于验证指定路径的目录是否存在。

因为制表符和连续的空格被视为分隔符,所以如果在关键字参数或实际数据的其他位置需要它们,则必须对它们进行转义。可以使用特殊的转义语法,如 \t表示制表符,\xA0表示不间断空格,以及内置变量 ${SPACE}${EMPTY}。详情请参阅转义部分。

信息

虽然使用两个空格作为分隔符就足够了,但建议使用四个空格,以便更容易识别分隔符。

注意

在Robot Framework 3.2之前,数据中使用的非ASCII空格在解析过程中会被转换为ASCII空格。现在所有数据都保持原样。

管道分隔格式

空格分隔格式的最大问题是在视觉上将关键字与参数分开可能会很棘手。这个问题特别突出,特别是如果关键字需要很多参数和/或参数包含空格的情况下。在这种情况下,管道分隔的变体可能会更好,因为它使分隔符更加明显可见。

一个文件可以同时包含空格分隔和管道分隔的行。管道分隔的行通过强制性的前导管道字符进行识别,但行尾的管道是可选的。除了在行的开头和结尾,管道的两侧始终必须至少有一个空格或制表符。不需要对齐管道,但通常这样做会使数据更易于阅读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| *** Settings ***   |
| Documentation | Example using the pipe separated format.
| Library | OperatingSystem

| *** Variables *** |
| ${MESSAGE} | Hello, world!

| *** Test Cases *** | | |
| My Test | [Documentation] | Example test. |
| | Log | ${MESSAGE} |
| | My Keyword | ${CURDIR} |
| Another Test | Should Be Equal | ${MESSAGE} | Hello, world!

| *** Keywords *** | | |
| My Keyword | [Arguments] | ${path} |
| | Directory Should Exist | ${path} |

在使用管道分隔格式时,参数内部的连续空格或制表符不需要转义。同样,空列不需要转义,除非它们位于末尾。然而,在实际测试数据中可能被空格包围的管道必须使用反斜杠进行转义:

1
2
3
| *** Test Cases *** |                 |                 |                      |
| Escaping Pipe | ${file count} = | Execute Command | ls -1 *.txt \| wc -l |
| | Should Be Equal | ${file count} | 42 |
  1. Execute Command 关键字执行了 ls -1 *.txt | wc -l 命令。
  2. ls -1 *.txt 列出了当前目录中所有以 .txt 结尾的文件。
  3. wc -l 统计了文件的行数。

最终,验证了文件的行数是否等于 42。

注意

保留参数中的连续空格和制表符是Robot Framework 3.2的新功能。在此之前,数据中使用的非ASCII空格也会被转换为ASCII空格。

reStructuredText 格式

reStructuredText(reST)是一种易于阅读的纯文本标记语法,通常用于Python项目的文档,包括Python本身以及本用户指南。reST文档最常编译为HTML,但也支持其他输出格式。使用reST与Robot Framework结合使用,允许在简洁的文本格式中混合富格式文档和测试数据,这样就可以使用简单的文本编辑器、差异工具和源代码控制系统轻松地处理。

注意

使用reStructuredText文件与Robot Framework需要安装Python docutils模块。

当使用Robot Framework与reStructuredText文件时,普通的Robot Framework数据被嵌入到所谓的代码块中。在标准reST中,代码块使用代码指令进行标记,但Robot Framework还支持Sphinx工具使用的code-block或sourcecode指令。

reStructuredText 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
reStructuredText example
------------------------

This text is outside code blocks and thus ignored.

.. code:: robotframework

*** Settings ***
Documentation Example using the reStructuredText format.
Library OperatingSystem

*** Variables ***
${MESSAGE} Hello, world!

*** Test Cases ***
My Test
[Documentation] Example test.
Log ${MESSAGE}
My Keyword ${CURDIR}

Another Test
Should Be Equal ${MESSAGE} Hello, world!

Also this text is outside code blocks and ignored. Code blocks not
containing Robot Framework data are ignored as well.

.. code:: robotframework

# Both space and pipe separated formats are supported.

| *** Keywords *** | | |
| My Keyword | [Arguments] | ${path} |
| | Directory Should Exist | ${path} |

.. code:: python

# This code block is ignored.
def example():
print('Hello, world!')

Robot Framework支持使用 .robot.rst.rst.rest扩展名的reStructuredText文件。为了避免解析不相关的reStructuredText文件,当执行目录时,默认情况下仅解析具有 .robot.rst扩展名的文件。可以通过使用 --parseinclude--extension选项来启用解析具有其他扩展名的文件。

当Robot Framework解析reStructuredText文件时,忽略严重级别以下的错误,以避免可能的非标准指令和其他标记的噪音。这也可能隐藏了真正的错误,但在通常使用reStructuredText工具处理文件时可以看到它们。

注意

自动解析 .robot.rst文件是Robot Framework 6.1的新功能。

JSON 格式

Robot Framework 支持 JSON 格式的数据。这种格式主要为工具开发者设计,而不是为常规的 Robot Framework 用户设计,因此不建议手动编辑。它的主要用途包括:

  • 在进程和机器之间传输数据。可以在一台机器上将套件转换为 JSON,然后在其他地方重新创建。
  • 将可能是嵌套套件的套件,从常规的 Robot Framework 数据构建成单个的 JSON 文件,这样解析起来更快。
  • 为生成测试或任务的外部工具提供替代的数据格式。

注意

JSON 数据支持是在 Robot Framework 6.1 中新增的,未来的 Robot Framework 版本中可能会进行增强。

将套件转换为 JSON

可以使用 TestSuite.to_json 方法将套件结构序列化为 JSON。如果没有参数,它会返回 JSON 数据作为字符串,但它也接受一个路径或一个打开的文件,以及与 JSON 格式相关的配置选项:

1
2
3
4
5
6
7
8
9
10
from robot.running import TestSuite

# 基于文件系统上的数据创建套件。
suite = TestSuite.from_file_system('/path/to/data')

# 获取 JSON 数据作为字符串。
data = suite.to_json()

# 将 JSON 数据保存到文件中,自定义缩进。
suite.to_json('data.rbt', indent=2)

如果更愿意使用 Python 数据,然后自己将其转换为 JSON 或其他格式,可以使用 TestSuite.to_dict

从 JSON 创建套件

可以使用 TestSuite.from_json 方法从 JSON 数据构建套件。它既可以处理 JSON 字符串,也可以处理 JSON 文件的路径:

1
2
3
4
5
6
7
8
9
10
from robot.running import TestSuite

# 从文件中的 JSON 数据创建套件。
suite = TestSuite.from_json('data.rbt')

# 从 JSON 字符串创建套件。
suite = TestSuite.from_json('{"name": "Suite", "tests": [{"name": "Test"}]}')

# 执行套件。注意,日志和报告需要单独创建。
suite.run(output='example.xml')

如果有一个 Python 字典的数据,可以使用 TestSuite.from_dict。无论套件是如何重新创建的,它只存在于内存中,原始的数据文件在文件系统上不会被重新创建。

如上例所示,可以使用 TestSuite.run 方法执行创建的套件。然而,直接执行 JSON 文件可能更容易,如下一节所述。

执行 JSON 文件

当使用 robot 命令执行测试或任务时,带有自定义 .rbt 扩展名的 JSON 文件会被自动解析。这包括运行像 robot tests.rbt 这样的单个 JSON 文件,以及运行包含 .rbt 文件的目录。如果更愿意使用标准的 .json 扩展名,需要配置哪些文件需要解析。

调整套件源

TestSuite.to_jsonTestSuite.to_dict 获取的数据中的套件源是绝对格式的。如果稍后在不同的机器上重新创建套件,源可能不匹配该机器上的目录结构。为了避免这种情况,可以使用 TestSuite.adjust_source 方法在获取数据之前使套件源相对化,并在套件重新创建后添加正确的根目录:

1
2
3
4
5
6
7
8
9
10
from robot.running import TestSuite

# 创建套件,调整源并转换为 JSON。
suite = TestSuite.from_file_system('/path/to/data')
suite.adjust_source(relative_to='/path/to')
suite.to_json('data.rbt')

# 在其他地方重新创建套件并相应地调整源。
suite = TestSuite.from_json('data.rbt')
suite.adjust_source(root='/new/path/to')
JSON 结构

在套件文件中创建的导入项、变量和关键字都包含在生成的 JSON 中,以及测试和任务。具体的 JSON 结构在 running.json 架构文件中有文档说明。

数据解析规则

忽略的数据

当 Robot Framework 解析测试数据文件时,它会忽略:

  • 第一个测试数据部分之前的所有数据。
  • 评论部分中的数据。
  • 所有空行。
  • 当使用管道分隔格式时,行尾的所有空单元格。
  • 当不用于转义时,所有的单个反斜杠(\)。
  • 当它是单元格的第一个字符时,井号(#)后面的所有字符。这意味着可以使用井号在测试数据中输入注释。

当 Robot Framework 忽略一些数据时,这些数据在任何生成的报告中都不可用,此外,大多数与 Robot Framework 一起使用的工具也会忽略它们。要添加在 Robot Framework 输出中可见的信息,请将其放在测试用例或套件的文档或其他元数据中,或使用 BuiltIn 关键字 Log 或 Comment 进行记录。

转义

Robot Framework 测试数据中的转义字符是反斜杠 \,此外,内置变量 EMPTY和{SPACE} 通常也可以用于转义。下面的各节将讨论不同的转义机制。

转义特殊字符

反斜杠字符可以用来转义特殊字符,以便使用它们的字面值:

字符 含义 示例
$ 美元符号,不会开始标量变量。 ${notvar}
@ 电子邮件符号,不会开始列表变量。 @{notvar}
& 和号,不会开始字典变量。 &{notvar}
% 百分号,不会开始环境变量。 %{notvar}
# 井号,不会开始注释。 # not comment
= 等号,不会成为命名参数语法的一部分。 not=named
| 管道字符,不是管道分隔格式的分隔符。 ls -1 *.txt| wc -l
\ 反斜杠字符,不会转义任何东西。 c:\temp, \${var}
形成转义序列

反斜杠字符也允许创建特殊的转义序列,这些序列被识别为在测试数据中难以或无法创建的字符:

转义序列:

序列 含义 示例
\n 换行符 first line\n2nd line
\r 回车符 text\rmore text
\t 制表符 text\tmore text
\xhh 十六进制值为 hh 的字符 空字节:\x00,ä:\xE4
\uhhhh 十六进制值为 hhhh 的字符 雪人:\u2603
\Uhhhhhhhh 十六进制值为 hhhhhhhh 的字符 爱情旅馆:\U0001f3e9

注意

在测试数据中创建的所有字符串,包括像 \x02 这样的字符,都是 Unicode,如果需要,必须显式地转换为字节字符串。这可以通过使用 BuiltIn 和 String 库中的 Convert To BytesEncode String To Bytes 关键字来完成,或者在 Python 代码中使用类似于 value.encode('UTF-8') 的语句来完成。

注意

如果在 \x\u\U 转义中使用了无效的十六进制值,最终的结果是原始值,但没有反斜杠字符。例如,\xAX(不是十六进制)和 \U00110000(值过大)分别会得到 xAXU00110000。尽管如此,这种行为在未来可能会改变。

注意

如果需要操作系统依赖的行终止符,可以使用内置变量 ${\n}(在 Windows 上是 \r\n,在其他地方是 \n)。

处理空值

当使用空格分隔格式时,用作分隔符的空格数量可以变化,因此除非进行转义,否则无法识别空值。空单元格可以用反斜杠字符或内置变量 ${EMPTY} 进行转义。通常推荐使用后者,因为它更容易理解。

1
2
3
4
5
6
7
8
*** Test Cases ***
Using backslash
Do Something first arg \
Do Something \ second arg

Using ${EMPTY}
Do Something first arg ${EMPTY}
Do Something ${EMPTY} second arg

当使用管道分隔格式时,只有当空值位于行尾时,才需要对空值进行转义:

1
2
3
4
5
6
| *** Test Cases *** |              |           |            |
| Using backslash | Do Something | first arg | \ |
| | Do Something | | second arg |
| | | | |
| Using ${EMPTY} | Do Something | first arg | ${EMPTY} |
| | Do Something | | second arg |

处理空格

空格,尤其是连续的空格,作为关键字的参数或者其他需要的一部分,会有两个问题:

  • 当使用空格分隔格式时,两个或更多连续的空格被视为分隔符。
  • 当使用管道分隔格式时,忽略前导和尾随空格。

在这些情况下,需要对空格进行转义。与转义空值类似,可以通过使用反斜杠字符或使用内置变量 ${SPACE} 来实现。

转义空格示例:

使用反斜杠转义 使用 ${SPACE} 转义 注释
\ leading space ${SPACE}leading space
trailing space \ trailing space ${SPACE} 反斜杠必须在空格后面。
\ \ ${SPACE} 两边都需要反斜杠。
consecutive \ \ spaces consecutive ${SPACE * 3}spaces 使用扩展变量语法。

如上例所示,使用 ${SPACE} 变量通常使测试数据更容易理解。当需要多个空格时,它与扩展变量语法结合使用特别方便。

将数据分割到多行

如果一行的数据过多,可以将其分割,并使用省略号(…)开始连续的行。省略号可以缩进以匹配起始行的缩进,并且必须始终跟随正常的测试数据分隔符。

在大多数地方,分割的行与未分割的行具有完全相同的语义。此规则的例外是套件、测试和关键字文档以及套件元数据。对于它们,分割的值会自动用换行符连接在一起,以便创建多行值。

... 语法也允许在变量部分分割变量。当长标量变量(例如 ${STRING})被分割到多行时,最终的值是通过将行连接在一起得到的。默认的分隔符是空格,但可以通过以 SEPARATOR=<sep> 开始值来更改。

以下两个示例说明了分割行,它们包含了完全相同的数据,但一个没有分割,一个进行了分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*** Settings ***
Documentation Here we have documentation for this suite.\nDocumentation is often quite long.\n\nIt can also contain multiple paragraphs.
Default Tags default tag 1 default tag 2 default tag 3 default tag 4 default tag 5

*** Variables ***
${STRING} This is a long string. It has multiple sentences. It does not have newlines.
${MULTILINE} This is a long multiline string.\nThis is the second line.\nThis is the third and the last line.
@{LIST} this list is quite long and items in it can also be long
&{DICT} first=This value is pretty long. second=This value is even longer. It has two sentences.

*** Test Cases ***
Example
[Tags] you probably do not have this many tags in real life
Do X first argument second argument third argument fourth argument fifth argument sixth argument
${var} = Get X first argument passed to this keyword is pretty long second argument passed to this keyword is long too
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
*** Settings ***
Documentation Here we have documentation for this suite.
... Documentation is often quite long.
...
... It can also contain multiple paragraphs.
Default Tags default tag 1 default tag 2 default tag 3
... default tag 4 default tag 5

*** Variables ***
${STRING} This is a long string.
... It has multiple sentences.
... It does not have newlines.
${MULTILINE} SEPARATOR=\n
... This is a long multiline string.
... This is the second line.
... This is the third and the last line.
@{LIST} this list is quite long and
... items in it can also be long
&{DICT} first=This value is pretty long.
... second=This value is even longer. It has two sentences.

*** Test Cases ***
Example
[Tags] you probably do not have this many
... tags in real life
Do X first argument second argument third argument
... fourth argument fifth argument sixth argument
${var} = Get X
... first argument passed to this keyword is pretty long
... second argument passed to this keyword is long too

本地化

Robot Framework 的本地化工作始于 Robot Framework 6.0,它允许翻译部分标题、设置、在行为驱动开发(BDD)中使用的 Given/When/Then 前缀,以及在自动布尔参数转换中使用的 true 和 false 字符串。未来计划扩展本地化支持,例如,到日志和报告,甚至可能也包括控制结构。

本节解释如何激活语言,支持哪些内置语言,如何创建自定义语言文件,以及如何贡献新的翻译。

启用语言

使用命令行选项 激活语言的主要机制是使用 --language 选项从命令行指定它们。当启用内置语言时,可以使用语言名称(如 Chinese Simplified)或语言代码(如 fi)。名称和代码都不区分大小写和空格,也忽略连字符(-)。要启用多种语言,需要多次使用 --language 选项:

1
2
robot --language Chinese Simplified testit.robot
robot --language pt --language ptbr testes.robot

当激活自定义语言文件时,也使用相同的 --language 选项。对于它们,值可以是文件的路径,或者,如果文件在模块搜索路径中,可以是模块名称:

1
2
robot --language Custom.py tests.robot
robot --language MyLang tests.robot

出于向后兼容性的原因,以及为了支持部分翻译,英语总是自动激活的。未来的版本可能允许禁用它。

预文件配置 也可以直接在数据文件中启用语言,方法是在任何部分标题之前有一行 Language: <value>(不区分大小写)。冒号后的值与 --language 选项的解释方式相同:

1
2
3
4
Language: Chinese Simplified

*** Asetukset ***
Dokumentaatio Example using Chinese Simplified.

如果需要启用多种语言,可以重复 Language: 行。这些配置行不能在注释中,所以像 # Language: Chinese Simplified 这样的内容没有效果。

由于技术限制,每个文件的语言配置也会影响后续文件的解析以及整个执行。这种行为可能在未来会改变,因此不应依赖它。如果使用每个文件的配置,应该在所有文件中使用它,或者使用 --language 选项全局启用语言。

自定义语言文件

如果需要的语言不是内置语言,或者想为某些特定需求创建一个完全自定义的语言,可以轻松地创建一个自定义语言文件。语言文件是 Python 文件,包含一个或多个语言定义,当语言文件被使用时,所有的语言定义都会被加载。语言定义是通过扩展 robot.api.Language 基类并根据需要覆盖类属性来创建的:

1
2
3
4
5
6
7
from robot.api import Language

class Example(Language):
test_cases_header = 'Validations'
tags_setting = 'Labels'
given_prefixes = ['Assuming']
true_strings = ['OK', '\N{THUMBS UP SIGN}']

假设上述代码位于 example.py 文件中,当激活语言文件时,可以使用该文件的路径或仅使用模块名称 example

上述示例只添加了一些可能的翻译。这是可以的,因为英语总是会自动启用。大多数值必须指定为字符串,但 BDD 前缀和 true/false 字符串允许多个值,并且必须给出列表。有关更多示例,请参见 Robot Framework 的内部语言模块,该模块包含 Language 类以及所有内置语言定义。

创建测试用例

当创建测试用例时,通常会采用以下的测试用例语法。将测试用例组织成测试套件,可以使用套件文件和套件目录,具体内容请参阅下一节。

当在使用机器人框架进行除了测试自动化以外的其他自动化任务时,建议创建任务而不是测试。任务的语法在大部分情况下与测试的语法相同,不同之处将在创建任务部分进行解释。

测试用例语法

基本语法

测试用例是在测试用例部分中从可用关键字构建的。关键字可以从测试库或资源文件导入,或者在测试用例文件本身的关键字部分中创建。

测试用例部分的第一列包含测试用例名称。测试用例从此列中有内容的行开始,并继续到下一个测试用例名称或部分的结束。在部分标题和第一个测试之间有内容是错误的。

第二列通常有关键字名称。此规则的一个例外是从关键字返回值设置变量,当第二列和可能的后续列包含变量名称,并且关键字名称位于它们之后。在任一情况下,关键字名称后的列包含指定关键字的可能参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Test Cases ***
Valid Login
Open Login Page
Input Username demo
Input Password mode
Submit Credentials
Welcome Page Should Be Open

Setting Variables
Do Something first argument second argument
${value} = Get Some Value
Should Be Equal ${value} Expected value
Note

注意

虽然测试用例名称可以包含任何字符,但通常不建议使用 ? 和尤其是 *,因为它们在选择测试用例时被视为通配符。例如,尝试只运行名为 Example * 的测试,如 --test 'Example *',实际上会运行以 Example 开头的任何测试。

在测试用例部分的设置

测试用例也可以有自己的设置。设置名称总是在第二列,通常是关键字的位置,它们的值在后续的列中。设置名称周围有方括号,以便将它们与关键字区分开来。下面列出了可用的设置,并在本节后面进行了解释。

[Documentation]

[文档] 用于指定测试用例文档。

[Setup], [Teardown]

[设置], [拆卸] 指定测试设置和拆卸。

[Tags]

[标签] 用于标记测试用例。

[Template]

[模板] 指定要使用的模板关键字。测试本身将只包含用作该关键字参数的数据。

[Timeout]

[超时] 用于设置测试用例超时。超时在其自己的部分中进行了讨论。

注意

设置名称不区分大小写,但推荐使用上述格式。设置曾经也不区分空格,但在 Robot Framework 3.1 中已被弃用,尝试使用像 [T a g s] 这样的东西会在 Robot Framework 3.2 中引发错误。仍然允许在括号和名称之间可能存在的空格(例如 [ 标签 ])。

带有设置的测试用例示例:

1
2
3
4
5
*** Test Cases ***
Test With Settings
[Documentation] Another dummy test
[Tags] dummy owner-johndoe
Log Hello, world!
在设置部分的测试用例相关设置

设置部分可以有以下与测试用例相关的设置。这些设置主要是前面列出的特定于测试用例的设置的默认值。

测试设置,测试拆卸 测试设置和拆卸的默认值。 测试标签 套件中所有测试将获得的标签,除了它们可能自己的标签。 测试模板 要使用的默认模板关键字。 测试超时 测试用例超时的默认值。超时在其自己的部分中进行了讨论。

[Test Setup, Test Teardown]

[测试设置,测试拆卸] 默认值为测试设置和拆卸。

[Test Tags]

[测试标签] 套件中所有测试将获得的标签,除了它们可能自己的标签。

[Test Template]

[测试模板] 要使用的默认模板关键字。

[Test Timeout]

[测试超时] 测试用例超时的默认值。超时在其自己的部分中进行了讨论。

使用参数

前面的例子已经展示了关键字如何接受不同的参数,本节将更全面地讨论这一重要功能。如何实际实现带有不同参数的用户定义关键字和库关键字将在后续章节中讨论。

关键字可以接受零个或多个参数,某些参数可能具有默认值。关键字接受哪些参数取决于其实现,通常最好的信息来源是关键字的文档。在本节的例子中,文档预期是使用Libdoc工具生成的,但相同的信息也可在使用通用文档工具如pydoc生成的文档中找到。

位置参数

大多数关键字都有一定数量的参数,必须始终给出。在关键字文档中,这通过指定用逗号分隔的参数名表示,如 first, second, third。在这种情况下,参数名实际上并不重要,除了它们应该解释参数的作用,但重要的是必须有与文档中指定的完全相同数量的参数。使用的参数太少或太多都会导致错误。

下面的测试使用了来自OperatingSystem 库的关键字 Create DirectoryCopy File。它们的参数分别指定为 pathsource, destination,这意味着它们分别接受一个和两个参数。最后一个关键字,来自BuiltIn的 No Operation,不接受任何参数。

1
2
3
4
5
*** Test Cases ***
Example
Create Directory ${TEMPDIR}/stuff
Copy File ${CURDIR}/file.txt ${TEMPDIR}/stuff
No Operation
默认值

参数通常具有默认值,可以给出或不给出。在文档中,默认值通常与参数名称用等号分隔,如 name=default value。所有参数都有默认值是可能的,但在具有默认值的参数之后不能有任何位置参数。

使用默认值的示例如下所示,其中使用了 Create File 关键字,其参数为 pathcontent=encoding=UTF-8。尝试在没有任何参数或多于三个参数的情况下使用它将无法正常工作。

1
2
3
4
5
*** Test Cases ***
Example
Create File ${TEMPDIR}/empty.txt
Create File ${TEMPDIR}/utf-8.txt Hyvä esimerkki
Create File ${TEMPDIR}/iso-8859-1.txt Hyvä esimerkki ISO-8859-1
可变数量的参数

也有可能某个关键字接受任意数量的参数。这些所谓的变长参数可以与必需参数和具有默认值的参数组合使用,但它们始终在它们之后给出。在文档中,它们的参数名称前面有一个星号,如 *varargs。

例如,操作系统库中的 Remove FilesJoin Paths 关键字具有参数 paths 和 base、parts,分别。前者可以接受任意数量的参数,但后者至少需要一个参数。

1
2
3
4
*** Test Cases ***
Example
Remove Files ${TEMPDIR}/f1.txt ${TEMPDIR}/f2.txt ${TEMPDIR}/f3.txt
@{paths} = Join Paths ${TEMPDIR} f1.txt f2.txt f3.txt f4.txt

在这个示例中:

  • *** Test Cases ***:定义了测试用例的开始。
  • Example:测试用例的名称。
  • Remove Files ${TEMPDIR}/f1.txt ${TEMPDIR}/f2.txt ${TEMPDIR}/f3.txt:执行了一个关键字,删除了指定的文件。
  • @{paths} = Join Paths ${TEMPDIR} f1.txt f2.txt f3.txt f4.txt:执行了另一个关键字,将路径组合在一起。
命名参数

命名参数语法使得使用具有默认值的参数更加灵活,并允许明确标记某个参数值的含义。从技术上讲,命名参数的工作方式与Python中的关键字参数完全相同。

基本语法

可以通过在值前加上参数名(如 arg=value)来命名给关键字的参数。当多个参数具有默认值时,这特别有用,因为可以只命名一些参数,让其他参数使用其默认值。例如,如果一个关键字接受参数 arg1=aarg2=barg3=c,并且它被调用时只有一个参数 arg3=override,那么参数 arg1arg2 将获得其默认值,但 arg3 将获得值 override。如果这听起来很复杂,下面的命名参数示例应该会使其更清晰。

命名参数语法对大小写和空格都很敏感。前者意味着,如果有一个参数 arg,必须像 arg=value 这样使用它,而 Arg=valueARG=value 都不行。后者意味着,在 = 符号前不允许有空格,可能在其后的空格将被视为给定值的一部分。

当命名参数语法与用户关键字一起使用时,必须在不带 ${} 装饰的情况下给出参数名。例如,带有参数 ${arg1}=first${arg2}=second 的用户关键字必须像 arg2=override 这样使用。

在命名参数之后使用普通位置参数,例如 | Keyword | arg=value | positional |,是不行的。命名参数的相对顺序并不重要。

命名参数与变量

在命名参数的名称和值中都可以使用变量。如果值是单个标量变量,它将按原样传递给关键字。这允许在使用命名参数语法时,也可以使用任何对象,而不仅仅是字符串,作为值。例如,调用像 arg=${object} 这样的关键字将把变量 ${object} 传递给关键字,而不会将其转换为字符串。

如果在命名参数名称中使用变量,变量在与参数名称匹配之前就会被解析。

命名参数语法要求在关键字调用中直接写入等号。这意味着,即使变量具有像 foo=bar 这样的值,单独的变量也永远不会触发命名参数语法。这一点在将关键字包装到其他关键字中时尤其需要记住。例如,如果一个关键字接受像 @{args} 这样的变量数量的参数,并使用相同的 @{args} 语法将所有参数传递给另一个关键字,那么在调用端使用的可能的 named=arg 语法将不会被识别。下面的示例进行了说明。

1
2
3
4
5
6
7
8
*** Test Cases ***
Example
Run Program shell=True # 这将不会作为一个命名参数传递给 Run Process

*** Keywords ***
Run Program
[Arguments] @{args}
Run Process program.py @{args} # 在 @{args} 内部不会识别命名参数

如果关键字需要接受并传递任何命名参数,它必须被改变以接受自由命名参数。参见自由命名参数示例,了解一个可以传递位置参数和命名参数的包装关键字版本。

转义命名参数语法

命名参数语法仅在等号前的参数部分与关键字的某个参数匹配时使用。可能存在一个位置参数,其字面值如 foo=quux,并且还有一个名为 foo 的无关参数。在这种情况下,参数 foo 可能错误地获取值 quux,或者更可能的是,出现语法错误。

在这些偶尔出现的匹配错误的情况下,可以使用反斜杠字符来转义语法,如 foo\=quux。现在,参数将获得字面值 foo=quux。请注意,如果没有名为 foo 的参数,那么不需要转义,但是因为它使情况更加明确,所以可能仍然是个好主意。

命名参数支持的位置

如前所述,命名参数语法适用于关键字。除此之外,它还适用于导入库时。

用户关键字和大多数测试库都支持命名参数。唯一的例外是明确使用位置参数的Python关键字。

命名参数示例

以下示例演示了如何在库关键字、用户关键字以及导入 Telnet 测试库时使用命名参数语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Settings ***
Library Telnet prompt=$ default_log_level=DEBUG

*** Test Cases ***
Example
Open connection 10.0.0.42 port=${PORT} alias=example
List files options=-lh
List files path=/tmp options=-l

*** Keywords ***
List files
[Arguments] ${path}=. ${options}=
Execute command ls ${options} ${path}
自由命名参数

Robot Framework支持自由命名参数,通常也被称为自由关键字参数或kwargs,类似于Python支持的**kwargs。这意味着一个关键字可以接收所有使用命名参数语法(name=value)的参数,并且不匹配关键字签名中指定的任何参数。

自由命名参数由与普通命名参数相同的关键字类型支持。关键字如何指定它们接受自由命名参数取决于关键字类型。例如,基于Python的关键字简单地使用**kwargs,用户关键字使用 &{kwargs}

自由命名参数支持变量,就像命名参数一样。在实践中,这意味着变量可以同时用在名称和值中,但是转义符必须始终直接可见。例如,只要使用的变量存在,foo=${bar}${foo}=${bar}都是有效的。额外的限制是自由参数名称必须始终是字符串。

示例

作为使用自由命名参数的第一个示例,看一下Process库中的Run Process关键字。它有一个签名 command*arguments**configuration,这意味着它接收要执行的命令(command),其参数作为可变数量的参数(*arguments)以及最后的可选配置参数作为自由命名参数(**configuration)。下面的示例还显示了变量与自由关键字参数一起工作的方式,就像使用命名参数语法时一样。

1
2
3
4
*** Test Cases ***
Free Named Arguments
Run Process program.py arg1 arg2 cwd=/home/user
Run Process program.py argument shell=True env=${ENVIRON}

请参阅创建测试库下的自由关键字参数(**kwargs)部分,以获取有关在自定义测试库中使用自由命名参数语法的更多信息。

作为第二个示例,创建一个包装用户关键字,用于运行上述示例中的program.py。包装关键字Run Program接受所有位置和命名参数,并将它们连同要执行的命令名称一起传递给Run Process。

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Free Named Arguments
Run Program arg1 arg2 cwd=/home/user
Run Program argument shell=True env=${ENVIRON}

*** Keywords ***
Run Program
[Arguments] @{args} &{config}
Run Process program.py @{args} &{config}
仅命名参数

从Robot Framework 3.1开始,关键字可以接受必须始终使用命名参数语法进行命名的参数。例如,如果一个关键字接受一个仅命名参数 example,那么它必须始终像 example=value 这样使用,而不能仅使用 value。这种语法受到了Python 3支持的仅关键字参数语法的启发。

在大多数情况下,仅命名参数的工作方式与命名参数相同。主要的区别是,使用静态库API实现的Python 2库不支持此语法。

作为使用用户关键字的仅命名参数的一个示例,下面是上述自由命名参数示例中的Run Program的一个变体,它只支持配置shell:

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Named-only Arguments
Run Program arg1 arg2 # 'shell' 是 False (默认)
Run Program argument shell=True # 'shell' 是 True

*** Keywords ***
Run Program
[Arguments] @{args} ${shell}=False
Run Process program.py @{args} shell=${shell}
嵌入到关键字名称中的参数

指定参数的一种完全不同的方法是将参数嵌入到关键字名称中。这种语法由测试库关键字和用户关键字都支持。

失败

测试用例失败时

如果测试用例使用的任何关键字失败,测试用例就会失败。通常,这意味着该测试用例的执行将停止,可能执行测试拆解,然后从下一个测试用例继续执行。如果不希望停止测试执行,也可以使用特殊的可继续失败。

错误消息

分配给失败的测试用例的错误消息直接来自失败的关键字。通常,错误消息是由关键字本身创建的,但是一些关键字允许配置它们。

在某些情况下,例如当使用可继续失败时,一个测试用例可能会失败多次。在这种情况下,最终的错误消息是通过组合各个错误得到的。非常长的错误消息会自动从中间切断,以使报告更易于阅读,但完整的错误消息始终作为失败关键字的消息在日志文件中可见。

默认情况下,错误消息是普通文本,但它们可以包含HTML格式。这通过在错误消息开始处使用标记字符串 HTML 来启用。这个标记将从报告和日志中显示的最终错误消息中删除。下面的第二个示例显示了在自定义消息中使用HTML。

1
2
3
4
5
6
7
*** Test Cases ***
Normal Error
Fail 这是一个相当无聊的例子...

HTML Error
${number} = Get Number
Should Be Equal ${number} 42 *HTML* 数字不是的<b>魔法</b>数字。

测试用例名称和文档

测试用例名称直接来自测试用例部分:它就是输入到测试用例列中的内容。一个测试套件中的测试用例应该有唯一的名称。关于这一点,也可以在测试本身中使用自动变量 ${TEST_NAME} 来引用测试名称。无论何时执行测试,包括所有用户关键字,以及测试设置和测试拆解,它都是可用的。

从Robot Framework 3.2开始,测试用例名称中可能的变量会被解析,以便最终名称将包含变量值。如果变量不存在,其名称将保持不变。

1
2
3
4
5
6
*** Variables ***
${MAX AMOUNT} ${5000000}

*** Test Cases ***
Amount cannot be larger than ${MAX AMOUNT}
# ...

[Documentation] 设置允许为测试用例设置自由形式的文档。该文本将显示在命令行输出和生成的日志和报告中。如果文档过长,可以将其分割成多行。可以使用简单的HTML格式,并且可以使用变量使文档动态化。可能不存在的变量将保持不变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
*** Test Cases ***
Simple
[Documentation] 简单且短小的文档。
No Operation

Multiple lines
[Documentation] 文档的第一行。
...
... 文档在这里继续。这些行在HTML输出中形成
... 一个段落。
No Operation

Formatting
[Documentation]
... 这个列表有:
... - *粗体*
... - _斜体_
... - 链接: http://robotframework.org
No Operation

Variables
[Documentation] 在 ${HOST} 上由 ${USER} 执行
No Operation

测试用例有清晰和描述性的名称是很重要的,而在这种情况下,它们通常不需要任何文档。如果测试用例的逻辑需要文档化,这通常是一个信号,表示测试用例中的关键字需要更好的名称,它们需要被增强,而不是添加额外的文档。最后,元数据,如上述最后一个示例中的环境和用户信息,通常最好使用标签来指定。

标记测试用例

在Robot Framework中使用标签是一种简单而强大的机制,用于分类测试用例和用户关键字。标签是自由文本,Robot Framework本身对它们没有特殊的含义,除了下面讨论的保留标签。标签至少可以用于以下目的:

  • 它们显示在测试报告、日志以及测试数据中,因此它们为测试用例提供元数据。
  • 基于它们自动收集关于测试用例(总数、通过、失败和跳过)的统计信息。
  • 它们可以用于包含和排除以及跳过测试用例。

有多种方式可以为测试用例指定标签,如下所述:

  • 在设置部分中的测试标签设置
    • 具有此设置的测试用例文件中的所有测试始终获得指定的标签。如果在套件初始化文件中使用此设置,所有子套件中的测试都会获得这些标签。
  • 每个测试用例的[Tags]设置
    • 测试将获得这些标签,除了使用测试标签设置指定的标签。[Tags]设置还允许使用-tag语法删除使用测试标签设置的标签。
  • –settag命令行选项
    • 所有测试都会获得此选项设置的标签,除了它们在其他地方获得的标签。
  • Set Tags,Remove Tags,Fail和Pass Execution关键字
    • 这些BuiltIn关键字可以用于在测试执行期间动态操作标签。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
*** Settings ***git fetch origin
Test Tags requirement: 42 smoke

*** Variables ***
${HOST} 10.0.1.42

*** Test Cases ***
No own tags
[Documentation] 测试有标签 'requirement: 42' 和 'smoke'。
No Operation

Own tags
[Documentation] 测试有标签 'requirement: 42','smoke' 和 'not ready'。
[Tags] not ready
No Operation

Own tags with variable
[Documentation] 测试有标签 'requirement: 42','smoke' 和 'host: 10.0.1.42'。
[Tags] host: ${HOST}
No Operation

Remove common tag
[Documentation] 测试只有标签 'requirement: 42'。
[Tags] -smoke
No Operation

Remove common tag using a pattern
[Documentation] 测试只有标签 'smoke'。
[Tags] -requirement: *
No Operation

Set Tags and Remove Tags keywords
[Documentation] 这个测试有标签 'smoke','example' 和 'another'。
Set Tags example another
Remove Tags requirement: *

如示例所示,可以使用变量创建标签,但否则它们保留数据中使用的确切名称。当比较标签时,例如,收集统计信息,选择要执行的测试,或者删除重复项,比较是大小写、空格和下划线不敏感的。

如上述示例所示,使用-tag语法删除标签支持简单的模式,如-requirement: *。以连字符开头的标签除了在[Tags]设置中没有其他特殊含义。如果需要使用[Tags]设置一个以连字符开头的标签,可以使用转义格式,如-tag。

注意

测试标签设置是在Robot Framework 6.0中新增的。早期版本支持Force Tags和Default Tags设置,这将在下一节中讨论。

注意

删除常见标签的-tag语法是在Robot Framework 7.0中新增的。

弃用 Force Tags 和 Default Tags

在Robot Framework 6.0之前,可以使用两种不同的设置在设置部分为测试指定标签:

Force Tags 所有测试无条件地获得这些标签。这与现在的Test Tags完全相同。 Default Tags 所有测试默认获得这些标签。如果测试有[Tags],它将不会获得这些标签。 这两种设置仍然可以工作,但它们被认为是已弃用的。将来会添加一个可见的弃用警告,最可能在Robot Framework 8.0中,最终这些设置将被删除。可以使用像Tidy这样的工具来简化过渡。

更新Force Tags只需要将其重命名为Test Tags。Default Tags设置将被完全删除,但在Robot Framework 7.0中引入的-tag功能提供了相同的基础功能。下面的示例演示了所需的更改。

旧语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
*** Settings ***
Force Tags all
Default Tags default

*** Test Cases ***
Common only
[Documentation] 测试有标签 'all' 和 'default'。
No Operation

No default
[Documentation] 测试只有标签 'all'。
[Tags]
No Operation

Own and no default
[Documentation] 测试有标签 'all' 和 'own'。
[Tags] own
No Operation

新语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** Settings ***
Test Tags all default

*** Test Cases ***
Common only
[Documentation] 测试有标签 'all' 和 'default'。
No Operation

No default
[Documentation] 测试只有标签 'all'。
[Tags] -default
No Operation

Own and no default
[Documentation] 测试有标签 'all' 和 'own'。
[Tags] own -default
No Operation
保留标签

用户通常可以自由地使用在他们的上下文中工作的任何标签。然而,有一些标签对于Robot Framework本身有预定义的含义,用它们来做其他用途可能会有意想不到的结果。Robot Framework现在和将来所有的特殊标签都有 robot: 前缀。为了避免问题,用户因此不应该使用任何带有这个前缀的标签,除非实际激活特殊功能。当前的保留标签列在下面,但是未来可能会添加更多这样的标签。

  • robot:continue-on-failure
    
    1
    2
    3



    robot:recursive-continue-on-failure
    1
    2
    3
    4

    - 用于启用继续失败模式。
    - ```
    robot:stop-on-failure
    1
    robot:recursive-stop-on-failure
    - 用于禁用继续失败模式。
  • robot:skip-on-failure
    
    1
    2
    3
    4

    - 如果测试失败,则标记为跳过。
    - ```
    robot:skip
    - 无条件跳过测试。
  • robot:exclude
    
    1
    2
    3
    4

    - 无条件排除测试。
    - ```
    robot:private
    - 标记关键字为私有。
  • robot:no-dry-run
    
    1
    2
    3
    4

    - 标记关键字在干运行模式下不执行。
    - ```
    robot:exit
    - 当执行停止时,自动添加到测试中。
  • robot:flatten
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45

    - 在执行时间启用平铺关键字。

    从RobotFramework 4.1开始,默认情况下在标签统计中抑制保留标签。当它们通过 `--tagstatinclude robot:*` 命令行选项明确包含时,它们将被显示。

    #### 测试设置和拆解

    Robot Framework具有与许多其他测试自动化框架类似的测试设置和拆解功能。简单来说,测试设置是在测试用例之前执行的操作,而测试拆解是在测试用例之后执行的操作。在Robot Framework中,设置和拆解只是具有可能参数的普通关键字。

    设置和拆解总是单个关键字。如果它们需要处理多个独立的任务,可以创建高级用户关键字来实现。另一种解决方案是使用BuiltIn关键字Run Keywords执行多个关键字。

    测试拆解在两个方面特殊。首先,即使测试用例失败,它也会被执行,所以它可以用于必须完成的清理活动,无论测试用例的状态如何。此外,即使其中一个关键字失败,拆解中的所有关键字也会被执行。这种继续失败的功能也可以用于普通关键字,但在拆解中它默认是开启的。

    在测试用例文件中为测试用例指定设置或拆解的最简单方法是使用设置部分中的Test Setup和Test Teardown设置。单个测试用例也可以有自己的设置或拆解。它们是在测试用例部分的[Setup]或[Teardown]设置中定义的,并且它们会覆盖可能的Test Setup和Test Teardown设置。在[Setup]或[Teardown]设置后没有关键字意味着没有设置或拆解。也可以使用值NONE来表示测试没有设置/拆解。

    ```robotframework
    *** Settings ***
    Test Setup Open Application App A
    Test Teardown Close Application

    *** Test Cases ***
    Default values
    [Documentation] 设置和拆解来自设置部分
    Do Something

    Overridden setup
    [Documentation] 自己的设置,拆解来自设置部分
    [Setup] Open Application App B
    Do Something

    No teardown
    [Documentation] 默认设置,完全没有拆解
    Do Something
    [Teardown]

    No teardown 2
    [Documentation] 设置和拆解也可以通过特殊值NONE禁用
    Do Something
    [Teardown] NONE

    Using variables
    [Documentation] 使用变量指定设置和拆解
    [Setup] ${SETUP}
    Do Something
    [Teardown] ${TEARDOWN}

作为设置或拆解执行的关键字的名称可以是一个变量。这通过从命令行给出关键字名称作为变量,便于在不同环境中有不同的设置或拆解。

注意

测试套件可以有自己的设置和拆解。套件设置在该测试套件中的任何测试用例或子测试套件之前执行,同样,套件拆解在它们之后执行。

测试模板

测试模板将普通的关键字驱动测试用例转换为数据驱动的测试。关键字驱动的测试用例的主体是由关键字和它们可能的参数构成的,而带有模板的测试用例只包含模板关键字的参数。与其在每个测试和/或文件中的所有测试中多次重复相同的关键字,不如只在每个测试或文件中使用一次。

模板关键字可以接受正常的位置参数和命名参数,以及嵌入到关键字名称中的参数。与其他设置不同,不能使用变量定义模板。

基本用法

以下示例测试用例说明了如何将接受正常位置参数的关键字用作模板。这两个测试在功能上完全相同。

1
2
3
4
5
6
7
*** Test Cases ***
Normal test case
Example keyword first argument second argument

Templated test case
[Template] Example keyword
first argument second argument

如示例所示,可以使用 [Template] 设置为单个测试用例指定模板。另一种方法是在设置部分使用 Test Template 设置,在这种情况下,模板将应用于该测试用例文件中的所有测试用例。[Template] 设置会覆盖设置部分设置的可能的模板,而 [Template] 的空值意味着测试没有模板,即使使用了 Test Template。也可以使用值 NONE 来表示测试没有模板。

如果一个带模板的测试用例在其主体中有多个数据行,那么模板将逐行应用于所有行。这意味着相同的关键字会被执行多次,每行数据执行一次。模板化的测试也是特殊的,因为即使其中一个或多个失败,所有的轮次也会被执行。这种继续失败模式也可以用于普通测试,但在模板化的测试中,该模式是自动开启的。

1
2
3
4
5
6
7
8
*** Settings ***
Test Template Example keyword

*** Test Cases ***
Templated test case
first round 1 first round 2
second round 1 second round 2
third round 1 third round 2

使用具有默认值或接受可变数量参数的关键字,以及使用命名参数和自由命名参数,与模板的工作方式完全相同。在参数中使用变量也是正常支持的。

嵌入参数的模板

模板支持嵌入参数语法的变体。使用模板时,此语法的工作方式是,如果模板关键字的名称中有变量,它们被视为参数的占位符,并用模板使用的实际参数替换。然后使用结果关键字,而不使用位置参数。这最好通过一个例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
带嵌入参数的普通测试用例
1 + 1 的结果应该是 2
1 + 2 的结果应该是 3

带嵌入参数的模板
[Template] ${calculation} 的结果应该是 ${expected}
1 + 1 2
1 + 2 3

*** Keywords ***
${calculation} 的结果应该是 ${expected}
${result} = 计算 ${calculation}
应该相等 ${result} ${expected}

当使用模板的嵌入参数时,模板关键字名称中的参数数量必须与其使用的参数数量匹配。尽管参数名称不需要匹配原始关键字的参数,也可以完全使用不同的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
不同的参数名称
[Template] ${foo} 的结果应该是 ${bar}
1 + 1 2
1 + 2 3

只有一些参数
[Template] ${calculation} 的结果应该是 3
1 + 2
4 - 1

新的参数
[Template] ${life} 的 ${meaning} 应该是 42
result 21 * 2

使用模板的嵌入参数的主要好处是参数名称被明确指定。当使用普通参数时,可以通过命名包含参数的列来达到相同的效果。这在下一节的数据驱动样式示例中有所说明。

使用FOR循环的模板

如果模板与FOR循环一起使用,模板将应用于循环内的所有步骤。在这种情况下,也会使用继续失败模式,这意味着即使有失败,所有的步骤也会与所有的循环元素一起执行。

1
2
3
4
5
6
7
8
9
*** Test Cases ***
带FOR循环的模板
[Template] Example keyword
FOR ${item} IN @{ITEMS}
${item} 2nd arg
END
FOR ${index} IN RANGE 42
1st arg ${index}
END
使用IF/ELSE结构的模板

IF/ELSE结构也可以与模板一起使用。例如,当与FOR循环一起使用时,这可以用于过滤执行的参数,这可能很有用。

1
2
3
4
5
6
7
8
*** Test Cases ***
带FOR和IF的模板
[Template] Example keyword
FOR ${item} IN @{ITEMS}
IF ${item} < 5
${item} 2nd arg
END
END

不同的测试用例风格

测试用例可以以多种不同的方式编写。描述某种工作流的测试用例可以以关键字驱动或行为驱动的风格编写。数据驱动的风格可以用于测试具有不同输入数据的相同工作流。

关键字驱动风格

工作流测试,如前面描述的有效登录测试,是由几个关键字及其可能的参数构建的。它们的正常结构是首先将系统置于初始状态(在有效登录示例中打开登录页面),然后对系统进行一些操作(输入名称,输入密码,提交凭据),最后验证系统的行为是否符合预期(应打开欢迎页面)。

数据驱动风格

编写测试用例的另一种风格是数据驱动的方法,其中测试用例只使用一个高级关键字(通常作为用户关键字创建),该关键字隐藏了实际的测试工作流。当需要使用不同的输入和/或输出数据测试相同的场景时,这些测试非常有用。可以在每个测试中重复相同的关键字,但是测试模板功能允许只指定一次要使用的关键字。

1
2
3
4
5
6
7
8
9
10
*** Settings ***
Test Template Login with invalid credentials should fail

*** Test Cases *** USERNAME PASSWORD
Invalid User Name invalid ${VALID PASSWORD}
Invalid Password ${VALID USER} invalid
Invalid User Name and Password invalid invalid
Empty User Name ${EMPTY} ${VALID PASSWORD}
Empty Password ${VALID USER} ${EMPTY}
Empty User Name and Password ${EMPTY} ${EMPTY}

信息

像上面的例子那样命名列可以使测试更易于理解。这是可能的,因为在标题行中,除第一个单元格外的其他单元格都被忽略。

上面的示例有六个独立的测试,每个无效的用户/密码组合一个,下面的示例说明了如何只有一个测试包含所有的组合。使用测试模板时,即使有失败,测试中的所有轮次也会被执行,所以这两种风格之间没有真正的功能差异。在上面的示例中,单独的组合被命名,所以更容易看出它们测试了什么,但是可能有大量的这些测试可能会混乱统计数据。使用哪种风格取决于上下文和个人偏好。

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Invalid Password
[Template] Login with invalid credentials should fail
invalid ${VALID PASSWORD}
${VALID USER} invalid
invalid whatever
${EMPTY} ${VALID PASSWORD}
${VALID USER} ${EMPTY}
${EMPTY} ${EMPTY}
行为驱动风格

也可以将测试用例编写为非技术项目利益相关者也必须理解的需求。这些可执行的需求是通常被称为接受测试驱动开发(ATDD)或示例规范化的过程的基石。

编写这些需求/测试的一种方式是由行为驱动开发(BDD)普及的Given-When-Then风格。在这种风格的测试用例中,初始状态通常用以Given开头的关键字表示,动作用以When开头的关键字描述,预期用以Then开头的关键字表示。如果一个步骤有多个动作,可以使用以And或But开头的关键字。

1
2
3
4
5
6
*** Test Cases ***
Valid Login
Given 登录页面已打开
When 插入有效的用户名和密码
and 提交凭据
Then 应该打开欢迎页面
忽略Given/When/Then/And/But前缀

当搜索匹配的关键字时,如果没有找到全名匹配,将丢弃前缀Given、When、Then、And和But。这对于用户关键字和库关键字都有效。例如,在上面的示例中,Given登录页面已打开可以实现为用户关键字,无论是否带有单词Given。忽略前缀还允许使用不同的前缀使用相同的关键字。例如,欢迎页面应该打开也可以用作And欢迎页面应该打开。

注意

这些前缀可以本地化。请参阅翻译附录以获取支持的翻译。

嵌入数据到关键字

在编写具体示例时,能够将实际数据传递给关键字实现是很有用的。用户关键字通过允许将参数嵌入到关键字名称中来支持这一点。

创建任务

除了测试自动化,Robot Framework还可以用于其他自动化目的,包括机器人流程自动化(RPA)。这一直是可能的,但Robot Framework 3.1添加了对自动化任务的官方支持,而不仅仅是测试。在大多数情况下,创建任务的工作方式与创建测试相同,唯一的实质区别在于术语。任务也可以像测试用例一样组织成套件。

任务语法

任务是基于可用关键字创建的,就像测试用例一样,任务语法通常与测试用例语法相同。主要区别在于,任务是在任务部分创建的,而不是在测试用例部分:

1
2
3
4
5
6
*** Tasks ***
Process invoice
Read information from PDF
Validate information
Submit information to backend system
Validate information is visible in web UI

在同一文件中同时有测试和任务是错误的。

与任务相关的设置

可以在任务部分使用的设置与测试用例部分中可以使用的设置完全相同。在设置部分,可以使用任务设置、任务拆解、任务模板和任务超时,而不是它们的测试变体。

创建测试套件

Robot Framework的测试用例是在测试用例文件中创建的,这些文件可以组织成目录。这些文件和目录创建了一个分层的测试套件结构。创建任务时也适用相同的概念,但术语有所不同。

套件文件

Robot Framework的测试用例是在套件文件(也称为测试用例文件)中的测试用例部分创建的。这样的文件会自动从它包含的所有测试用例创建一个测试套件。测试用例的数量没有上限,但建议少于十个,除非使用数据驱动的方法,其中一个测试用例只包含一个高级关键字。

在设置部分,可以使用以下设置来自定义套件:

  • Name
    • 用于设置自定义套件名称。默认名称是根据文件或目录名称创建的。
  • Documentation
    • 用于指定套件文档。
  • Metadata
    • 用于设置自由套件元数据作为名称-值对。
  • Suite Setup, Suite Teardown
    • 指定套件设置和拆解。

注意

设置名称是不区分大小写的,但推荐使用上述格式。

套件目录

测试用例文件可以组织成目录,这些目录创建了更高级别的测试套件。从目录创建的测试套件不能直接包含任何测试用例,而是包含其他包含测试用例的测试套件。然后,这些目录可以放置到其他目录中,创建更高级别的套件。结构没有限制,所以可以根据需要组织测试用例。

当执行一个测试目录时,它包含的文件和目录会被递归处理,如下所示:

  • 以点(.)或下划线(_)开头的文件和目录名会被忽略。
  • 名称为CVS的目录会被忽略(区分大小写)。
  • 支持的文件格式的文件会被处理。
  • 其他文件会被忽略。
  • 如果处理的文件或目录不包含任何测试用例,它会被静默忽略(向系统日志写入一条消息),并继续处理。

套件初始化文件

从目录创建的测试套件可以有与从测试用例文件创建的套件相似的设置。因为目录本身不能包含这种信息,所以必须将其放入一个特殊的测试套件初始化文件中。初始化文件的名称必须始终采用__init__.ext的格式,其中扩展名必须是支持的文件格式之一(通常是__init__.robot)。这种命名格式借鉴自Python,其中以这种方式命名的文件表示目录是一个模块。

从Robot Framework 6.1开始,也可以为通过给出多个路径启动测试执行时自动创建的套件定义套件初始化文件。

初始化文件的结构和语法与测试用例文件相同,只是它们不能有测试用例部分,而且不是所有的设置都被支持。在初始化文件中创建或导入的变量和关键字在较低级别的测试套件中不可用。如果需要共享变量或关键字,可以将它们放入资源文件中,这些文件可以由初始化和测试用例文件导入。

初始化文件的主要用途是指定与套件文件类似的测试套件相关设置,但也可以设置一些与测试用例相关的设置。如何在初始化文件中使用不同的设置在下面解释。

  • Name, Documentation, Metadata, Suite Setup, Suite Teardown
    • 这些套件特定的设置在套件初始化文件中的工作方式与套件文件中的工作方式相同。
  • Test Tags
    • 指定的标签无条件地设置为此目录包含的所有套件文件中的所有测试,递归地。新的在Robot Framework 6.1。需要使用旧版本的Force Tags。
  • Test Setup, Test Teardown, Test Timeout
    • 将测试设置/拆解或测试超时的默认值设置为此目录包含的所有测试用例。可以在较低级别覆盖。注意,用作设置和拆解的关键字必须在使用它们的测试的测试用例文件中可用。在初始化文件本身中定义关键字是不够的。
  • Task Setup, Task Teardown, Task Tags, Task Timeout
    • 分别是Test Setup, Test Teardown, Test Tags和Test Timeout的别名,可以在创建任务,而不是测试时使用。
  • Default Tags, Test Template
    • 在初始化文件中不支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Settings ***
Documentation 示例套件
Suite Setup Do Something ${MESSAGE}
Test Tags example
Library SomeLibrary

*** Variables ***
${MESSAGE} Hello, world!

*** Keywords ***
Do Something
[Arguments] ${args}
Some Keyword ${arg}
Another Keyword

套件名称

测试套件的名称默认由文件或目录名称构造。创建名称时,将忽略扩展名,可能的下划线被替换为空格,全小写的名称被转换为标题大小写。例如,some_tests.robot变成Some TestsMy_test_directory变成My test directory

文件或目录名称可以包含一个前缀来控制套件的执行顺序。前缀与基本名称之间用两个下划线分隔,当构造实际的测试套件名称时,前缀和下划线都被删除。例如,文件01__some_tests.robot02__more_tests.robot分别创建测试套件Some TestsMore Tests,前者在后者之前执行。

从Robot Framework 6.1开始,也可以通过在设置部分使用Name设置为套件指定一个自定义名称:

1
2
*** Settings ***
Name Custom suite name

可以使用--name选项从命令行覆盖顶级套件的名称。

套件文档

使用设置部分中的Documentation设置设置测试套件的文档。它可以在套件文件和套件初始化文件中使用。套件文档与测试用例文档在显示位置和创建方式上具有完全相同的特性。有关语法的详细信息,请参阅文档格式化附录。

1
2
3
*** Settings ***
Documentation An example suite documentation with *some* _formatting_.
... Long documentation can be split into multiple lines.

可以使用--doc选项从命令行覆盖顶级套件的文档。

自由套件元数据

除了文档外,套件还可以有自由元数据。这些元数据以名称-值对的形式在设置部分使用Metadata设置定义。它在报告和日志中的显示方式与文档类似。

元数据的名称是给Metadata设置的第一个参数,剩余的参数指定其值。值的处理方式与文档相同,这意味着它支持HTML格式和变量,而且较长的值可以分成多行。

1
2
3
4
5
6
7
*** Settings ***
Metadata Version 2.0
Metadata Robot Framework http://robotframework.org
Metadata Platform ${PLATFORM}
Metadata Longer Value
... Longer metadata values can be split into multiple
... rows. Also *simple* _formatting_ is supported.

可以使用--metadata选项从命令行设置顶级套件的自由元数据。

套件设置和拆解

不仅测试用例,而且测试套件也可以有一个设置和一个拆解。套件设置在运行任何套件的测试用例或子测试套件之前执行,套件拆解在它们之后执行。所有的测试套件都可以有一个设置和一个拆解;对于从目录创建的套件,它们必须在套件初始化文件中指定。

与测试用例类似,套件设置和拆解是可能带有参数的关键字。它们在设置部分中使用Suite SetupSuite Teardown设置定义,分别。关键字名称和可能的参数位于设置名称后的列中。

如果套件设置失败,那么它和其子测试套件中的所有测试用例都会立即被分配一个失败状态,并且它们实际上不会被执行。这使得套件设置成为在可能运行测试用例之前必须满足的预条件的理想选择。

套件拆解通常用于在所有测试用例执行完毕后进行清理。即使同一套件的设置失败,它也会被执行。如果套件拆解失败,那么套件中的所有测试用例都会被标记为失败,无论它们的原始执行状态如何。注意,即使其中一个失败,套件拆解中的所有关键字也会被执行。

作为设置或拆解执行的关键字的名称可以是一个变量。这通过从命令行给出关键字名称作为变量,便于在不同环境中有不同的设置或拆解。

使用测试库

测试库包含那些最低级别的关键字,通常被称为库关键字,它们实际上与被测试的系统进行交互。所有的测试用例总是使用来自某个库的关键字,通常通过高级用户关键字。本节解释如何使用测试库以及如何使用它们提供的关键字。创建测试库在另一节中描述。

导入库

测试库通常使用Library设置导入,但也可以使用Import Library关键字。

使用Library设置

测试库通常使用设置部分中的Library设置导入,并在后续列中有库名称。与大多数其他数据不同,库名称既区分大小写又区分空格。如果库在一个包中,必须使用包括包名在内的全名。

在那些库需要参数的情况下,它们列在库名称后的列中。在测试库导入中可以像关键字的参数一样使用默认值、可变数量的参数和命名参数。库名称和参数都可以使用变量设置。

1
2
3
4
5
*** Settings ***
Library OperatingSystem
Library my.package.TestLibrary
Library MyLibrary arg1 arg2
Library ${LIBRARY}

可以在套件文件、资源文件和套件初始化文件中导入测试库。在所有这些情况下,导入的库中的所有关键字都在该文件中可用。对于资源文件,这些关键字也在使用它们的其他文件中可用。

使用Import Library关键字

另一种使用测试库的可能性是使用BuiltIn库中的关键字Import Library。这个关键字接受库名称和可能的参数,就像Library设置一样。导入的库中的关键字在使用Import Library关键字的测试套件中可用。这种方法在库在测试执行开始时不可用,只有其他一些关键字使其可用的情况下很有用。

1
2
3
4
5
*** Test Cases ***
Example
Do Something
Import Library MyLibrary arg1 arg2
KW From MyLibrary

指定要导入的库

要导入的库可以通过使用库名称或库路径来指定。无论是使用Library设置还是Import Library关键字导入库,这些方法的工作方式都是相同的。

使用库名称

指定要导入的测试库的最常见方法是使用其名称,就像本节中的所有示例所做的那样。在这些情况下,Robot Framework试图从模块搜索路径中找到实现库的类或模块。以某种方式安装的库应该自动在模块搜索路径中,但对于其他库,可能需要单独配置搜索路径。

这种方法的最大好处是,当配置了模块搜索路径(通常使用自定义的启动脚本)后,普通用户不需要考虑库实际上安装在哪里。缺点是,将自己的(可能非常简单的)库放入搜索路径可能需要一些额外的配置。

使用库的物理路径

指定要导入的库的另一种机制是使用文件系统中的路径。这个路径被认为是相对于当前测试数据文件所在的目录的,就像资源和变量文件的路径一样。这种方法的主要好处是不需要配置模块搜索路径。

如果库是一个文件,那么它的路径必须包含扩展名,即.py。如果库是作为目录实现的,那么如果路径是相对的,它的路径必须有一个尾随的正斜杠(/)。对于绝对路径,尾随的斜杠是可选的。以下示例演示了这些不同的用法。

1
2
3
4
*** Settings ***
Library PythonLibrary.py
Library relative/path/PythonDirLib/ possible arguments
Library ${RESOURCES}/Example.class

这种方法的一个限制是,作为Python类实现的库必须在与类同名的模块中。

设置库的自定义名称

库名称在关键字名称之前显示在测试日志中,如果多个关键字有相同的名称,它们必须使用关键字名称前缀为库名称。库名称通常从实现它的模块或类名称中获取,但在某些情况下,更改它是可取的:

  • 需要多次导入相同的库,但参数不同。否则这是不可能的。
  • 库名称过长不便。
  • 希望使用变量在不同的环境中导入不同的库,但用相同的名称引用它们。
  • 库名称误导或其他贫穷。在这种情况下,更改实际名称当然是更好的解决方案。

指定新名称的基本语法是在库名称后面有文本AS(区分大小写),然后在其后面有新名称。指定的名称显示在日志中,并且在使用关键字的全名(LibraryName.Keyword Name)时必须在测试数据中使用。

1
2
3
*** Settings ***
Library packagename.TestLib AS TestLib
Library ${LIBRARY} AS MyName

库的可能参数放在原始库名称和AS标记之间。以下示例说明了如何使用不同的参数多次导入相同的库:

1
2
3
4
5
6
7
8
9
*** Settings ***
Library SomeLibrary localhost 1234 AS LocalLib
Library SomeLibrary server.domain 8080 AS RemoteLib

*** Test Cases ***
Example
LocalLib.Some Keyword some arg second arg
RemoteLib.Some Keyword another arg whatever
LocalLib.Another Keyword

在设置部分导入库和使用Import Library关键字时,都可以为测试库设置自定义名称。

注意

在Robot Framework 6.0之前,给库指定自定义名称时使用的标记是WITH NAME,而不是AS。旧的语法继续工作,但它被认为是已弃用的,最终将被删除。

标准库

有些测试库是与Robot Framework一起分发的,这些库被称为标准库。BuiltIn库是特殊的,因为它是自动使用的,因此它的关键字总是可用的。其他标准库需要像任何其他库一样导入,但是不需要安装它们。

普通标准库

下面列出了可用的普通标准库,并附有链接到它们的文档:

  • BuiltIn
  • Collections
  • DateTime
  • Dialogs
  • OperatingSystem
  • Process
  • Screenshot
  • String
  • Telnet
  • XML
远程库

除了上面列出的普通标准库,还有一个远程库,它与其他标准库完全不同。它本身没有任何关键字,但它作为Robot Framework和实际测试库实现之间的代理。这些库可以在核心框架之外的其他机器上运行,甚至可以使用Robot Framework原生不支持的语言实现。

有关此概念的更多信息,请参阅单独的远程库接口部分。

外部库

任何不是标准库的测试库,按定义,都是外部库。Robot Framework的开源社区已经实现了几个通用库,如SeleniumLibrarySwingLibrary,这些库没有与核心框架一起打包。可以从 http://robotframework.org 找到公开可用的库列表。

显然,使用Robot Framework的团队也可以实现通用和自定义库。有关该主题的更多信息,请参阅创建测试库部分。

不同的外部库可能有完全不同的安装和使用机制。有时,它们可能还需要单独安装一些其他依赖项。所有的库都应该有清晰的安装和使用文档,最好能自动化安装过程。

变量

简介

变量是Robot Framework的一个重要特性,它们可以在测试数据的大多数地方使用。最常见的是在测试用例和关键字部分的关键字参数中使用,但所有设置也允许在其值中使用变量。普通的关键字名称不能用变量指定,但可以使用BuiltIn关键字Run Keyword来达到同样的效果。

Robot Framework有自己的变量,可以分别使用语法${SCALAR}@{LIST}&{DICT}作为标量、列表或字典使用。除此之外,还可以直接使用语法%{ENV_VAR}使用环境变量。

变量在以下情况下很有用:

  • 当测试数据中的字符串经常变化时。使用变量,只需要在一个地方做这些更改。
  • 当创建系统独立和操作系统独立的测试数据时。使用变量而不是硬编码的字符串可以大大简化这个过程(例如,使用${RESOURCES}代替c:\resources,或者使用${HOST}代替10.0.0.1:8080)。因为变量可以在启动测试时从命令行设置,所以更改系统特定的变量很容易(例如,--variable HOST:10.0.0.2:1234 --variable RESOURCES:/opt/resources)。这也有助于本地化测试,这通常涉及使用不同的字符串运行相同的测试。
  • 当需要将除字符串以外的对象作为关键字的参数时。没有变量,这是不可能的。
  • 当不同的关键字,甚至在不同的测试库中,需要通信时。可以将一个关键字的返回值分配给一个变量,并将它作为另一个的参数传递。
  • 当测试数据中的值很长或者复杂时。例如,${URL}http://long.domain.name:8080/path/to/service?foo=1&bar=2&zap=42短。
  • 如果在测试数据中使用了不存在的变量,使用它的关键字会失败。如果需要将用于变量的相同语法作为字面字符串,必须使用反斜杠转义,如\${NAME}

使用变量

本节解释如何使用变量,包括常规标量变量语法${var},如何在列表和字典上下文中使用变量,如@{var}&{var},以及如何使用环境变量,如%{var}。后续部分将讨论创建变量的不同方式。

Robot Framework的变量,类似于关键字,是不区分大小写的,而且也忽略空格和下划线。然而,建议使用大写字母表示全局变量(例如,${PATH}${TWO WORDS}),使用小写字母表示只在某些测试用例或用户关键字中可用的局部变量(例如,${my var})。更重要的是,应该一致地使用大小写。

变量名由变量类型标识符($,@,&,%),花括号({,})和花括号之间的实际变量名组成。与使用类似变量语法的某些编程语言不同,花括号总是必需的。变量名基本上可以在花括号之间有任何字符。然而,建议只使用从a到z的字母、数字、下划线和空格,这甚至是使用扩展变量语法的要求。

标量变量语法

在Robot Framework测试数据中使用变量的最常见方式是使用标量变量语法,如${var}。当使用这种语法时,变量名被其值替换。大多数时候,变量值是字符串,但变量可以包含任何对象,包括数字、列表、字典,甚至自定义对象。

下面的例子说明了标量变量的使用。假设变量${GREET}${NAME}可用,并分别赋值为字符串Hello和world,那么下面的示例测试用例是等价的。

1
2
3
4
5
6
7
8
*** Test Cases ***
Constants
Log Hello
Log Hello, world!!

Variables
Log ${GREET}
Log ${GREET}, ${NAME}!!

当一个标量变量单独使用,没有其他文本或变量围绕它,就像上面的${GREET},变量被其值替换,值可以是任何对象。如果变量不是单独使用,就像上面的${GREER}, ${NAME}!!,它的值首先被转换为字符串,然后与其他数据连接。

注意

当使用命名参数语法向关键字传递参数时,也会按原样使用变量值,如argname=${var}

下面的例子演示了变量单独使用和与其他内容一起使用的区别。首先,假设有一个变量${STR}设置为字符串Hello, world!和${OBJ}设置为以下Python对象的实例:

1
2
3
4
class MyObj:

def __str__():
return "Hi, terra!"

设置了这两个变量后,有以下测试数据:

1
2
3
4
5
6
*** Test Cases ***
Objects
KW 1 ${STR}
KW 2 ${OBJ}
KW 3 I said "${STR}"
KW 4 You said "${OBJ}"

最后,当执行这个测试数据时,不同的关键字接收到的参数如下所示:

  • KW 1得到一个字符串Hello, world!
  • KW 2得到存储在变量${OBJ}中的对象
  • KW 3得到一个字符串I said “Hello, world!”
  • KW 4得到一个字符串You said “Hi, terra!”

注意

如果变量不能表示为Unicode,那么将变量转换为Unicode显然会失败。例如,如果试图使用字节序列作为关键字的参数,那么将值连接在一起,如${byte1}${byte2}。一个解决方法是创建一个包含整个值的变量,并在单元格中单独使用它(例如,${bytes}),因为这样值就会按原样使用。

列表变量语法

当一个变量像${EXAMPLE}那样作为标量使用时,它的值会被原样使用。如果一个变量的值是一个列表或类似列表,那么也可以像@{EXAMPLE}那样将它作为一个列表变量使用。在这种情况下,列表会被展开,单个项会作为单独的参数传入。这最容易通过一个例子来解释。假设一个变量@{USER}的值是['robot', 'secret'],那么下面的两个测试用例是等价的:

1
2
3
4
5
6
*** Test Cases ***
Constants
Login robot secret

List Variable
Login @{USER}

Robot Framework将其自己的变量存储在一个内部存储中,并允许将它们作为标量、列表或字典使用。将一个变量作为列表使用需要它的值是一个Python列表或类似列表的对象。Robot Framework不允许将字符串用作列表,但接受其他可迭代的对象,如元组或字典。

从Robot Framework 4.0开始,可以将列表扩展与列表项访问结合使用,使得以下用法成为可能:

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Nested container
${nested} = Evaluate [['a', 'b', 'c'], {'key': ['x', 'y']}]
Log Many @{nested}[0] # Logs 'a', 'b' and 'c'.
Log Many @{nested}[1][key] # Logs 'x' and 'y'.

Slice
${items} = Create List first second third
Log Many @{items}[1:] # Logs 'second' and 'third'.
使用列表变量与其他数据

可以将列表变量与其他参数一起使用,包括其他列表变量。

1
2
3
4
5
*** Test Cases ***
Example
Keyword @{LIST} more args
Keyword ${SCALAR} @{LIST} constant
Keyword @{LIST} @{ANOTHER} @{ONE MORE}
使用列表变量与设置

列表变量只能与一些设置一起使用。它们可以用作导入的库和变量文件的参数,但库和变量文件的名称本身不能是列表变量。也可以在设置和拆解中使用列表变量作为关键字的参数,但不能用作关键字的名称。对于与标签相关的设置,它们可以自由地使用。在不支持列表变量的地方,可以使用标量变量。

1
2
3
4
5
6
7
8
*** Settings ***
Library ExampleLibrary @{LIB ARGS} # This works
Library ${LIBRARY} @{LIB ARGS} # This works
Library @{LIBRARY AND ARGS} # This does not work
Suite Setup Some Keyword @{KW ARGS} # This works
Suite Setup ${KEYWORD} @{KW ARGS} # This works
Suite Setup @{KEYWORD AND ARGS} # This does not work
Default Tags @{TAGS} # This works
字典变量语法

如上所述,包含列表的变量可以作为列表变量使用,将列表项作为单独的参数传递给关键字。类似地,包含Python字典或类似字典的对象的变量可以像&{EXAMPLE}那样作为字典变量使用。实际上,这意味着字典被展开,单个项作为命名参数传递给关键字。假设一个变量&{USER}的值是{'name': 'robot', 'password': 'secret'},那么下面的两个测试用例是等价的。

1
2
3
4
5
6
*** Test Cases ***
Constants
Login name=robot password=secret

Dict Variable
Login &{USER}

注意

从Robot Framework 4.0开始,可以将字典扩展与字典项访问结合使用,使得像&{nested}[key]这样的用法成为可能。

使用字典变量与其他数据

可以将字典变量与其他参数一起使用,包括其他字典变量。因为命名参数语法要求位置参数在命名参数之前,所以字典只能跟在命名参数或其他字典后面。

1
2
3
4
5
*** Test Cases ***
Example
Keyword &{DICT} named=arg
Keyword positional @{LIST} &{DICT}
Keyword &{DICT} &{ANOTHER} &{ONE MORE}
使用字典变量与设置

字典变量通常不能与设置一起使用。唯一的例外是导入、设置和拆解,其中字典可以作为参数使用。

1
2
3
*** Settings ***
Library ExampleLibrary &{LIB ARGS}
Suite Setup Some Keyword &{KW ARGS} named=arg
访问列表和字典项

可以使用特殊语法${var}[item]${var}[nested][item]访问可下标的变量(例如,列表和字典)的项。从Robot Framework 4.0开始,也可以通过使用语法@{var}[item]&{var}[item]将项访问与列表扩展和字典扩展一起使用。

注意

在Robot Framework 3.1之前,正常的项访问语法是列表的@{var}[item]和字典的&{var}[item]。Robot Framework 3.1引入了通用的${var}[item]语法以及一些其他的好的增强,旧的项访问语法在Robot Framework 3.2中被弃用。

访问序列项

可以使用语法${var}[index]访问包含序列(例如,列表、字符串或字节)的变量的某个项,其中index是所选值的索引。索引从零开始,可以使用负索引从末尾访问项,尝试访问索引过大的项会导致错误。索引会自动转换为整数,也可以使用变量作为索引。

1
2
3
4
5
6
7
8
9
10
*** Test Cases ***
Positive index
Login ${USER}[0] ${USER}[1]
Title Should Be Welcome ${USER}[0]!

Negative index
Keyword ${SEQUENCE}[-1]

Index defined as variable
Keyword ${SEQUENCE}[${INDEX}]

序列项访问也支持Python的相同的”切片”功能,语法如${var}[1:]。使用这种语法,不会得到一个单独的项,而是得到原始序列的一个切片。与Python一样,可以指定开始索引、结束索引和步长:

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Test Cases ***
Start index
Keyword ${SEQUENCE}[1:]

End index
Keyword ${SEQUENCE}[:4]

Start and end
Keyword ${SEQUENCE}[2:-1]

Step
Keyword ${SEQUENCE}[::2]
Keyword ${SEQUENCE}[1:-1:10]

注意

切片语法在Robot Framework 3.1中是新的。它在Robot Framework 4.0中被扩展,可以与列表扩展像@{var}[1:]这样一起工作。

注意

在Robot Framework 3.2之前,只有包含列表、元组或其他被认为是类似列表的对象的变量支持项和切片访问。现在,所有的序列,包括字符串和字节,都被支持。

访问单个字典项

可以使用语法${NAME}[key]访问字典变量的某个值,其中key是所选值的名称。键被认为是字符串,但非字符串键可以作为变量使用。以这种方式访问的字典值可以像标量变量一样使用。

如果一个键是一个字符串,也可以使用属性访问语法${NAME.key}访问它的值。有关此语法的更多详细信息,请参阅创建字典变量。

1
2
3
4
5
6
7
8
9
10
11
*** Test Cases ***
Dictionary variable item
Login ${USER}[name] ${USER}[password]
Title Should Be Welcome ${USER}[name]!

Key defined as variable
Log Many ${DICT}[${KEY}] ${DICT}[${42}]

Attribute access
Login ${USER.name} ${USER.password}
Title Should Be Welcome ${USER.name}!
嵌套项访问

也可以使用相同的项访问语法${var}[item1][item2]访问嵌套的可下标变量。这在处理经常由REST服务返回的JSON数据时特别有用。例如,如果一个变量${DATA}包含[{'id': 1, 'name': 'Robot'}, {'id': 2, 'name': 'Mr. X'}],这个测试将通过:

1
2
3
4
*** Test Cases ***
Nested item access
Should Be Equal ${DATA}[0][name] Robot
Should Be Equal ${DATA}[1][id] ${2}
环境变量

Robot Framework允许在测试数据中使用环境变量,语法为%{ENV_VAR_NAME}。它们限制为字符串值。可以通过将变量名和默认值用等号分隔,如%{ENV_VAR_NAME=default value},来指定一个默认值,如果环境变量不存在,则使用该默认值。

在测试执行之前在操作系统中设置的环境变量在执行期间是可用的,可以使用OperatingSystem库中的关键字Set Environment Variable创建新的环境变量,或者使用关键字Delete Environment Variable删除现有的环境变量。因为环境变量是全局的,所以在一个测试用例中设置的环境变量可以在执行后的其他测试用例中使用。然而,对环境变量的更改在测试执行后不会生效。

1
2
3
4
5
6
7
*** Test Cases ***
Environment variables
Log Current user: %{USER}
Run %{JAVA_HOME}${/}javac

Environment variables with defaults
Set port %{APPLICATION_PORT=8080}

注意

在Robot Framework 3.2中,新增了指定默认值的支持。

创建变量

变量可以从不同的来源产生。

变量部分

变量最常见的来源是套件文件和资源文件中的变量部分。变量部分很方便,因为它们允许在与其他测试数据相同的地方创建变量,而且所需的语法非常简单。它们的主要缺点是值总是字符串,而且不能动态创建。如果这两者中的任何一个是问题,可以使用变量文件代替。

创建标量变量

最简单的变量赋值就是将一个字符串设置为一个标量变量。这是通过在变量部分的第一列给出变量名(包括${}),在第二列给出值来完成的。如果第二列为空,则设置一个空字符串作为值。也可以在值中使用已定义的变量。

1
2
3
4
*** Variables ***
${NAME} Robot Framework
${VERSION} 2.0
${ROBOT} ${NAME} ${VERSION}

也可以(但不是必须)在变量名后面使用等号=,使变量赋值稍微明确一些。

1
2
3
*** Variables ***
${NAME} = Robot Framework
${VERSION} = 2.0

如果一个标量变量有一个长值,可以使用...语法将它分割成多行。默认情况下,行是用一个空格连接在一起的,但是可以通过在最后一个值后面使用一个分隔符配置选项来改变这一点:

1
2
3
4
5
6
7
*** Variables ***
${EXAMPLE} This value is joined
... together with a space.
${MULTILINE} First line.
... Second line.
... Third line.
... separator=\n

分隔符选项在Robot Framework 7.0中是新的,但也支持旧版本的分隔符配置。在它们中,第一个值可以包含一个特殊的SEPARATOR标记:

1
2
3
4
5
*** Variables ***
${MULTILINE} SEPARATOR=\n
... First line.
... Second line.
... Third line.

分隔符选项和SEPARATOR标记都是区分大小写的。建议使用分隔符选项,除非需要支持旧版本。

创建列表变量

创建列表变量和创建标量变量一样容易。同样,变量名在变量部分的第一列,值在后续的列中。一个列表变量可以有任意数量的值,从零开始,如果需要多个值,它们可以分割成几行。

1
2
3
4
5
6
*** Variables ***
@{NAMES} Matti Teppo
@{NAMES2} @{NAMES} Seppo
@{NOTHING}
@{MANY} one two three four
... five six seven
创建字典变量

在变量部分创建字典变量与创建列表变量类似。区别在于,项需要使用name=value语法或现有的字典变量创建。如果有多个具有相同名称的项,最后一个值具有优先权。如果名称包含一个字面等号,可以使用反斜杠\=进行转义。

1
2
3
4
5
6
*** Variables ***
&{USER 1} name=Matti address=xxx phone=123
&{USER 2} name=Teppo address=yyy phone=456
&{MANY} first=1 second=${2} ${3}=third
&{EVEN MORE} &{MANY} first=override empty=
... =empty key\=here=value

字典变量与普通Python字典相比有两个额外的属性。首先,这些字典的值可以像属性一样访问,这意味着可以使用扩展变量语法,如${VAR.key}。这只有在键是一个有效的属性名称,并且不匹配Python字典有的任何正常属性时才有效。例如,单个值&{USER}[name]也可以像${USER.name}(注意在这种情况下需要$)那样访问,但是使用${MANY.3}是不可能的。

信息

对于嵌套的字典变量,键可以像${VAR.nested.key}那样访问。这简化了处理嵌套数据结构的工作。

字典变量的另一个特殊属性是它们是有序的。这意味着,如果遍历这些字典,它们的项总是按照它们定义的顺序出现。如果字典被用作列表变量与FOR循环或其他方式一起使用,这可能很有用。当一个字典被用作一个列表变量时,实际的值包含字典键。例如,@{MANY}变量将具有值['first', 'second', 3]

基于另一个变量创建变量名

从Robot Framework 7.0开始,可以根据另一个变量动态创建变量名:

1
2
3
4
5
6
7
*** Variables ***
${X} Y
${${X}} Z # Name is created based on '${X}'.

*** Test Cases ***
Dynamically created name
Should Be Equal ${Y} Z

变量文件

变量文件是创建不同类型变量的最强大的机制。使用它们,可以将变量赋值给任何对象,并且它们还支持动态创建变量。变量文件的语法和如何使用变量文件在资源和变量文件部分有解释。

在命令行中设置变量

可以在命令行中单独使用--variable(-v)选项或使用变量文件与--variablefile(-V)选项来设置变量。从命令行设置的变量对所有执行的测试数据文件全局可用,并且它们还会覆盖变量部分和测试数据中导入的变量文件中具有相同名称的可能变量。

设置单个变量的语法是--variable name:value,其中name是变量的名称(不包括${}),value是它的值。可以通过多次使用此选项来设置多个变量。只有标量变量可以使用此语法设置,它们只能获取字符串值。

1
2
--variable EXAMPLE:value
--variable HOST:localhost:7272 --variable USER:robot

在上面的例子中,设置了变量,使得

  • ${EXAMPLE}得到值value
  • ${HOST}${USER}得到值localhost:7272robot

从命令行使用变量文件的基本语法是--variablefile path/to/variables.py,在使用变量文件部分有更多的细节。实际创建的变量取决于引用的变量文件中有哪些变量。

如果从命令行给出了变量文件和单个变量,那么后者具有更高的优先级。

关键字的返回值

关键字的返回值也可以设置为变量。这允许在不同的测试库中的不同关键字之间进行通信。

以这种方式设置的变量与其他任何变量在其他方面是相似的,但它们只在创建它们的局部范围内可用。因此,例如,不可能在一个测试用例中设置一个变量,然后在另一个测试用例中使用它。这是因为,一般来说,自动化测试用例不应该相互依赖,而且意外地设置一个在其他地方使用的变量可能会导致难以调试的错误。如果真正需要在一个测试用例中设置一个变量,并在另一个测试用例中使用它,可以使用BuiltIn关键字,如下一节所解释的。

分配标量变量

任何由关键字返回的值都可以分配给一个标量变量。如下例所示,所需的语法非常简单:

1
2
3
4
*** Test Cases ***
Returning
${x} = Get X an argument
Log We got ${x}!

在上述例子中,Get X关键字返回的值首先被设置到变量${x}中,然后被Log关键字使用。在变量名后面有等号=不是必须的,但它使赋值更明确。像这样在测试用例和用户关键字级别创建局部变量都可以工作。

注意

虽然一个值被赋值给一个标量变量,但如果它有一个类似列表的值,它可以作为一个列表变量使用,如果它有一个类似字典的值,它可以作为一个字典变量使用。

1
2
3
4
5
*** Test Cases ***
Example
${list} = Create List first second third
Length Should Be ${list} 3
Log Many @{list}
分配带有项值的变量

从Robot Framework 6.1开始,当使用支持项赋值的变量,如列表或字典,可以通过使用语法${var}[item]指定项的索引或键来设置它们的值,其中item部分本身可以包含一个变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
Item assignment to list
${list} = Create List one two three four
${list}[0] = Set Variable first
${list}[${1}] = Set Variable second
${list}[2:3] = Evaluate ['third']
${list}[-1] = Set Variable last
Log Many @{list} # Logs 'first', 'second', 'third' and 'last'

Item assignment to dictionary
${dict} = Create Dictionary first_name=unknown
${dict}[first_name] = Set Variable John
${dict}[last_name] = Set Variable Doe
Log ${dictionary} # Logs {'first_name': 'John', 'last_name': 'Doe'}
基于另一个变量创建变量名

从Robot Framework 7.0开始,可以根据另一个变量动态创建被赋值变量的名称:

1
2
3
4
5
*** Test Cases ***
Dynamically created name
${x} = Set Variable y
${${x}} = Set Variable z # Name is created based on '${x}'.
Should Be Equal ${y} z
分配列表变量

如果一个关键字返回一个列表或任何类似列表的对象,可以将其分配给一个列表变量:

1
2
3
4
5
*** Test Cases ***
Example
@{list} = Create List first second third
Length Should Be ${list} 3
Log Many @{list}

因为所有的Robot Framework变量都存储在同一个命名空间中,所以将一个值分配给一个标量变量或一个列表变量之间没有太大的区别。这可以通过比较上面的最后两个例子来看出。主要的区别是,当创建一个列表变量时,Robot Framework会自动验证该值是否是一个列表或类似列表,存储的变量值将是从返回值创建的一个新列表。当分配给一个标量变量时,返回值不会被验证,存储的值将是返回的完全相同的对象。

分配字典变量

如果一个关键字返回一个字典或任何类似字典的对象,可以将其分配给一个字典变量:

1
2
3
4
5
6
*** Test Cases ***
Example
&{dict} = Create Dictionary first=1 second=${2} ${3}=third
Length Should Be ${dict} 3
Do Something &{dict}
Log ${dict.first}

因为所有的Robot Framework变量都存储在同一个命名空间中,也可以将一个字典分配给一个标量变量,然后在需要的时候将其作为一个字典使用。然而,显式创建一个字典变量有一些实际的好处。首先,Robot Framework验证返回的值是否是一个字典或类似字典,就像它验证列表变量只能得到一个类似列表的值一样。

一个更大的好处是,该值被转换为一个特殊的字典,它也在创建变量部分中创建字典变量时使用。这些字典中的值可以使用属性访问,如上例中的${dict.first}。这些字典也是有序的,但如果原始字典不是有序的,结果的顺序是任意的。

分配多个变量

如果一个关键字返回一个列表或一个类似列表的对象,可以将单个值分配给多个标量变量或标量变量和一个列表变量。

1
2
3
4
5
6
*** Test Cases ***
Assign multiple
${a} ${b} ${c} = Get Three
${first} @{rest} = Get Three
@{before} ${last} = Get Three
${begin} @{middle} ${end} = Get Three

假设关键字Get Three返回一个列表[1, 2, 3],则创建以下变量:

  • ${a}${b}${c}的值分别为1、2和3。
  • ${first}的值为1,@{rest}的值为[2, 3]
  • @{before}的值为[1, 2]${last}的值为3。
  • ${begin}的值为1,@{middle}的值为[2]${end}的值为3。

如果返回的列表的值多于或少于要分配的标量变量的数量,那么这是一个错误。此外,只允许一个列表变量,字典变量只能单独分配。

自动记录分配的变量值

为了更容易理解执行过程中发生了什么,分配的值的开始部分会被自动记录。默认情况下,显示前200个字符,但可以通过在运行测试时使用--maxassignlength命令行选项来改变这个值。如果值为零或负数,整个分配的值将被隐藏。

1
2
--maxassignlength 1000
--maxassignlength 0

不完全记录值的原因是,它可能非常大。如果总是想完全看到某个值,可以使用BuiltIn Log关键字在分配后记录它。

注意

--maxassignlength选项在Robot Framework 5.0中是新的。

VAR语法

从Robot Framework 7.0开始,可以使用VAR语法在测试和用户关键字内部创建变量。VAR标记是区分大小写的,必须跟着一个变量名和值。除了必须的VAR,整体语法大多与在变量部分创建变量时的语法相同。

新的语法旨在使创建变量更简单、更统一。它特别用于替代BuiltIn关键字Set VariableSet Test VariableSet Suite VariableSet Global Variable,但也可以用来替代CatenateCreate ListCreate Dictionary

创建标量变量

在简单的情况下,通过给出一个变量名和它的值来创建标量变量。值可以是一个硬编码的字符串,也可以包含一个变量。如果值很长,可以将其分割成多个列和行。在这种情况下,默认情况下,部分会用一个空格连接在一起,但可以使用分隔符配置选项来指定要使用的分隔符。在创建基于关键字的返回值的变量和在变量部分中的变量时,变量名后面可以有一个可选的=

1
2
3
4
5
6
7
8
9
10
11
12
*** Test Cases ***
Scalar examples
VAR ${simple} variable
VAR ${equals} = this works too
VAR ${variable} value contains ${simple}
VAR ${sentence} This is a bit longer variable value
... that is split into multiple rows.
... These parts are joined with a space.
VAR ${multiline} This is another longer value.
... This time there is a custom separator.
... As the result this becomes a multiline string.
... separator=\n
创建列表和字典变量

列表和字典变量的创建与标量变量类似。创建字典时,必须使用name=value语法指定项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*** Test Cases ***
List examples
VAR @{two items} Robot Framework
VAR @{empty list}
VAR @{lot of stuff}
... first item
... second item
... third item
... fourth item
... last item

Dictionary examples
VAR &{two items} name=Robot Framework url=http://robotframework.org
VAR &{empty dict}
VAR &{lot of stuff}
... first=1
... second=2
... third=3
... fourth=4
... last=5
作用域

使用VAR语法创建的变量只在创建它们的测试或用户关键字内部可用。然而,可以通过使用作用域配置选项来改变这一点。支持的值有LOCAL(默认)、TEST(在当前测试中可用)、TASK(TEST的别名)、SUITE(在当前套件中可用)和GLOBAL(全局可用)。尽管Robot Framework变量是不区分大小写的,但建议使用大写字母表示非局部变量名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
*** Variables ***
${SUITE} this value is overridden

*** Test Cases ***
Scope example
VAR ${local} local value
VAR ${TEST} test value scope=TEST
VAR ${SUITE} suite value scope=SUITE
VAR ${GLOBAL} global value scope=GLOBAL
Should Be Equal ${local} local value
Should Be Equal ${TEST} test value
Should Be Equal ${SUITE} suite value
Should Be Equal ${GLOBAL} global value
Keyword
Should Be Equal ${TEST} new test value
Should Be Equal ${SUITE} new suite value
Should Be Equal ${GLOBAL} new global value

Scope example, part 2
Should Be Equal ${SUITE} new suite value
Should Be Equal ${GLOBAL} new global value

*** Keywords ***
Keyword
Should Be Equal ${TEST} test value
Should Be Equal ${SUITE} suite value
Should Be Equal ${GLOBAL} global value
VAR ${TEST} new ${TEST} scope=TEST
VAR ${SUITE} new ${SUITE} scope=SUITE
VAR ${GLOBAL} new ${GLOBAL} scope=GLOBAL
Should Be Equal ${TEST} new test value
Should Be Equal ${SUITE} new suite value
Should Be Equal ${GLOBAL} new global value
条件创建变量

VAR语法可以与IF/ELSE结构一起工作,这使得条件创建变量变得容易。在简单的情况下,使用内联IF可能很方便。

1
2
3
4
5
6
7
8
9
10
11
12
*** Test Cases ***
IF/ELSE example
IF "${ENV}" == "devel"
VAR ${address} 127.0.0.1
VAR ${name} demo
ELSE
VAR ${address} 192.168.1.42
VAR ${name} robot
END

Inline IF
IF "${ENV}" == "devel" VAR ${name} demo ELSE VAR ${name} robot
基于另一个变量创建变量名

如果有需要,也可以根据另一个变量动态创建变量名。

1
2
3
4
5
*** Test Cases ***
Dynamic name
VAR ${x} y # Normal assignment.
VAR ${${x}} z # Name created dynamically.
Should Be Equal ${y} z

使用Set Test/Suite/Global Variable关键字

注意

在使用Robot Framework 7.0或更高版本时,推荐使用VAR语法而不是这些关键字。

BuiltIn库有Set Test VariableSet Suite VariableSet Global Variable关键字,可以用于在测试执行期间动态设置变量。如果一个变量已经存在于新的作用域中,其值将被覆盖,否则将创建一个新的变量。

使用Set Test Variable关键字设置的变量在当前执行的测试用例的作用域内处处可用。例如,如果在一个用户关键字中设置了一个变量,它既可以在测试用例级别使用,也可以在当前测试中使用的所有其他用户关键字中使用。其他的测试用例将不会看到用这个关键字设置的变量。在测试的作用域之外调用Set Test Variable(例如,在套件设置或拆卸中)是一个错误。

使用Set Suite Variable关键字设置的变量在当前执行的测试套件的作用域内处处可用。因此,使用这个关键字设置变量具有与在测试数据文件中使用变量部分创建它们或从变量文件中导入它们相同的效果。其他的测试套件,包括可能的子测试套件,将不会看到用这个关键字设置的变量。

使用Set Global Variable关键字设置的变量在设置它们后执行的所有测试用例和套件中全局可用。因此,使用这个关键字设置变量具有与使用--variable--variablefile选项从命令行创建它们相同的效果。因为这个关键字可以在任何地方改变变量,所以应该小心使用。

注意

Set Test/Suite/Global Variable关键字直接将命名变量设置到测试、套件或全局变量作用域,并且不返回任何东西。另一方面,另一个BuiltIn关键字Set Variable使用返回值设置局部变量。

内置变量

Robot Framework自动提供了一些内置变量。

操作系统变量

与操作系统相关的内置变量使得测试数据对操作系统无关。

变量 说明
${CURDIR} 测试数据文件所在目录的绝对路径。此变量区分大小写。
${TEMPDIR} 系统临时目录的绝对路径。在类 UNIX 系统中通常是 /tmp,在 Windows 中是 c:\Documents and Settings\<user>\Local Settings\Temp
${EXECDIR} 测试执行开始的目录的绝对路径。
${/} 系统目录路径分隔符。在类 UNIX 系统中是 /,在 Windows 中是 \
${:} 系统路径元素分隔符。在类 UNIX 系统中是 :,在 Windows 中是 ;
${\n} 系统行分隔符。在类 UNIX 系统中是 \n,在 Windows 中是 \r\n
1
2
3
4
*** Test Cases ***
Example
Create Binary File ${CURDIR}${/}input.data Some text here${\n}on two lines
Set Environment Variable CLASSPATH ${TEMPDIR}${:}${CURDIR}${/}foo.jar

数字变量

变量语法可以用于创建整数和浮点数,如下例所示。当一个关键字期望得到一个实际的数字,而不是一个看起来像数字的字符串作为参数时,这是有用的。

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Example 1A
Connect example.com 80 # Connect gets two strings as arguments

Example 1B
Connect example.com ${80} # Connect gets a string and an integer

Example 2
Do X ${3.14} ${-1e-4} # Do X gets floating point numbers 3.14 and -0.0001

也可以使用0b、0o和0x前缀分别从二进制、八进制和十六进制值创建整数。语法不区分大小写。

1
2
3
4
5
6
*** Test Cases ***
Example
Should Be Equal ${0b1011} ${11}
Should Be Equal ${0o10} ${8}
Should Be Equal ${0xff} ${255}
Should Be Equal ${0B1010} ${0XA}

布尔和None/null变量

也可以使用变量语法创建布尔值和Python的None,就像创建数字一样。

1
2
3
4
5
6
7
*** Test Cases ***
Boolean
Set Status ${true} # Set Status gets Boolean true as an argument
Create Y something ${false} # Create Y gets a string and Boolean false

None
Do XYZ ${None} # Do XYZ gets Python None as an argument

这些变量是不区分大小写的,所以例如${True}${true}是等价的。

空格和空变量

可以使用变量${SPACE}${EMPTY}分别创建空格和空字符串。这些变量在需要转义空格或空单元格时很有用。如果需要多个空格,可以使用扩展变量语法,如${SPACE * 5}。在下面的例子中,Should Be Equal关键字得到相同的参数,但使用变量的那些比使用反斜杠的更容易理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
*** Test Cases ***
One space
Should Be Equal ${SPACE} \ \

Four spaces
Should Be Equal ${SPACE * 4} \ \ \ \ \

Ten spaces
Should Be Equal ${SPACE * 10} \ \ \ \ \ \ \ \ \ \ \

Quoted space
Should Be Equal "${SPACE}" " "

Quoted spaces
Should Be Equal "${SPACE * 2}" " \ "

Empty
Should Be Equal ${EMPTY} \

还有一个空列表变量@{EMPTY}和一个空字典变量&{EMPTY}。因为它们没有内容,所以当它们在测试数据中被使用时,基本上就消失了。它们在测试模板中很有用,例如当模板关键字没有参数时,或者在不同的作用域中覆盖列表或字典变量时。不能修改@{EMPTY}&{EMPTY}的值。

1
2
3
4
5
6
7
8
*** Test Cases ***
Template
[Template] Some keyword
@{EMPTY}

Override
Set Global Variable @{LIST} @{EMPTY}
Set Suite Variable &{DICT} &{EMPTY}

信息

${SPACE}表示ASCII空格(\x20),其他空格应使用转义序列指定,如\xA0(NO-BREAK SPACE)和\u3000(IDEOGRAPHIC SPACE)。

自动变量

在测试数据中也可以使用一些自动变量。这些变量在测试执行期间可以有不同的值,有些甚至不是一直可用。改变这些变量的值不会影响原始值,但是一些值可以使用BuiltIn库的关键字动态改变。

可用的自动变量如下:

变量 解释 可用性
${TEST NAME} 当前测试用例的名称。 测试用例
@{TEST TAGS} 包含当前测试用例的标签,按字母顺序排列。可以使用Set TagsRemove Tags关键字动态修改。 测试用例
${TEST DOCUMENTATION} 当前测试用例的文档。可以使用Set Test Documentation关键字动态设置。 测试用例
${TEST STATUS} 当前测试用例的状态,PASSFAIL 测试拆卸
${TEST MESSAGE} 当前测试用例的消息。 测试拆卸
${PREV TEST NAME} 上一个测试用例的名称,如果没有执行过测试,则为空字符串。 任何地方
${PREV TEST STATUS} 上一个测试用例的状态:PASSFAIL,或者当没有执行过测试时为空字符串。 任何地方
${PREV TEST MESSAGE} 上一个测试用例可能的错误消息。 任何地方
${SUITE NAME} 当前测试套件的全名。 任何地方
${SUITE SOURCE} 套件文件或目录的绝对路径。 任何地方
${SUITE DOCUMENTATION} 当前测试套件的文档。可以使用Set Suite Documentation关键字动态设置。 任何地方
&{SUITE METADATA} 当前测试套件的自由元数据。可以使用Set Suite Metadata关键字设置。 任何地方
${SUITE STATUS} 当前测试套件的状态,PASSFAIL 套件拆卸
${SUITE MESSAGE} 当前测试套件的完整消息,包括统计信息。 套件拆卸
${KEYWORD STATUS} 当前关键字的状态,PASSFAIL 用户关键字拆卸
${KEYWORD MESSAGE} 当前关键字可能的错误消息。 用户关键字拆卸
${LOG LEVEL} 当前日志级别。 任何地方
${OUTPUT DIR} 输出目录的绝对路径,为字符串。 任何地方
${OUTPUT FILE} 输出文件的绝对路径,为字符串,如果输出文件未创建,则为字符串NONE 任何地方
${LOG FILE} 日志文件的绝对路径,为字符串,如果日志文件未创建,则为字符串NONE 任何地方
${REPORT FILE} 报告文件的绝对路径,为字符串,如果报告文件未创建,则为字符串NONE 任何地方
${DEBUG FILE} 调试文件的绝对路径,为字符串,如果调试文件未创建,则为字符串NONE 任何地方
&{OPTIONS} 一个暴露命令行选项的字典。字典的键匹配命令行选项,可以像{OPTIONS}[key]和OPTIONS[key]和{OPTIONS.key}这样访问。可用的选项有:{OPTIONS.exclude} (–exclude)、OPTIONS.exclude(−−exclude)、{OPTIONS.include} (–include)、{OPTIONS.skip} (–skip)、OPTIONS.skip(−−skip)、{OPTIONS.skip_on_failure} (–skiponfailure)。在RF 5.0中新增。以后可以暴露更多的选项。 任何地方

套件相关的变量${SUITE SOURCE}${SUITE NAME}${SUITE DOCUMENTATION}&{SUITE METADATA}以及与命令行选项相关的选项,如${LOG FILE}&{OPTIONS},在导入库和变量文件时就已经可用。然而,在导入时,这些自动变量中可能的变量还没有被解析。

变量优先级和作用域

来自不同来源的变量有不同的优先级,并且在不同的作用域中可用。

变量优先级

命令行中的变量

在实际的测试执行开始之前,可以设置的所有变量中,命令行中设置的变量具有最高的优先级。它们会覆盖在测试用例文件的变量部分创建的可能的变量,以及在测试数据中导入的资源和变量文件中的变量。

单独设置的变量(--variable选项)会覆盖使用变量文件设置的变量(--variablefile选项)。如果多次指定同一个单独的变量,最后指定的那个将覆盖前面的。这允许在启动脚本中为变量设置默认值,并从命令行中覆盖它们。注意,如果多个变量文件有相同的变量,那么在首先指定的文件中的变量具有最高的优先级。

测试用例文件中的变量部分

在测试用例文件的变量部分创建的变量对该文件中的所有测试用例都可用。这些变量会覆盖在导入的资源和变量文件中具有相同名称的可能的变量。

在变量部分创建的变量在创建它们的文件中的所有其他部分都可用。这意味着它们也可以在设置部分中使用,例如,用于从资源和变量文件中导入更多的变量。

导入的资源和变量文件

从资源和变量文件导入的变量在测试数据中创建的所有变量中具有最低的优先级。资源文件和变量文件中的变量具有相同的优先级。如果几个资源和/或变量文件有相同的变量,那么首先导入的文件中的变量将被使用。

如果一个资源文件导入资源文件或变量文件,那么它自己的变量部分中的变量具有比它导入的变量更高的优先级。所有这些变量都可用于导入此资源文件的文件。

注意,从资源和变量文件导入的变量在导入它们的文件的变量部分中不可用。这是因为在处理设置部分(其中导入资源文件和变量文件)之前,已经处理了变量部分。

在测试执行期间设置的变量

在测试执行期间设置的变量,无论是使用关键字的返回值还是使用Set Test/Suite/Global Variable关键字,总是覆盖在它们被设置的作用域中可能存在的变量。从某种意义上说,它们因此具有最高的优先级,但另一方面,它们不影响它们定义的作用域之外的变量。

内置变量

${TEMPDIR}${TEST_NAME}这样的内置变量具有所有变量中最高的优先级。它们不能使用变量部分或从命令行覆盖,但即使它们也可以在测试执行期间重置。这个规则的一个例外是数字变量,如果没有找到其他变量,它们会动态地被解析。因此,它们可以被覆盖,但这通常是一个坏主意。此外,${CURDIR}是特殊的,因为它在测试数据处理时间就已经被替换了。

变量作用域

根据它们的创建位置和方式,变量可以具有全局、测试套件、测试用例或局部作用域。

全局作用域

全局变量在测试数据中的任何地方都可用。这些变量通常在命令行中使用--variable--variablefile选项设置,但也可以在测试数据的任何地方使用VAR语法或Set Global Variable关键字创建新的全局变量或更改现有的全局变量。此外,内置变量也是全局的。

建议使用大写字母表示所有全局变量。

测试套件作用域

具有测试套件作用域的变量在定义或导入它们的测试套件的任何地方都可用。它们可以在变量部分中创建,从资源和变量文件中导入,或者在测试执行期间使用VAR语法或Set Suite Variable关键字设置。

测试套件作用域不是递归的,这意味着在高级测试套件中可用的变量在低级套件中不可用。如果需要,可以使用资源和变量文件共享变量。

由于这些变量在使用它们的测试套件中可以被认为是全局的,因此也建议使用大写字母表示它们。

测试用例作用域

具有测试用例作用域的变量在测试用例和测试使用的所有用户关键字中可见。最初在这个作用域中没有变量,但是可以在测试用例的任何地方使用VAR语法或Set Test Variable关键字创建它们。在套件设置或套件拆卸中尝试创建测试变量会导致错误。

测试用例作用域中的变量也在某种程度上是全局的。因此,通常建议使用大写字母表示它们。

局部作用域

测试用例和用户关键字有一个局部变量作用域,其他测试或关键字看不到。局部变量可以使用执行的关键字的返回值和VAR语法创建,用户关键字也可以将它们作为参数获取。

建议使用小写字母表示局部变量。

高级变量特性

扩展变量语法

扩展变量语法允许访问分配给变量的对象的属性(例如,${object.attribute})甚至调用其方法(例如,${obj.getName()})。它既适用于标量变量,也适用于列表变量,但主要用于前者。

扩展变量语法是一个强大的特性,但应谨慎使用。访问属性通常不是问题,相反,因为一个包含多个属性的对象的变量通常比有多个变量更好。另一方面,调用方法,特别是当它们带有参数时,可能会使测试数据变得相当复杂。如果发生这种情况,建议将代码移动到测试库中。

下面的例子说明了扩展变量语法的最常见用法。首先假设有以下变量文件和测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyObject:

def __init__(self, name):
self.name = name

def eat(self, what):
return '%s eats %s' % (self.name, what)

def __str__(self):
return self.name

OBJECT = MyObject('Robot')
DICTIONARY = {1: 'one', 2: 'two', 3: 'three'}
*** Test Cases ***
Example
KW 1 ${OBJECT.name}
KW 2 ${OBJECT.eat('Cucumber')}
KW 3 ${DICTIONARY[2]}

当执行这个测试数据时,关键字得到的参数如下:

  • KW 1得到字符串Robot
  • KW 2得到字符串Robot eats Cucumber
  • KW 3得到字符串two

扩展变量语法按以下顺序进行评估:

  1. 使用完整的变量名搜索变量。只有在没有找到匹配的变量时,才会评估扩展变量语法。
  2. 创建基变量的名称。名称的主体由打开{后的所有字符组成,直到第一个不是字母数字字符或空格的字符出现。例如,${OBJECT.name}${DICTIONARY[2]}的基变量分别是OBJECT和DICTIONARY。
  3. 搜索与主体匹配的变量。如果没有匹配,将引发异常并使测试用例失败。
  4. 将花括号内的表达式作为Python表达式进行评估,以便将基变量名替换为其值。如果评估失败,因为语法无效或查询的属性不存在,将引发异常并使测试失败。
  5. 将整个扩展变量替换为从评估返回的值。

许多标准的Python对象,包括字符串和数字,都有可以使用扩展变量语法显式或隐式使用的方法。有时这可能非常有用,减少了设置临时变量的需要,但也很容易过度使用它,创建真正的神秘测试数据。下面的例子展示了一些相当好的用法。

1
2
3
4
5
6
7
8
9
10
*** Test Cases ***
String
${string} = Set Variable abc
Log ${string.upper()} # Logs 'ABC'
Log ${string * 2} # Logs 'abcabc'

Number
${number} = Set Variable ${-2}
Log ${number * 10} # Logs -20
Log ${number.__abs__()} # Logs 2

注意

尽管在正常的Python代码中推荐使用abs(number)而不是number.__abs__(),但使用${abs(number)}是不起作用的。这是因为变量名必须在扩展语法的开始。在测试数据中像这样使用__xxx__方法已经有点可疑了,通常最好将这种逻辑移动到测试库中。

扩展变量语法也适用于列表变量上下文。例如,如果一个分配给变量${EXTENDED}的对象有一个包含列表值的属性,它可以被用作列表变量@{EXTENDED.attribute}

扩展变量赋值

可以使用关键字返回值和扩展变量语法的变体,将对象的属性设置到标量变量中。假设有前面例子中的变量${OBJECT},可以像下面的例子那样设置它的属性。

1
2
3
4
*** Test Cases ***
Example
${OBJECT.name} = Set Variable New name
${OBJECT.new_attr} = Set Variable New attribute

扩展变量赋值语法按照以下规则进行评估:

  1. 被赋值的变量必须是一个标量变量,并且至少有一个点。否则,不使用扩展赋值语法,而是正常赋值变量。
  2. 如果存在一个与完整名称相匹配的变量(例如,上面例子中的${OBJECT.name}),那么该变量将被赋予一个新的值,不使用扩展语法。
  3. 创建基变量的名称。名称的主体由打开${和最后一个点之间的所有字符组成,例如,${OBJECT.name}中的OBJECT${foo.bar.zap}中的foo.bar。正如第二个例子所示,基名称可能包含正常的扩展变量语法。
  4. 通过取最后一个点和闭合}之间的所有字符来创建要设置的属性的名称,例如,${OBJECT.name}中的name。如果名称不是以字母或下划线开头,并且只包含这些字符和数字,那么该属性被认为是无效的,不使用扩展语法。而是创建一个具有完整名称的新变量。
  5. 搜索与基名称匹配的变量。如果没有找到变量,那么不使用扩展语法,而是使用完整的变量名称创建一个新的变量。
  6. 如果找到的变量是一个字符串或一个数字,那么忽略扩展语法,并使用完整的名称创建一个新的变量。这是因为不能向Python字符串或数字添加新的属性,这样新的语法也不会向后不兼容。
  7. 如果所有前面的规则都匹配,那么属性被设置到基变量。如果设置失败,由于任何原因,将引发异常并使测试失败。

注意

与通常使用关键字返回值赋值变量不同,使用扩展赋值语法对变量进行的更改不限于当前作用域。因为没有创建新的变量,而是改变了现有变量的状态,所以所有看到该变量的测试和关键字也会看到这些更改。

变量内的变量

变量也可以在变量内部,当使用这种语法时,变量从内到外进行解析。例如,如果有一个变量${var${x}},那么${x}首先被解析。如果它的值是name,那么最终的值就是变量${varname}的值。可以有多个嵌套的变量,但如果它们中的任何一个不存在,解析最外层的变量就会失败。

在下面的例子中,Do X获取的值是${JOHN HOME}${JANE HOME},取决于Get Name返回的是john还是jane。如果它返回其他的东西,解析${${name} HOME}就会失败。

1
2
3
4
5
6
7
8
*** Variables ***
${JOHN HOME} /home/john
${JANE HOME} /home/jane

*** Test Cases ***
Example
${name} = Get Name
Do X ${${name} HOME}

内联Python评估

变量语法也可以用于评估Python表达式。基本语法是${{expression}},即表达式周围有双花括号。表达式可以是任何有效的Python表达式,如${{1 + 2}}${{['a', 'list']}}。允许在表达式周围有空格,所以${{ 1 + 2 }}${{ ['a', 'list'] }}也是有效的。除了使用正常的标量变量,列表变量和字典变量也分别支持@{{expression}}&{{expression}}语法。

这个相当高级的功能的主要用途是:

  • 评估涉及Robot Framework的变量的Python表达式(${{len('${var}') > 3}}${{$var[0] if $var is not None else None}})。
  • 创建不是Python基本类型的值(${{decimal.Decimal('0.11')}}${{datetime.date(2019, 11, 5)}})。
  • 动态创建值(${{random.randint(0, 100)}}${{datetime.date.today()}})。
  • 构造集合,特别是嵌套的集合(${{[1, 2, 3, 4]}}${{ {'id': 1, 'name': 'Example', 'children': [7, 9]} }})。
  • 访问Python模块中的常量和其他有用的属性(${{math.pi}}${{platform.system()}})。

这与前面讨论的扩展变量语法有一些类似的功能。如上面的例子所示,这种语法更强大,因为它提供了访问Python内置函数如len()和模块如math的能力。除了能够在表达式中使用像${var}这样的变量(它们在评估之前被替换),在评估期间还可以使用特殊的$var语法使用变量。整个表达式语法在评估表达式附录中有解释。

信息

与其创建复杂的表达式,通常最好将逻辑移动到自定义库中。这样可以简化维护,使测试数据更容易理解,也可以提高执行速度。

注意

内联Python评估语法是在Robot Framework 3.2中新增的。

创建用户关键字

关键字部分用于通过组合现有关键字来创建新的高级关键字。这些关键字被称为用户关键字,以区别于在测试库中实现的最低级别的库关键字。创建用户关键字的语法与创建测试用例的语法非常接近,这使得学习变得容易。

基本语法

在很多方面,用户关键字的整体语法与测试用例语法相同。用户关键字在关键字部分中创建,这与测试用例部分的区别仅在于用于标识它们的名称。用户关键字的名称与测试用例名称一样,位于第一列。此外,用户关键字也是由关键字创建的,无论是来自测试库的关键字还是其他用户关键字。关键字名称通常在第二列,但是在从关键字返回值设置变量时,它们在后续列中。

1
2
3
4
5
6
7
8
9
*** Keywords ***
Open Login Page
Open Browser http://host/login.html
Title Should Be Login Page

Title Should Start With
[Arguments] ${expected}
${title} = Get Title
Should Start With ${title} ${expected}

大多数用户关键字需要一些参数。这个重要的特性已经在上面的第二个例子中使用了,它在本节后面详细解释,与用户关键字返回值类似。

用户关键字可以在套件文件、资源文件和套件初始化文件中创建。在资源文件中创建的关键字对使用它们的文件可用,而其他关键字只在创建它们的文件中可用。

关键字部分中的设置

用户关键字可以有与测试用例类似的设置,它们有相同的方括号语法将它们与关键字名称分开。所有可用的设置都列在下面,并在本节后面解释。

  • [Documentation] 用于设置用户关键字文档。
  • [Tags] 为关键字设置标签。
  • [Arguments] 指定用户关键字参数。
  • [Setup], [Teardown] 指定用户关键字的设置和拆卸。[Setup]是在Robot Framework 7.0中新增的。
  • [Timeout] 设置可能的用户关键字超时。超时在自己的部分中讨论。
  • [Return] 指定用户关键字返回值。在Robot Framework 7.0中已弃用,应使用RETURN语句。

注意

上面使用的格式是推荐的,但设置名称不区分大小写,方括号和名称之间允许有空格。例如,[ TAGS ]:setting是有效的。

用户关键字的名称和文档

用户关键字的名称在关键字部分的第一列中定义。当然,名称应该是描述性的,接受有相当长的关键字名称。实际上,当创建用例样式的测试用例时,最高级别的关键字通常被构造为句子甚至段落。

用户关键字可以有一个文档,通过[Documentation]设置进行设置。它支持与测试用例文档相同的格式,分割到多行,以及其他特性。这个设置在测试数据中记录了用户关键字。它也显示在更正式的关键字文档中,Libdoc工具可以从资源文件中创建这个文档。最后,文档的第一行逻辑行,直到第一个空行,显示为测试日志中的关键字文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*** Keywords ***
One line documentation
[Documentation] One line documentation.
No Operation

Multiline documentation
[Documentation] The first line creates the short doc.
...
... This is the body of the documentation.
... It is not shown in Libdoc outputs but only
... the short doc is shown in logs.
No Operation

Short documentation in multiple lines
[Documentation] If the short doc gets longer, it can span
... multiple physical lines.
...
... The body is separated from the short doc with
... an empty line.
No Operation

有时候,关键字需要被移除,替换为新的,或者由于其他原因被弃用。用户关键字可以通过在文档开始处标记DEPRECATED来标记为弃用,这将在使用关键字时引发警告。有关更多信息,请参见弃用关键字部分。

注意

在Robot Framework 3.1之前,简短的文档只包含关键字文档的第一行物理行。

用户关键字标签

用户关键字和库关键字都可以有标签。与标记测试用例类似,有两个设置影响用户关键字标签:

  • 在设置部分的关键字标签设置 所有在此设置的文件中的关键字总是获得指定的标签。
  • 每个关键字的[Tags]设置 关键字获得这些标签,除了可能使用关键字标签设置指定的标签。[Tags]设置还允许使用-tag语法删除关键字标签设置的标签。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** Settings ***
Keyword Tags gui html

*** Keywords ***
No own tags
[Documentation] Keyword has tags 'gui' and 'html'.
No Operation

Own tags
[Documentation] Keyword has tags 'gui', 'html', 'own' and 'tags'.
[Tags] own tags
No Operation

Remove common tag
[Documentation] Test has tags 'gui' and 'own'.
[Tags] own -html
No Operation

关键字标签可以使用变量指定,-tag语法支持模式等,与测试用例标签完全一样。

除了使用专用设置外,关键字标签也可以在文档的最后一行使用Tags:前缀指定,以便用逗号分隔标签。例如,以下两个关键字获得相同的三个标签:

1
2
3
4
5
6
7
8
9
*** Keywords ***
Settings tags using separate setting
[Tags] my fine tags
No Operation

Settings tags using documentation
[Documentation] I have documentation. And my documentation has tags.
... Tags: my, fine, tags
No Operation

关键字标签显示在日志和由Libdoc生成的文档中,其中关键字也可以基于标签进行搜索。--removekeywords--flattenkeywords命令行选项也支持通过标签选择关键字,以后可能会添加关键字标签的新用途。

与测试用例标签类似,用户关键字标签带有robot:前缀是由Robot Framework本身保留的特殊功能。因此,用户不应使用这些前缀的任何标签,除非实际激活特殊功能。从Robot Framework 6.1开始,可以使用保留的标签robot:flatten在执行时间内展开关键字。

注意

关键字标签是在Robot Framework 6.0中新增的。在早期版本中,所有关键字标签都需要使用[Tags]设置指定。

注意

删除常见标签的-tag语法是在Robot Framework 7.0中新增的。

用户关键字参数

大多数用户关键字需要接受一些参数。指定它们的语法可能是使用Robot Framework通常需要的最复杂的特性,但即使是这样,它也相对容易,特别是在最常见的情况下。参数通常使用[Arguments]设置指定,参数名称使用与变量相同的语法,例如${arg}

用户关键字的位置参数

指定参数的最简单方式(除了根本不需要它们)是只使用位置参数。在大多数情况下,这就是所有需要的。

语法是这样的,首先给出[Arguments]设置,然后在后续的单元格中定义参数名称。每个参数都在自己的单元格中,使用与变量相同的语法。关键字必须使用与其签名中的参数名称一样多的参数。实际的参数名称对框架来说并不重要,但从用户的角度来看,它们应该尽可能具有描述性。建议在变量名称中使用小写字母,无论是${my_arg}${my arg}还是${myArg}

1
2
3
4
5
6
7
8
9
10
*** Keywords ***
One Argument
[Arguments] ${arg_name}
Log Got argument ${arg_name}

Three Arguments
[Arguments] ${arg1} ${arg2} ${arg3}
Log 1st argument: ${arg1}
Log 2nd argument: ${arg2}
Log 3rd argument: ${arg3}
用户关键字的默认值

在创建用户关键字时,位置参数在大多数情况下是足够的。然而,有时候关键字对一些或所有的参数有默认值是有用的。用户关键字也支持默认值,需要的新语法并没有给已经讨论过的基本语法增加很多内容。

简单来说,默认值被添加到参数中,首先是等号(=),然后是值,例如${arg}=default。可以有许多带有默认值的参数,但它们都必须在正常的位置参数之后给出。默认值可以包含在测试、套件或全局范围内创建的变量,但不能使用关键字执行器的局部变量。默认值也可以根据关键字接受的早期参数定义。

注意

默认值的语法对空格敏感。等号前不允许有空格,等号后可能的空格被认为是默认值本身的一部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*** Keywords ***
One Argument With Default Value
[Arguments] ${arg}=default value
[Documentation] This keyword takes 0-1 arguments
Log Got argument ${arg}

Two Arguments With Defaults
[Arguments] ${arg1}=default 1 ${arg2}=${VARIABLE}
[Documentation] This keyword takes 0-2 arguments
Log 1st argument ${arg1}
Log 2nd argument ${arg2}

One Required And One With Default
[Arguments] ${required} ${optional}=default
[Documentation] This keyword takes 1-2 arguments
Log Required: ${required}
Log Optional: ${optional}

Default Based On Earlier Argument
[Arguments] ${a} ${b}=${a} ${c}=${a} and ${b}
Should Be Equal ${a} ${b}
Should Be Equal ${c} ${a} and ${b}

当一个关键字接受几个带有默认值的参数,只有其中一些需要被覆盖时,通常使用命名参数语法是方便的。当这种语法用于用户关键字时,参数被指定,而不使用${}装饰。例如,上面的第二个关键字可以像下面这样使用,${arg1}仍然会得到它的默认值。

1
2
3
*** Test Cases ***
Example
Two Arguments With Defaults arg2=new value

所有的Pythonistas都已经注意到,指定默认参数的语法大量借鉴了Python函数默认值的语法。

用户关键字的可变数量参数

有时候,即使有默认值也不够,需要一个关键字接受可变数量的参数。用户关键字也支持这个特性。所有需要的就是在关键字签名中可能的位置参数之后有一个列表变量,如@{varargs}。这个语法可以与前面描述的默认值结合使用,最后,列表变量得到所有剩余的参数,这些参数不匹配其他参数。因此,列表变量可以有任何数量的项,甚至是零。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** Keywords ***
Any Number Of Arguments
[Arguments] @{varargs}
Log Many @{varargs}

One Or More Arguments
[Arguments] ${required} @{rest}
Log Many ${required} @{rest}

Required, Default, Varargs
[Arguments] ${req} ${opt}=42 @{others}
Log Required: ${req}
Log Optional: ${opt}
Log Others:
FOR ${item} IN @{others}
Log ${item}
END

注意

如果上面的最后一个关键字用多于一个的参数使用,第二个参数${opt}总是得到给定的值,而不是默认值。即使给定的值是空的,也会发生这样的情况。最后一个例子也说明了用户关键字接受的可变数量的参数如何在for循环中使用。这种两个相当高级的函数的组合有时候可能非常有用。

上面的例子中的关键字可以像这样使用:

1
2
3
4
5
6
7
8
9
10
*** Test Cases ***
Varargs with user keywords
Any Number Of Arguments
Any Number Of Arguments arg
Any Number Of Arguments arg1 arg2 arg3 arg4
One Or More Arguments required
One Or More Arguments arg1 arg2 arg3 arg4
Required, Default, Varargs required
Required, Default, Varargs required optional
Required, Default, Varargs arg1 arg2 arg3 arg4 arg5

再次,Pythonistas可能注意到,可变数量的参数语法非常接近Python中的语法。

用户关键字的自由命名参数

用户关键字也可以通过在关键字签名中将字典变量(如&{named})作为绝对最后的参数来接受自由命名的参数。当调用关键字时,这个变量将得到所有不匹配关键字签名中的任何位置参数或仅命名参数的命名参数。

1
2
3
4
5
6
7
8
9
10
11
12
*** Keywords ***
Free Named Only
[Arguments] &{named}
Log Many &{named}

Positional And Free Named
[Arguments] ${required} &{extra}
Log Many ${required} &{extra}

Run Program
[Arguments] @{args} &{config}
Run Process program.py @{args} &{config}

上面的最后一个例子显示了如何创建一个包装关键字,它接受任何位置或命名参数,并将它们传递。参见自由命名参数示例以获取同一关键字的完整示例。

用户关键字的自由命名参数支持与Python中的kwargs工作方式类似。在签名中,以及在传递参数时,&{kwargs}与Python的**kwargs几乎相同。

用户关键字的仅命名参数

从Robot Framework 3.1开始,用户关键字支持仅命名参数,这些参数受到Python 3关键字仅参数的启发。这种语法通常是在可变数量的参数(@{varargs})之后有正常的参数。如果关键字不使用varargs,可以只使用@{}来表示后续的参数是仅命名的:

1
2
3
4
5
6
7
8
*** Keywords ***
With Varargs
[Arguments] @{varargs} ${named}
Log Many @{varargs} ${named}

Without Varargs
[Arguments] @{} ${first} ${second}
Log Many ${first} ${second}

仅命名参数可以与位置参数以及自由命名参数一起使用。当使用自由命名参数时,它们必须是最后的:

1
2
3
4
5
6
7
8
*** Keywords ***
With Positional
[Arguments] ${positional} @{} ${named}
Log Many ${positional} ${named}

With Free Named
[Arguments] @{varargs} ${named only} &{free named}
Log Many @{varargs} ${named only} &{free named}

当向关键字传递仅命名参数时,它们的顺序并不重要,只要它们遵循可能的位置参数。例如,上面的关键字可以像这样使用:

1
2
3
4
5
6
7
8
9
10
*** Test Cases ***
Example
With Varargs named=value
With Varargs positional second positional named=foobar
Without Varargs first=1 second=2
Without Varargs second=toka first=eka
With Positional foo named=bar
With Positional named=2 positional=1
With Free Named positional named only=value x=1 y=2
With Free Named foo=a bar=b named only=c quux=d

仅命名参数可以有默认值,与正常的用户关键字参数类似。一个小的区别是,有和没有默认值的参数的顺序并不重要。

1
2
3
4
5
6
7
8
*** Keywords ***
With Default
[Arguments] @{} ${named}=default
Log Many ${named}

With And Without Defaults
[Arguments] @{} ${optional}=default ${mandatory} ${mandatory 2} ${optional 2}=default 2 ${mandatory 3}
Log Many ${optional} ${mandatory} ${mandatory 2} ${optional 2} ${mandatory 3}

将参数嵌入到关键字名称中

上一节解释了如何将参数传递给关键字,使它们在关键字名称后单独列出。Robot Framework还有另一种传递参数的方法,即直接将参数嵌入到关键字名称中,如下面的第二个测试所使用:

1
2
3
4
5
6
*** Test Cases ***
Normal arguments
Select from list cat

Embedded arguments
Select cat from list

如上例所示,将参数嵌入到关键字名称中可以使数据更易于阅读和理解,即使对没有任何Robot Framework经验的人也是如此。

基本语法

上一个例子展示了如何使用关键字Select cat from list比使用Select from listcat作为参数传递给它更流畅。显然可以将Select cat from list实现为一个正常的关键字,不接受任何参数,但是然后需要为其他动物实现各种其他关键字,如Select dog from list。嵌入式参数简化了这个问题,可以代替实现只有一个关键字,名称为Select ${animal} from list,并用任何动物使用它:

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Embedded arguments
Select cat from list
Select dog from list

*** Keywords ***
Select ${animal} from list
Open Page Pet Selection
Select Item From List animal_list ${animal}

如上例所示,嵌入式参数只是通过在关键字名称中使用变量来指定。在名称中使用的参数自然地在关键字内部可用,并且它们的值取决于如何调用关键字。在上面的例子中,当关键字第一次使用时,${animal}的值是cat,当它第二次使用时,值是dog

从Robot Framework 6.1开始,可以创建接受嵌入式和”正常”参数的用户关键字:

1
2
3
4
5
6
7
8
9
10
11
*** Test Cases ***
Embedded and normal arguments
Number of cats should be 2
Number of dogs should be count=3

*** Keywords ***
Number of ${animals} should be
[Arguments] ${count}
Open Page Pet Selection
Select Items From List animal_list ${animals}
Number of Selected List Items Should Be ${count}

除了特殊的名称,嵌入式参数的关键字就像其他用户关键字一样创建。它们也像其他关键字一样使用,除了在匹配关键字时,它们的名称中不忽略空格和下划线。然而,它们像其他关键字一样不区分大小写。例如,关键字Select ${animal} from list可以像select cow from list那样使用,但不能像Select cow fromlist那样使用。

嵌入式参数不支持像正常参数那样的默认值或可变数量的参数。如果需要这样的功能,应该使用正常参数。可以将嵌入式参数作为变量传递,但这可能会降低可读性:

1
2
3
4
5
6
7
8
9
10
11
*** Variables ***
${SELECT} cat

*** Test Cases ***
Embedded arguments with variable
Select ${SELECT} from list

*** Keywords ***
Select ${animal} from list
Open Page Pet Selection
Select Item From List animal_list ${animal}
嵌入参数匹配错误的值

使用嵌入参数的一个棘手部分是确保在调用关键字时使用的值匹配正确的参数。这是一个问题,特别是如果有多个参数,并且分隔它们的字符也可能出现在给定的值中。例如,在下面的例子中,Select Los Angeles Lakers匹配Select ${city} ${team},使得${city}包含Los${team}包含Angeles Lakers

1
2
3
4
5
6
7
8
*** Test Cases ***
Example
Select Chicago Bulls
Select Los Angeles Lakers

*** Keywords ***
Select ${city} ${team}
Log Selected ${team} from ${city}.

解决这个问题的一个简单方法是用双引号或其他实际值中未使用的字符包围参数。这个修复的例子工作得很好,城市和团队匹配正确:

1
2
3
4
5
6
7
8
*** Test Cases ***
Example
Select "Chicago" "Bulls"
Select "Los Angeles" "Lakers"

*** Keywords ***
Select "${city}" "${team}"
Log Selected ${team} from ${city}.

这种方法不能解决所有的冲突,但它在常见的情况下有所帮助,通常推荐使用。另一个好处是,它使参数从关键字的其余部分突出出来。

当创建忽略Behavior Driven Development (BDD)中通常使用的given/when/then/and/but前缀的关键字时,参数匹配过多的问题经常出现。例如,${name} goes home匹配Given Janne goes home,使得${name}得到Given Janne的值。像"${name}" goes home这样的参数周围的引号,可以轻松解决这个问题。

限制参数匹配什么值的另一种解决方案是使用自定义正则表达式。

解决冲突

当使用嵌入参数时,很常见的一种情况是有多个关键字实现与使用的关键字匹配。例如,在下面的例子中,Execute "ls" with "lf"匹配了两个关键字。它匹配Execute "${cmd}" with "${opts}"是非常明显的,也是想要的,但它也匹配Execute "${cmd}",使得${cmd}匹配ls" with "-lh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Settings ***
Library Process

*** Test Cases ***
Automatic conflict resolution
Execute "ls"
Execute "ls" with "-lh"

*** Keywords ***
Execute "${cmd}"
Run Process ${cmd} shell=True

Execute "${cmd}" with "${opts}"
Run Process ${cmd} ${opts} shell=True

当这种冲突发生时,Robot Framework试图自动选择最佳匹配并使用它。在上面的例子中,Execute "${cmd}" with "${opts}"被认为是比更通用的Execute "${cmd}"更好的匹配,因此运行示例就成功了,没有冲突。

并不总是可能找到一个比其他的更好的匹配。例如,下面的第二个测试失败,因为Robot Framework将两个关键字匹配得同样好。这种冲突需要手动解决,要么通过重命名关键字,要么通过使用自定义正则表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
No conflict
Automation framework
Robot uprising

Unresolvable conflict
Robot Framework

*** Keywords ***
${type} Framework
Should Be Equal ${type} Automation

Robot ${action}
Should Be Equal ${action} uprising

接受只有”正常”参数或根本没有参数的关键字被认为比接受嵌入参数的关键字匹配得更好。例如,如果将以下关键字添加到上面的例子中,Robot Framework在后面的测试中使用的关键字匹配它,测试成功:

1
2
3
*** Keywords ***
Robot Framework
No Operation

在查看哪个匹配最好之前,Robot Framework检查是否有一些匹配的关键字在调用者关键字的同一文件中实现。如果有这样的关键字,它们将优先于其他关键字。或者,可以使用库搜索顺序来控制Robot Framework在资源和库中查找关键字的顺序。

注意

如果多个带有嵌入参数的关键字匹配,自动解决冲突是Robot Framework 6.0中的新特性。在早期版本中,可以使用下面解释的自定义正则表达式代替。

使用自定义正则表达式

当调用带有嵌入参数的关键字时,内部使用正则表达式(简称regexps)匹配值。默认的逻辑是,名称中的每个参数都被替换为一个模式.*?,该模式匹配任何字符串,并尽可能少地匹配。这个逻辑通常工作得相当好,但如上所述,有时关键字匹配错误的值,有时存在无法解决的冲突。在这些情况下,解决方案是指定一个自定义的正则表达式,确保关键字只匹配它应该在那个特定上下文中匹配的内容。要能够使用这个特性,并完全理解本节中的例子,需要至少理解正则表达式语法的基础。

自定义嵌入参数的正则表达式在参数的基本名称之后定义,参数和regexp用冒号分隔。例如,应该只匹配数字的参数可以定义为${arg:\d+}

使用自定义正则表达式的示例如下。注意,第一个例子显示了如何解决Select ${city} ${team}不正确匹配Select Los Angeles Lakers的早期问题,而不需要引用。这是通过实现关键字来实现的,使得${team}只能包含非空白字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
*** Settings ***
Library DateTime

*** Test Cases ***
Do not match whitespace characters
Select Chicago Bulls
Select Los Angeles Lakers

Match numbers and characters from set
1 + 2 = 3
53 - 11 = 42

Match either date or literal 'today'
Deadline is 2022-09-21
Deadline is today

*** Keywords ***
Select ${city} ${team:\S+}
Log Selected ${team} from ${city}.

${number1:\d+} ${operator:[+-]} ${number2:\d+} = ${expected:\d+}
${result} = Evaluate ${number1} ${operator} ${number2}
Should Be Equal As Integers ${result} ${expected}

Deadline is ${date:(\d{4}-\d{2}-\d{2}|today)}
IF '${date}' == 'today'
${date} = Get Current Date
ELSE
${date} = Convert Date ${date}
END
Log Deadline is on ${date}.
支持的正则表达式语法

由于是用Python实现的,Robot Framework自然使用Python的re模块,它有相当标准的正则表达式语法。这种语法在其他方面都得到了嵌入参数的完全支持,但不能使用格式为(?...)的regexp扩展。如果正则表达式语法无效,创建关键字会失败,错误可在测试执行错误中看到。

转义特殊字符

正则表达式大量使用反斜杠字符(\),既用于形成特殊序列(例如\d),也用于转义在regexps中有特殊含义的字符(例如\$)。通常在Robot Framework数据中,反斜杠字符需要用另一个反斜杠转义,但在这个上下文中不需要。如果需要在模式中有一个字面反斜杠,那么反斜杠必须像${path:c:\\temp\\.*}那样被转义。

在模式中可能的孤立的开放和关闭的大括号必须像${open:\{}${close:\}}那样被转义,否则Robot Framework无法正确解析变量语法。如果有匹配的大括号,像${digits:\d{2}}那样,不需要转义。

注意

在Robot Framework 3.2之前,必须像${digits:\d{2\}}那样在模式中转义所有的关闭大括号。这种语法不幸的是不被Robot Framework 3.2或更高版本支持,使用它的关键字必须在升级时更新。

注意

在Robot Framework 6.0之前,在模式中使用字面反斜杠需要像${path:c:\\\\temp\\\\.*}那样将它们双重转义。使用字面反斜杠的模式需要在升级时更新。

使用自定义嵌入参数的正则表达式

当使用自定义正则表达式的嵌入参数时,Robot Framework会自动增强指定的正则表达式,使它们匹配变量以及匹配模式的文本。例如,使用前面例子中的关键字,下面的测试用例将通过。

1
2
3
4
5
6
7
*** Variables ***
${DATE} 2011-06-27

*** Test Cases ***
Example
Deadline is ${DATE}
${1} + ${2} = ${3}

使用变量的一个限制是,它们的实际值不会与自定义正则表达式进行匹配。结果是,关键字可能会被调用,其值可能不允许其自定义正则表达式。从Robot Framework 6.0开始,这种行为已被弃用,未来将对值进行验证。有关更多信息,请参阅问题#4462。

行为驱动开发示例

将参数作为关键字名称的一部分的一个大的好处是,当使用行为驱动风格编写测试时,它使得使用更高级别的类似句子的关键字更容易。如下面的例子所示,这种支持通常与在关键字定义中省略Given、When和Then前缀的可能性结合使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
*** Test Cases ***
Add two numbers
Given I have Calculator open
When I add 2 and 40
Then result should be 42

Add negative numbers
Given I have Calculator open
When I add 1 and -2
Then result should be -1

*** Keywords ***
I have ${program} open
Start Program ${program}

I add ${number 1} and ${number 2}
Input Number ${number 1}
Push Button +
Input Number ${number 2}
Push Button =

Result should be ${expected}
${result} = Get Result
Should Be Equal ${result} ${expected}

注意

Robot Framework中的嵌入参数特性受到了流行的BDD工具Cucumber中创建步骤定义的启发。

用户关键字返回值

与库关键字一样,用户关键字也可以返回值。当使用Robot Framework 5.0或更高版本时,推荐的方法是使用原生的RETURN语句。旧的[Return]设置在Robot Framework 7.0中被弃用,同时BuiltIn关键字Return From Keyword和Return From Keyword If也被认为是弃用的。

无论如何返回值,它们都可以在测试用例和其他用户关键字中分配给变量。

使用RETURN语句

返回值的推荐方法是使用RETURN语句。它接受可选的返回值,并可以与IF和内联IF结构一起使用。它的用法最容易通过例子来解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
*** Keywords ***
Return One Value
[Arguments] ${arg}
[Documentation] Return a value unconditionally.
... Notice that keywords after RETURN are not executed.
${value} = Convert To Upper Case ${arg}
RETURN ${value}
Fail Not executed

Return Three Values
[Documentation] Return multiple values.
RETURN a b c

Conditional Return
[Arguments] ${arg}
[Documentation] Return conditionally.
Log Before
IF ${arg} == 1
Log Returning!
RETURN
END
Log After

Find Index
[Arguments] ${test} ${items}
[Documentation] Advanced example involving FOR loop, inline IF and @{list} variable syntax.
FOR ${index} ${item} IN ENUMERATE @{items}
IF $item == $test RETURN ${index}
END
RETURN ${-1}

如果想自己测试上面的例子,可以使用这些测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*** Settings ***
Library String

*** Test Cases ***
One return value
${ret} = Return One Value argument
Should Be Equal ${ret} ARGUMENT

Multiple return values
${a} ${b} ${c} = Return Three Values
Should Be Equal ${a}, ${b}, ${c} a, b, c

Conditional return
Conditional Return 1
Conditional Return 2

Advanced
@{list} = Create List foo bar baz
${index} = Find Index bar ${list}
Should Be Equal ${index} ${1}
${index} = Find Index non existing ${list}
Should Be Equal ${index} ${-1}

注意

RETURN语法是区分大小写的,就像IF和FOR一样。

注意

RETURN是Robot Framework 5.0中的新特性。如果需要支持旧版本,请使用下面解释的方法。

使用[Return]设置

[Return]设置定义了关键字在执行后应返回什么。虽然推荐将它放在关键字的末尾,这在逻辑上是合理的,但它的位置并不影响它的使用方式。

[Return]设置的一个固有限制是它不能被条件性地使用。因此,只有前两个RETURN语句的例子可以使用它来创建。

1
2
3
4
5
6
7
8
*** Keywords ***
Return One Value
[Arguments] ${arg}
${value} = Convert To Upper Case ${arg}
[Return] ${value}

Return Three Values
[Return] a b c

注意

在Robot Framework 7.0中,[Return]设置被弃用,应该使用RETURN语句。如果需要支持不支持RETURN的旧版本的Robot Framework,可以使用下一节讨论的特殊关键字。

使用特殊关键字返回

BuiltIn关键字Return From Keyword和Return From Keyword If允许在关键字的中间条件性地从用户关键字返回。它们两者也接受可选的返回值,这些返回值的处理方式与上面讨论的RETURN语句和[Return]设置完全相同。

引入RETURN语句使这些关键字变得多余。下面的例子包含了与早期RETURN例子相同的关键字,但这些关键字更冗长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
*** Keywords ***
Return One Value
[Arguments] ${arg}
${value} = Convert To Upper Case ${arg}
Return From Keyword ${value}
Fail Not executed

Return Three Values
Return From Keyword a b c

Conditional Return
[Arguments] ${arg}
Log Before
IF ${arg} == 1
Log Returning!
Return From Keyword
END
Log After

Find Index
[Arguments] ${test} ${items}
FOR ${index} ${item} IN ENUMERATE @{items}
Return From Keyword If $item == $test ${index}
END
Return From Keyword ${-1}

注意

这些关键字实际上已经被弃用,除非需要支持Robot Framework 5.0之前的版本,否则应该使用RETURN语句。使用这些关键字时还没有可见的弃用警告,但它们将在未来被大声地弃用,并最终被移除。

用户关键字设置和拆卸

用户关键字可以有一个设置和一个拆卸,就像测试一样。它们分别使用[Setup]和[Teardown]设置直接指定给拥有它们的关键字。与测试不同,无法为某个文件中的所有关键字指定一个公共的设置或拆卸。

设置和拆卸总是一个单独的关键字,但它们自己可以是执行多个关键字的用户关键字。可以将它们指定为变量,使用特殊的NONE值(不区分大小写)与根本没有设置或拆卸是一样的。

用户关键字设置与创建的用户关键字中的第一个关键字没有太大的不同。唯一的功能性差异是,设置可以被指定为一个变量,但能够明确地标记一个关键字为设置也可能是有用的。

用户关键字的拆卸,就像测试的拆卸一样,即使用户关键字失败也会被执行。因此,当需要在关键字结束时做一些事情,无论其状态如何,它们都非常有用。为了确保所有的清理活动都被完成,与测试拆卸一样,用户关键字拆卸默认启用了失败后继续模式。

1
2
3
4
5
6
7
8
9
10
*** Keywords ***
Setup and teardown
[Setup] Log New in RF 7!
Do Something
[Teardown] Log Old feature.

Using variables
[Setup] ${SETUP}
Do Something
[Teardown] ${TEARDOWN}

注意

用户关键字设置是Robot Framework 7.0中的新特性。

私有用户关键字

用户关键字可以被标记为一个特殊的robot:private标签,以表示它们只应该在创建它们的文件中使用:

1
2
3
4
5
6
7
*** Keywords ***
Public Keyword
Private Keyword

Private Keyword
[Tags] robot:private
No Operation

使用robot:private标签并不直接阻止在创建它的文件之外使用关键字,但这样的用法会引发警告。如果有一个公共的和一个私有的具有相同名称的关键字,将使用公共的,但这种情况也会引发警告。

私有关键字包含在Libdoc创建的规格文件中,但不包含在其HTML输出文件中。

注意

私有用户关键字是Robot Framework 6.0中的新特性。

资源和变量文件

套件文件和套件初始化文件中的用户关键字和变量只能在创建它们的文件中使用,但资源文件提供了共享它们的机制。创建资源文件的高级语法与创建套件文件完全相同,支持的文件格式也相同。主要的区别是资源文件不能有测试。

变量文件提供了一个强大的机制来创建和共享变量。例如,它们允许除字符串之外的值,并启用动态创建变量。它们的灵活性来自于它们是使用Python或YAML创建的,这也使它们比变量部分稍微复杂一些。

资源文件

资源文件通常使用纯文本格式创建,但也支持reStructuredText格式和JSON格式。

使用资源文件

资源文件是使用Settings部分的Resource设置导入的,以便将资源文件的路径作为设置的参数给出。资源文件的推荐扩展名是.resource。出于向后兼容性的原因,.robot、.txt和.tsv也可以工作,但在未来可能会强制使用.resource。

如果资源文件路径是绝对的,那么直接使用它。否则,首先相对于导入文件所在的目录搜索资源文件。如果在那里没有找到文件,那么就从Python的模块搜索路径中的目录中搜索。从模块搜索路径搜索资源文件使得可以将它们打包到Python包中作为包数据,并像package/example.resource那样导入它们。

资源文件路径可以包含变量,建议使用它们来使路径系统无关(例如,{RESOURCES}/login.resource或者只是RESOURCES/login.resource或者只是{RESOURCE_PATH})。此外,路径中的正斜杠(/)在Windows上自动改为反斜杠(\)。

1
2
3
4
5
*** Settings ***
Resource example.resource
Resource ../resources/login.resource
Resource package/example.resource
Resource ${RESOURCES}/common.resource

在资源文件中定义的用户关键字和变量在使用该资源文件的文件中可用。同样可用的还有该资源文件导入的所有库、资源文件和变量文件中的所有关键字和变量。

注意

.resource扩展名是Robot Framework 3.1中的新特性。

资源文件结构

资源文件的高级结构与套件文件的结构相同,但它们不能包含测试或任务。此外,资源文件中的Setting部分只能包含导入(Library、Resource、Variables)、Documentation和Keyword Tags。Variable部分和Keyword部分的使用方式与套件文件完全相同。

如果几个资源文件有一个用户关键字具有相同的名称,它们必须被使用,以便关键字名称前缀为资源文件名称而不带扩展名(例如,myresources.Some Keyword和common.Some Keyword)。此外,如果几个资源文件包含相同的变量,首先导入的那个将被使用。

记录资源文件

在资源文件中创建的关键字可以使用[Documentation]设置进行记录。资源文件本身可以在Setting部分有Documentation,就像套件一样。

Libdoc和各种编辑器使用这些文档,它们自然地对任何打开资源文件的人都可用。当运行关键字时,关键字的文档的第一个逻辑行,直到第一个空行,被记录下来,但在测试执行期间,资源文件文档被忽略。

示例资源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
*** Settings ***
Documentation An example resource file
Library SeleniumLibrary
Resource ${RESOURCES}/common.resource

*** Variables ***
${HOST} localhost:7272
${LOGIN URL} http://${HOST}/
${WELCOME URL} http://${HOST}/welcome.html
${BROWSER} Firefox

*** Keywords ***
Open Login Page
[Documentation] Opens browser to login page
Open Browser ${LOGIN URL} ${BROWSER}
Title Should Be Login Page

Input Name
[Arguments] ${name}
Input Text username_field ${name}

Input Password
[Arguments] ${password}
Input Text password_field ${password}
使用reStructured文本格式的资源文件

可以使用套件文件的reStructuredText格式也适用于资源文件。这样的资源文件可以使用.rst或.rest扩展名,它们在其他方面与普通资源文件的导入方式完全相同:

1
2
*** Settings ***
Resource example.rst

当使用reStructuredText格式解析资源文件时,Robot Framework忽略所有包含Robot Framework数据的代码块之外的所有数据,就像解析reStructuredText套件文件一样。例如,下面的资源文件导入OperatingSystem库,定义${MESSAGE}变量并创建My Keyword关键字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Resource file using reStructuredText
------------------------------------

This text is outside code blocks and thus ignored.

.. code:: robotframework

*** Settings ***
Library OperatingSystem

*** Variables ***
${MESSAGE} Hello, world!

Also this text is outside code blocks and ignored. Code blocks not
containing Robot Framework data are ignored as well.

.. code:: robotframework

# Both space and pipe separated formats are supported.

| *** Keywords *** | | |
| My Keyword | [Arguments] | ${path} |
| | Directory Should Exist | ${path} |
使用JSON格式的资源文件

资源文件可以使用JSON创建,就像套件文件一样。这样的JSON资源文件必须使用标准的.json扩展名或自定义的.rsrc扩展名。它们在其他方面与普通资源文件的导入方式完全相同:

1
2
*** Settings ***
Resource example.rsrc

资源文件可以使用ResourceFile.to_json转换为JSON,并使用ResourceFile.from_json重新创建:

1
2
3
4
5
6
7
8
9
10
from robot.running import ResourceFile

# 基于文件系统上的数据创建资源文件。
resource = ResourceFile.from_file_system('example.resource')

# 将JSON数据保存到文件。
resource.to_json('example.rsrc')

# 从JSON数据重新创建资源。
resource = ResourceFile.from_json('example.rsrc')

变量文件

变量文件包含可以在测试数据中使用的变量。变量也可以使用Variable部分创建或从命令行设置,但变量文件允许动态创建它们,并且使创建除字符串之外的其他变量值变得容易。

变量文件通常作为模块实现,有两种不同的方法来创建变量:

从模块直接获取变量

变量被指定为模块属性。在简单的情况下,语法非常简单,不需要真正的编程。例如,MY_VAR = 'my value'创建一个变量${MY_VAR},其值为指定的文本。这种方法的一个限制是它不允许使用参数。

从特殊函数获取变量

变量文件可以有一个特殊的get_variables(或getVariables)方法,该方法返回变量作为映射。因为该方法可以接受参数,所以这种方法非常灵活。

或者,变量文件可以被实现为类,框架将实例化这些类。在这种情况下,也可以将变量创建为属性,或者从get_variables方法动态获取它们。变量文件也可以创建为YAML和JSON。

使用变量文件
设置部分

所有的测试数据文件都可以使用设置部分的Variables设置导入变量文件。变量文件通常使用文件的路径进行导入,就像使用Resource设置导入资源文件一样。与资源文件相似,导入的变量文件的路径被认为是相对于导入文件所在的目录的,如果没有找到,它会从模块搜索路径中的目录中搜索。路径也可以包含变量,斜杠在Windows上被转换为反斜杠。

示例:

1
2
3
4
*** Settings ***
Variables myvariables.py
Variables ../data/variables.py
Variables ${RESOURCES}/common.yaml

从Robot Framework 5.0开始,使用Python实现的变量文件也可以使用模块名称导入,就像库一样。使用这种方法时,模块需要在模块搜索路径中。

示例:

1
2
3
*** Settings ***
Variables myvariables
Variables rootmodule.Variables

如果一个变量文件接受参数,它们被指定为导入的变量文件的路径或名称之后:

1
2
3
*** Settings ***
Variables arguments.py arg1 ${ARG2}
Variables arguments argument

一个变量文件中的所有变量都在导入它的测试数据文件中可用。如果导入了几个变量文件,它们包含一个具有相同名称的变量,最早导入的文件中的那个将被使用。此外,在Variable部分创建的变量和从命令行设置的变量覆盖变量文件中的变量。

命令行

使用变量文件的另一种方式是使用命令行选项--variablefile。变量文件是使用路径或模块名称引用的,就像使用Variables设置导入它们一样。可能的参数用冒号(:)连接到路径:

1
2
3
4
5
6
--variablefile myvariables.py
--variablefile path/variables.py
--variablefile /absolute/path/common.py
--variablefile variablemodule
--variablefile arguments.py:arg1:arg2
--variablefile rootmodule.Variables:arg1:arg2

从命令行使用的变量文件也会从模块搜索路径中搜索,就像在Setting部分导入的变量文件一样。相对路径被认为是相对于开始执行的目录的。

如果一个变量文件被给定为一个绝对的Windows路径,那么驱动器字母后的冒号不被认为是一个分隔符:

1
--variablefile C:\path\variables.py

也可以使用分号(;)作为参数分隔符。这在变量文件参数本身包含冒号的情况下很有用,但在UNIX-like操作系统上需要用引号包围整个值:

1
2
--variablefile C:\path\variables.py;D:\data.xls
--variablefile "myvariables.py;argument:with:colons"

在命令行中使用的变量文件中的变量在所有测试数据文件中都全局可用,就像使用--variable选项设置的单个变量一样。如果使用了--variablefile--variable选项,并且有相同名称的变量,那么使用--variable选项单独设置的那些将优先。

从模块直接获取变量
基本语法

当变量文件被使用时,它们被导入为Python模块,所有不以下划线(_)开头的模块级别属性默认被认为是变量。因为变量名是不区分大小写的,所以既可以使用小写也可以使用大写名称,但一般来说,推荐使用大写字母表示全局变量和属性。

1
2
3
4
5
6
VARIABLE = "An example string"
ANOTHER_VARIABLE = "This is pretty easy!"
INTEGER = 42
STRINGS = ["one", "two", "kolme", "four"]
NUMBERS = [1, INTEGER, 3.14]
MAPPING = {"one": 1, "two": 2, "three": 3}

在上面的例子中,创建了变量${VARIABLE}${ANOTHER VARIABLE}等。前两个变量是字符串,第三个是整数,然后是两个列表,最后的值是一个字典。所有这些变量都可以作为标量变量使用,列表和字典也可以作为列表变量,如@{STRINGS}(在字典的情况下,该变量只包含键),字典也可以作为字典变量,如&{MAPPING}

为了使创建列表变量或字典变量更明确,可以在变量名前加上LIST__DICT__前缀:

1
2
3
4
from collections import OrderedDict

LIST__ANIMALS = ["cat", "dog"]
DICT__FINNISH = OrderedDict([("cat", "kissa"), ("dog", "koira")])

这些前缀不会成为最终变量名的一部分,但它们会使Robot Framework验证值实际上是列表型或字典型。对于字典,实际存储的值也会转换为一个特殊的字典,这个字典也在Variable部分创建字典变量时使用。这些字典的值可以作为属性访问,如${FINNISH.cat}。这些字典也是有序的,但保留源顺序也需要原始字典是有序的。

上面两个例子中的变量也可以使用下面的Variable部分创建。

1
2
3
4
5
6
7
8
9
*** Variables ***
${VARIABLE} An example string
${ANOTHER VARIABLE} This is pretty easy!
${INTEGER} ${42}
@{STRINGS} one two kolme four
@{NUMBERS} ${1} ${INTEGER} ${3.14}
&{MAPPING} one=${1} two=${2} three=${3}
@{ANIMALS} cat dog
&{FINNISH} cat=kissa dog=koira

注意

从变量文件获取的字符串中不替换变量。例如,VAR = "an ${example}"将创建一个变量${VAR},其值为字面字符串an ${example},无论变量${example}是否存在。

使用对象作为值

变量文件中的变量不限于只有字符串或其他基本类型作为值,如Variable部分。相反,它们的变量可以包含任何对象。在下面的例子中,变量${MAPPING}包含一个Python字典,还有两个变量是从同一文件中实现的自定义对象创建的。

1
2
3
4
5
6
7
8
MAPPING = {'one': 1, 'two': 2}

class MyObject:
def __init__(self, name):
self.name = name

OBJ1 = MyObject('John')
OBJ2 = MyObject('Jane')
动态创建变量

因为变量文件是使用真正的编程语言创建的,所以它们可以有动态的逻辑来设置变量。

1
2
3
4
5
6
7
8
9
10
11
import os
import random
import time

USER = os.getlogin() # 当前登录名
RANDOM_INT = random.randint(0, 10) # 范围[0,10]内的随机整数
CURRENT_TIME = time.asctime() # 时间戳,如 'Thu Apr 6 12:45:21 2006'
if time.localtime()[3] > 12:
AFTERNOON = True
else:
AFTERNOON = False

上面的例子使用标准的Python库来设置不同的变量,但可以使用自己的代码来构造值。下面的例子说明了这个概念,但同样地,代码可以从数据库、外部文件甚至从用户那里读取数据。

1
2
3
4
5
6
7
8
9
import math

def get_area(diameter):
radius = diameter / 2
area = math.pi * radius * radius
return area

AREA1 = get_area(1)
AREA2 = get_area(2)
选择要包含的变量

当Robot Framework处理变量文件时,所有不以下划线开始的属性都被认为是变量。这意味着,即使是在变量文件中创建的函数或类,或者从其他地方导入的,也被认为是变量。例如,最后一个例子中除了${AREA1}${AREA2}之外,还包含了变量${math}${get_area}

通常,额外的变量不会引起问题,但它们可能会覆盖一些其他变量,导致难以调试的错误。忽略其他属性的一种可能性是在它们前面加上下划线:

1
2
3
4
5
6
7
8
9
import math as _math

def _get_area(diameter):
radius = diameter / 2.0
area = _math.pi * radius * radius
return area

AREA1 = _get_area(1)
AREA2 = _get_area(2)

如果有大量的其他属性,而不是给它们所有的前缀,通常更容易使用一个特殊的属性__all__,并给它一个要作为变量处理的属性名列表。

1
2
3
4
5
6
7
8
9
10
11
import math

__all__ = ['AREA1', 'AREA2']

def get_area(diameter):
radius = diameter / 2.0
area = math.pi * radius * radius
return area

AREA1 = get_area(1)
AREA2 = get_area(2)

注意

__all__属性也是,最初是由Python用来决定在使用from modulename import *语法时要导入哪些属性。

选择实际创建哪些变量的第三个选项是使用下面讨论的特殊get_variables函数。

从特殊函数获取变量

获取变量的另一种方法是在变量文件中有一个特殊的get_variables函数(也可以使用驼峰式语法getVariables)。如果存在这样的函数,Robot Framework会调用它,并期望以Python字典的形式接收变量,其中变量名作为键,变量值作为值。创建的变量可以像直接从模块获取变量一样用作标量、列表和字典,也可以使用LIST__DICT__前缀来使创建列表和字典变量更明确。下面的例子在功能上与直接从模块获取变量的第一个例子完全相同。

1
2
3
4
5
6
7
8
def get_variables():
variables = {"VARIABLE ": "An example string",
"ANOTHER VARIABLE": "This is pretty easy!",
"INTEGER": 42,
"STRINGS": ["one", "two", "kolme", "four"],
"NUMBERS": [1, 42, 3.14],
"MAPPING": {"one": 1, "two": 2, "three": 3}}
return variables

get_variables也可以接受参数,这有助于改变实际创建的变量。函数的参数就像为Python函数设置的任何其他参数一样。当使用变量文件时,参数在变量文件的路径之后指定,在命令行中,它们与路径用冒号或分号分隔。

下面的虚拟例子展示了如何使用变量文件的参数。在一个更现实的例子中,参数可能是一个路径,指向一个要从中读取变量的外部文本文件或数据库。

1
2
3
4
5
6
7
8
9
10
11
variables1 = {'scalar': 'Scalar variable',
'LIST__list': ['List','variable']}
variables2 = {'scalar' : 'Some other value',
'LIST__list': ['Some','other','value'],
'extra': 'variables1 does not have this at all'}

def get_variables(arg):
if arg == 'one':
return variables1
else:
return variables2

从Robot Framework 7.0开始,变量文件的参数支持自动参数转换以及命名参数语法。例如,一个带有get_variables(first: int = 0, second: str = '')的变量文件可以这样导入:

1
2
3
*** Settings ***
Variables example.py 42 # 转换为整数。
Variables example.py second=value # 命名参数语法。
将变量文件实现为类

也可以将变量文件实现为类。

实现

因为变量文件总是使用文件系统路径导入,所以类必须和它所在的模块有相同的名称。

框架将使用无参数创建类的实例,并从实例中获取变量。与模块一样,变量可以直接在实例中定义为属性,或者从特殊的get_variables方法中获取。

当变量直接在实例中定义时,所有包含可调用值的属性都被忽略,以避免从可能的方法中创建变量。如果实际上需要可调用的变量,需要使用其他方法来创建变量文件。

示例

第一个示例从属性创建变量。它从类属性创建变量${VARIABLE}@{LIST},从实例属性创建${ANOTHER VARIABLE}

1
2
3
4
5
6
7
class StaticExample:
variable = 'value'
LIST__list = [1, 2, 3]
_not_variable = 'starts with an underscore'

def __init__(self):
self.another_variable = 'another value'

第二个示例利用动态方法获取变量。它只创建一个变量${DYNAMIC VARIABLE}

1
2
3
4
class DynamicExample:

def get_variables(self, *args):
return {'dynamic variable': ' '.join(args)}
变量文件作为YAML

变量文件也可以实现为YAML文件。YAML是一种数据序列化语言,具有简单且对人类友好的语法,但对机器来说解析起来仍然很容易。下面的例子演示了一个简单的YAML文件:

1
2
3
4
5
6
7
8
9
string:   Hello, world!
integer: 42
list:
- one
- two
dict:
one: yksi
two: kaksi
with spaces: kolme

YAML变量文件可以像普通的变量文件一样从命令行使用--variablefile选项、在Settings部分使用Variables设置,以及动态地使用Import Variables关键字。它们会被自动识别为其扩展名,必须是.yaml.yml。如果导入了上面的YAML文件,它将创建与此Variable部分完全相同的变量:

1
2
3
4
5
*** Variables ***
${STRING} Hello, world!
${INTEGER} ${42}
@{LIST} one two
&{DICT} one=yksi two=kaksi with spaces=kolme

用作变量文件的YAML文件必须始终在顶层是映射。如上例所示,映射中的键和值分别成为变量名和值。变量值可以是YAML语法支持的任何数据类型。如果名称或值包含非ASCII字符,YAML变量文件必须是UTF-8编码的。

用作值的映射会自动转换为特殊的字典,这些字典也在Variable部分创建字典变量时使用。最重要的是,这些字典的值可以作为属性访问,如${DICT.one},假设它们的名称作为Python属性名称是有效的。如果名称包含空格或者其他不是有效属性名称的字符,总是可以使用像${DICT}[with spaces]这样的语法访问字典值。

注意

使用Robot Framework的YAML文件需要安装PyYAML模块。通常可以使用pip安装它,像这样:pip install pyyaml

变量文件作为JSON

变量文件也可以实现为JSON文件。与上一节讨论的YAML一样,JSON是一种针对人类和机器的数据序列化格式。它基于JavaScript语法,虽然不如YAML对人类友好,但仍然相对容易理解和修改。下面的例子包含了与早期YAML例子完全相同的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"string": "Hello, world!",
"integer": 42,
"list": [
"one",
"two"
],
"dict": {
"one": "yksi",
"two": "kaksi",
"with spaces": "kolme"
}
}

JSON变量文件会被自动识别为它们的.json扩展名,它们可以像YAML变量文件一样使用。它们对结构、编码等的要求也完全相同。与YAML不同,Python支持JSON,所以不需要安装额外的模块。

注意

对JSON变量文件的支持是Robot Framework 6.1中的新特性。

控制结构

本节描述了可以用来控制测试执行流程的各种结构。这些结构在大多数编程语言中都很常见,它们允许条件执行,重复执行一组关键字和进行精细的错误处理。出于可读性的原因,应谨慎使用这些结构,并且更复杂的用例最好在测试库中实现。

FOR循环

在测试自动化中,重复执行相同的操作是很常见的需求。在Robot Framework中,测试库可以有任何类型的循环结构,大多数时候应该在它们中实现循环。Robot Framework也有自己的FOR循环语法,这在需要重复执行来自不同库的关键字时非常有用。

FOR循环可以用于测试用例和用户关键字。除了非常简单的情况外,用户关键字更好,因为它们隐藏了FOR循环引入的复杂性。基本的FOR循环语法,FOR item IN sequence,源自Python,但其他各种编程语言也支持类似的语法。

简单的FOR循环

在普通的FOR循环中,一个变量根据一组值被赋值,每次迭代一个值。语法以FOR(区分大小写)作为标记开始,然后是循环变量,然后是一个必须的IN(区分大小写)作为分隔符,最后是要迭代的值。这些值可以包含变量,包括列表变量。

在FOR循环中使用的关键字在以下的行中,循环以END(区分大小写)在其自己的行中结束。循环内的关键字不需要缩进,但强烈建议缩进,以使语法更易于阅读。

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Test Cases ***
Example
FOR ${animal} IN cat dog
Log ${animal}
Log 2nd keyword
END
Log Outside loop

Second Example
FOR ${var} IN one two ${3} four ${five}
... kuusi 7 eight nine ${last}
Log ${var}
END

上面的Example中的FOR循环执行了两次,所以首先循环变量${animal}的值是cat,然后是dog。循环由两个Log关键字组成。在第二个例子中,循环值被分成两行,循环总共运行了十次。

使用列表变量的FOR循环通常很方便。下面的例子说明了这一点,其中@{ELEMENTS}包含了一个任意长的元素列表,关键字Start Element被用于逐一使用所有的元素。

1
2
3
4
5
*** Test Cases ***
Example
FOR ${element} IN @{ELEMENTS}
Start Element ${element}
END
旧的FOR循环语法

在Robot Framework 3.1之前,FOR循环的语法与现在不同。开始循环的标记是:FOR而不是FOR,循环内容需要用反斜杠明确标记,而不是使用END标记来结束循环。上面的第一个例子使用旧语法会是这样的:

1
2
3
4
5
6
*** Test Cases ***
Example
:FOR ${animal} IN cat dog
\ Log ${animal}
\ Log 2nd keyword
Log Outside loop

旧语法在Robot Framework 3.2中被弃用,并在Robot Framework 4.0中完全移除。

嵌套FOR循环

从Robot Framework 4.0开始,可以通过在另一个循环内添加循环来简单地使用嵌套FOR循环:

1
2
3
4
5
6
7
8
*** Keywords ***
Handle Table
[Arguments] @{table}
FOR ${row} IN @{table}
FOR ${cell} IN @{row}
Handle Cell ${cell}
END
END

可以有多个嵌套级别,循环也可以与其他控制结构组合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
Multiple nesting levels
FOR ${root} IN r1 r2
FOR ${child} IN c1 c2 c3
FOR ${grandchild} IN g1 g2
Log Many ${root} ${child} ${grandchild}
END
END
FOR ${sibling} IN s1 s2 s3
IF '${sibling}' != 's2'
Log Many ${root} ${sibling}
END
END
END
使用多个循环变量

通过在FOR和IN标记之间使用多个循环变量,可以在一次迭代中遍历多个值。可以有任意数量的循环变量,但值的数量必须能被变量的数量整除。每次迭代消耗的值的数量与变量的数量相同。

如果有很多值需要迭代,通常方便的做法是将它们组织在循环变量下面,如下面的例子中的第一个循环:

1
2
3
4
5
6
7
8
9
10
11
*** Test Cases ***
Multiple loop variables
FOR ${index} ${english} ${finnish} IN
... 1 cat kissa
... 2 dog koira
... 3 horse hevonen
Add Translation ${english} ${finnish} ${index}
END
FOR ${name} ${id} IN @{EMPLOYERS}
Create ${name} ${id}
END
FOR-IN-RANGE循环

前一节中的所有FOR循环都是遍历一个序列。这是最常见的用例,但有时候需要一个循环执行特定次数。为此,Robot Framework有一个特殊的FOR index IN RANGE limit循环语法,它源自Python中使用内置的range()函数的类似习语。

与其他FOR循环一样,FOR-IN-RANGE循环以FOR开始,后面跟着一个循环变量。在这种格式中,只能有一个循环变量,它包含当前的循环索引。在变量之后,必须有IN RANGE标记(区分大小写),后面跟着循环限制。

在最简单的情况下,只指定了循环的上限。在这种情况下,循环索引从零开始,增加一直到,但不包括,限制。也可以给出开始和结束限制。然后索引从开始限制开始,但增加类似于简单情况。最后,也可以给出步长值,指定要使用的增量。如果步长是负的,它被用作递减。

可以使用简单的算术,如加法和减法,与范围限制一起使用。当限制用变量指定时,这特别有用。开始、结束和步长通常给出为整数,但也可以使用浮点值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
*** Test Cases ***
Only upper limit
[Documentation] Loops over values from 0 to 9.
FOR ${index} IN RANGE 10
Log ${index}
END

Start and end
[Documentation] Loops over values from 1 to 10.
FOR ${index} IN RANGE 1 11
Log ${index}
END

Also step given
[Documentation] Loops over values 5, 15, and 25.
FOR ${index} IN RANGE 5 26 10
Log ${index}
END

Negative step
[Documentation] Loops over values 13, 3, and -7.
FOR ${index} IN RANGE 13 -13 -10
Log ${index}
END

Arithmetic
[Documentation] Arithmetic with variable.
FOR ${index} IN RANGE ${var} + 1
Log ${index}
END

Float parameters
[Documentation] Loops over values 3.14, 4.34, and 5.54.
FOR ${index} IN RANGE 3.14 6.09 1.2
Log ${index}
END
FOR-IN-ENUMERATE循环

有时候,遍历列表并跟踪在列表中的位置是很有用的。Robot Framework有一个特殊的FOR index … IN ENUMERATE …语法来处理这种情况。这种语法源自Python内置的enumerate()函数。

FOR-IN-ENUMERATE循环的语法就像常规的FOR循环语法一样,只是变量和值之间的分隔符是IN ENUMERATE(区分大小写)。通常,它们被用来在任何其他循环变量之前有一个额外的索引变量。默认情况下,索引在第一次迭代时的值为0,第二次迭代时的值为1,依此类推。

例如,下面的两个测试用例做了同样的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*** Variables ***
@{LIST} a b c

*** Test Cases ***
Manage index manually
${index} = Set Variable -1
FOR ${item} IN @{LIST}
${index} = Evaluate ${index} + 1
My Keyword ${index} ${item}
END

FOR-IN-ENUMERATE
FOR ${index} ${item} IN ENUMERATE @{LIST}
My Keyword ${index} ${item}
END

从Robot Framework 4.0开始,可以通过在FOR … IN ENUMERATE …头部的最后一项使用start=语法来指定自定义的开始索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Variables ***
@{LIST} a b c
${START} 10

*** Test Cases ***
FOR-IN-ENUMERATE with start
FOR ${index} ${item} IN ENUMERATE @{LIST} start=1
My Keyword ${index} ${item}
END

Start as variable
FOR ${index} ${item} IN ENUMERATE @{LIST} start=${start}
My Keyword ${index} ${item}
END

start=语法必须在FOR头部明确使用,它不能自身来自变量。如果要枚举的最后一个实际项以start=开始,需要像start=这样进行转义。

就像常规的FOR循环一样,只要列表中的值的数量能被循环变量的数量(不包括索引变量)整除,就可以在每次循环迭代中遍历多个值:

1
2
3
4
5
6
7
8
*** Test Cases ***
FOR-IN-ENUMERATE with two values per iteration
FOR ${index} ${en} ${fi} IN ENUMERATE
... cat kissa
... dog koira
... horse hevonen
Log "${en}" in English is "${fi}" in Finnish (index: ${index})
END

如果只使用一个循环变量与FOR-IN-ENUMERATE循环,那个变量将成为一个包含索引和迭代值的Python元组:

1
2
3
4
5
6
*** Test Cases ***
FOR-IN-ENUMERATE with one loop variable
FOR ${x} IN ENUMERATE @{LIST}
Length Should Be ${x} 2
Log Index is ${x}[0] and item is ${x}[1].
END

注意

只使用一个循环变量的FOR-IN-ENUMERATE循环是Robot Framework 3.2中的新特性。

FOR-IN-ZIP循环

有些测试会构建几个相关的列表,然后一起遍历它们。Robot Framework为这种情况提供了一个快捷方式:FOR … IN ZIP …,这是从Python内置的zip()函数派生出来的。

这可能最容易通过一个例子来展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*** Variables ***
@{NUMBERS} ${1} ${2} ${5}
@{NAMES} one two five

*** Test Cases ***
Iterate over two lists manually
${length}= Get Length ${NUMBERS}
FOR ${index} IN RANGE ${length}
Log Many ${NUMBERS}[${index}] ${NAMES}[${index}]
END

FOR-IN-ZIP
FOR ${number} ${name} IN ZIP ${NUMBERS} ${NAMES}
Log Many ${number} ${name}
END

如上面的例子所示,FOR-IN-ZIP循环需要在循环变量和值之间有自己的自定义分隔符IN ZIP(区分大小写)。与FOR-IN-ZIP循环一起使用的值必须是列表或类似列表的对象。

要迭代的项目必须始终以标量变量(如${items})或列表变量(如@{lists})的形式给出,这些变量产生实际的迭代列表。前一种方法更常见,已经在上面演示过。后一种方法的工作方式如下:

1
2
3
4
5
6
7
8
9
10
*** Variables ***
@{NUMBERS} ${1} ${2} ${5}
@{NAMES} one two five
@{LISTS} ${NUMBERS} ${NAMES}

*** Test Cases ***
FOR-IN-ZIP with lists from variable
FOR ${number} ${name} IN ZIP @{LISTS}
Log Many ${number} ${name}
END

要迭代的列表的数量不受限制,但必须与循环变量的数量匹配。或者,可以只有一个循环变量,然后它变成一个Python元组,获取所有列表的项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Variables ***
@{ABC} a b c
@{XYZ} x y z
@{NUM} 1 2 3

*** Test Cases ***
FOR-IN-ZIP with multiple lists
FOR ${a} ${x} ${n} IN ZIP ${ABC} ${XYZ} ${NUM}
Log Many ${a} ${x} ${n}
END

FOR-IN-ZIP with one variable
FOR ${items} IN ZIP ${ABC} ${XYZ} ${NUM}
Length Should Be ${items} 3
Log Many ${items}[0] ${items}[1] ${items}[2]
END

从Robot Framework 6.1开始,可以配置如果迭代的项目的长度不同应该怎么做。默认情况下,最短的项目定义了有多少次迭代,较长的项目的末尾的值被忽略。这可以通过使用mode选项来改变,该选项有三个可能的值:

  • STRICT:项目必须有相等的长度。如果没有,执行失败。这与使用Python的zip函数的strict=True是一样的。
  • SHORTEST:较长的项目中的项目被忽略。只要其中一个项目被耗尽,就支持无限迭代器。这是默认行为。
  • LONGEST:最长的项目定义了有多少次迭代。较短的项目中缺失的值用fill选项指定的值填充,如果没有使用fill选项,则用None填充。这与使用Python的zip_longest函数是一样的,只是它有fillvalue参数而不是fill。

以下示例说明了所有这些模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
*** Variables ***
@{CHARACTERS} a b c d f
@{NUMBERS} 1 2 3

*** Test Cases ***
STRICT mode
[Documentation] 由于列表长度不同,此循环失败。
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=STRICT
Log ${c}: ${n}
END

SHORTEST mode
[Documentation] 此循环执行三次。
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=SHORTEST
Log ${c}: ${n}
END

LONGEST mode
[Documentation] 此循环执行五次。
... 在最后两轮中,`${n}`的值为`None`。
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=LONGEST
Log ${c}: ${n}
END

LONGEST mode with custom fill value
[Documentation] 此循环执行五次。
... 在最后两轮中,`${n}`的值为`0`。
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=LONGEST fill=0
Log ${c}: ${n}
END

注意

如果列表长度不同的行为将在未来改变,以便STRICT模式将成为默认。如果不希望这样,需要显式使用SHORTEST模式。

字典迭代

普通的FOR循环和FOR-IN-ENUMERATE循环支持在字典中迭代键和值。这种语法要求至少有一个循环值是字典变量。可以使用多个字典变量,并以key=value语法给出额外的项。项目按照它们定义的顺序进行迭代,如果同一个键获得多个值,将使用最后的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*** Variables ***
&{DICT} a=1 b=2 c=3

*** Test Cases ***
Dictionary iteration with FOR loop
FOR ${key} ${value} IN &{DICT}
Log Key is '${key}' and value is '${value}'.
END

Dictionary iteration with FOR-IN-ENUMERATE loop
FOR ${index} ${key} ${value} IN ENUMERATE &{DICT}
Log On round ${index} key is '${key}' and value is '${value}'.
END

Multiple dictionaries and extra items in 'key=value' syntax
&{more} = Create Dictionary e=5 f=6
FOR ${key} ${value} IN &{DICT} d=4 &{more} g=7
Log Key is '${key}' and value is '${value}'.
END

通常,最容易使用字典迭代语法,使键和值获得单独的变量,就像上面的例子那样。对于普通的FOR循环,也可以只使用一个变量,该变量将成为一个包含键和值的元组。如果只使用一个变量与FOR-IN-ENUMERATE循环,它将成为一个包含索引、键和值的元组。FOR-IN-ENUMERATE循环中的两个变量意味着将索引赋给第一个变量,并使第二个变量成为一个包含键和值的元组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*** Test Cases ***
One loop variable
FOR ${item} IN &{DICT}
Log Key is '${item}[0]' and value is '${item}[1]'.
END

One loop variable with FOR-IN-ENUMERATE
FOR ${item} IN ENUMERATE &{DICT}
Log On round ${item}[0] key is '${item}[1]' and value is '${item}[2]'.
END

Two loop variables with FOR-IN-ENUMERATE
FOR ${index} ${item} IN ENUMERATE &{DICT}
Log On round ${index} key is '${item}[0]' and value is '${item}[1]'.
END

除了在字典中迭代名称和值,还可以迭代键,然后可能根据键获取值。这种语法需要将字典作为列表变量使用:

1
2
3
4
5
*** Test Cases ***
Iterate over keys
FOR ${key} IN @{DICT}
Log Key is '${key}' and value is '${DICT}[${key}]'.
END

注意

在字典中迭代键和值是Robot Framework 3.2中的新特性。在早期版本中,可以像上面的最后一个例子那样迭代字典键。

移除不必要的关键字输出

具有多次迭代的FOR循环通常会产生大量的输出,并显著增加生成的输出和日志文件的大小。可以使用–removekeywords和–flattenkeywords命令行选项移除或扁平化不必要的关键字。

重复单个关键字

在只需要重复一个关键字的情况下,FOR循环可能过于复杂。在这些情况下,通常更容易使用BuiltIn关键字Repeat Keyword。这个关键字接受一个关键字和重复它的次数作为参数。重复关键字的次数可以有一个可选的后缀times或x,使语法更易读。

1
2
3
4
5
*** Test Cases ***
Example
Repeat Keyword 5 Some Keyword arg1 arg2
Repeat Keyword 42 times My Keyword
Repeat Keyword ${var} Another Keyword argument

WHILE循环

WHILE循环结合了FOR循环和IF/ELSE结构的特性。它们指定一个条件,并在条件保持为真的情况下重复循环体。例如,可以利用这一点来重复一个非确定性序列,直到发生期望的结果,或者在某些情况下,它们可以用作FOR循环的替代品。

注意

WHILE循环是Robot Framework 5.0中的新特性。

基本的WHILE语法
1
2
3
4
5
6
*** Test Cases ***
Example
VAR ${rc} 1
WHILE ${rc} != 0
${rc} = Keyword that returns zero on success
END

WHILE循环的条件在Python中进行评估,因此可以使用Python内置的如len()等函数,并自动导入模块以支持像math.pi * math.pow({radius}, 2) < 10这样的用法。像上面的例子中的radius,2)<10这样的用法。像上面的例子中的{rc}这样的普通变量在评估之前被替换,但变量也可以在评估命名空间中使用特殊的$rc语法。后一种方法在变量的字符串表示不能直接用于条件时很方便。例如,字符串需要引号,多行字符串和包含引号的字符串本身会引起额外的问题。有关评估语法的更多信息和示例,请参见评估表达式附录。

从Robot Framework 6.1开始,WHILE语句中的条件可以省略。这被解释为条件始终为真,这在使用下面描述的limit选项时可能有用。

限制WHILE循环的迭代次数

对于WHILE循环,总是有可能实现一个无限循环,无论是有意的还是无意的。这发生在循环条件永远不变为假的情况下。虽然在应用程序编程中无限循环有一些用途,但在自动化中,无限循环很少是期望的结果。如果在Robot Framework中发生这样的循环,必须强制停止执行,并且不能创建日志或报告。因此,Robot Framework中的WHILE循环默认有10 000次迭代的限制。如果超过了限制,循环就会失败。

可以使用limit配置参数设置限制,要么作为最大迭代次数,要么作为整个循环的最大时间。当限制是迭代次数时,可以只使用像100这样的整数,并在值后面添加times或x后缀,如100 times。当限制是超时时,可以使用像10 s或1 hour 10 minutes这样的时间字符串。也可以通过使用NONE(不区分大小写)来完全禁用限制。下面的示例说明了所有这些选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
*** Test Cases ***
Limit as iteration count
WHILE True limit=100
Log This is run 100 times.
END
WHILE True limit=10 times
Log This is run 10 times.
END
WHILE True limit=42x
Log This is run 42 times.
END

Limit as time
WHILE True limit=10 seconds
Log This is run 10 seconds.
END

No limit
WHILE True limit=NONE
Log This runs forever.
END

注意

在Robot Framework 7.0中,使用times和x后缀与迭代次数是新特性。

如果超过了限制,循环中的关键字不会被强制停止。相反,循环将以类似于循环条件变为假的方式退出。主要的区别是,在这种情况下,循环的状态将是FAIL。

从Robot Framework 6.1开始,可以使用on_limit参数来配置超过限制时的行为。它支持两个值pass和fail,不区分大小写。如果值是pass,当达到限制时,执行将正常继续,WHILE循环的状态将是PASS。fail的值与默认行为类似,例如,如果超过了限制,循环和测试将失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Test Cases ***
Continue when iteration limit is reached
WHILE True limit=5 on_limit=pass
Log Loop will be executed five times
END
Log This will be executed normally.

Continue when time limit is reached
WHILE True limit=10s on_limit=pass
Log Loop will be executed for 10 seconds.
Sleep 0.5s
END
Log This will be executed normally.

默认情况下,当达到限制时,会引发错误消息WHILE loop was aborted because it did not finish within the limit of 0.5 seconds. Use the ‘limit’ argument to increase or remove the limit if needed.。从Robot Framework 6.1开始,可以使用on_limit_message配置参数更改错误消息。

1
2
3
4
5
*** Test Cases ***
Limit as iteration count
WHILE True limit=0.5s on_limit_message=Custom While loop error message
Log This is run 0.5 seconds.
END

注意

on_limit_message配置参数是Robot Framework 6.1中的新特性。

嵌套WHILE循环

WHILE循环可以嵌套,并且可以与其他控制结构组合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
Nesting WHILE
${x} = Set Variable 10
WHILE ${x} > 0
${y} = Set Variable ${x}
WHILE ${y} > 0
${y} = Evaluate ${y} - 1
END
IF ${x} > 5
${x} = Evaluate ${x} - 1
ELSE
${x} = Evaluate ${x} - 2
END
END
移除不必要的关键字输出

具有多次迭代的WHILE循环通常会产生大量的输出,并显著增加生成的输出和日志文件的大小。可以使用–removekeywords和–flattenkeywords命令行选项移除或扁平化不必要的关键字。

使用BREAK和CONTINUE控制循环

FOR循环和WHILE循环的执行都可以用BREAK和CONTINUE语句来控制。前者提前退出整个循环,后者停止执行当前的循环迭代并继续下一个。在实践中,它们与Python、Java和许多其他编程语言中的break和continue语句具有相同的语义。

BREAK和CONTINUE通常在IF/ELSE或TRY/EXCEPT结构中有条件地使用,特别是与它们一起使用的内联IF语法通常很方便。这些语句必须在循环体中使用,可能在上述控制结构内部,并且在循环体中调用的关键字中使用它们是无效的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
*** Test Cases ***
BREAK with FOR
${text} = Set Variable zero
FOR ${var} IN one two three
IF '${var}' == 'two' BREAK
${text} = Set Variable ${text}-${var}
END
Should Be Equal ${text} zero-one

CONTINUE with FOR
${text} = Set Variable zero
FOR ${var} IN one two three
IF '${var}' == 'two' CONTINUE
${text} = Set Variable ${text}-${var}
END
Should Be Equal ${text} zero-one-three

CONTINUE and BREAK with WHILE
WHILE True
TRY
${value} = Do Something
EXCEPT
CONTINUE
END
Do something with value ${value}
BREAK
END

Invalid BREAK usage
[Documentation] BREAK and CONTINUE can only be used in the loop body,
... not in keywords used in the loop.
FOR ${var} IN one two three
Invalid BREAK
END

*** Keywords ***
Invalid BREAK
[Documentation] This keyword fails due to invalid syntax.
BREAK

注意

BREAK和CONTINUE语句是Robot Framework 5.0中的新特性,与WHILE类似。早期版本支持使用BuiltIn关键字Exit For Loop、Exit For Loop If、Continue For Loop和Continue For Loop If来控制FOR循环。这些关键字仍然可以继续工作,但将来将被弃用和删除。

注意

也可以使用RETURN语句来退出循环。它只在循环用于用户关键字内部时才起作用。

IF/ELSE语法

有时候,需要有条件地执行一些关键字。从Robot Framework 4.0开始,有一个单独的IF/ELSE语法,但也有其他方式可以有条件地执行关键字。注意,如果逻辑变得复杂,通常最好将其移动到测试库中。

基本的IF语法

Robot Framework的原生IF语法以IF(区分大小写)开始,并以END(区分大小写)结束。IF标记需要一个值,该值是要评估的条件。如果条件为真,则在IF和END标记之间的自己的行上执行关键字。强烈推荐在IF块中缩进关键字,但这不是强制的。

在下面的例子中,如果${rc}大于零,则执行关键字Some keyword和Another keyword:

1
2
3
4
5
6
*** Test Cases ***
Example
IF ${rc} > 0
Some keyword
Another keyword
END

条件在Python中进行评估,因此可以使用Python内置的如len()等函数,并自动导入模块以支持像platform.system() == ‘Linux’和math.ceil({x}) == 1这样的用法。像上面的例子中的x)==1这样的用法。像上面的例子中的{rc}这样的普通变量在评估之前被替换,但变量也可以在评估命名空间中使用特殊的$rc语法。后一种方法在变量的字符串表示不能直接用于条件时很方便。例如,字符串需要引号,多行字符串和包含引号的字符串本身会引起额外的问题。有关评估语法的更多信息和示例,请参见评估表达式附录。

ELSE分支

像大多数其他支持条件执行的语言一样,Robot Framework的IF语法也支持ELSE分支,如果IF条件不为真,则执行ELSE分支。

在这个例子中,如果${rc}大于零,则执行Some keyword,否则执行Another keyword:

1
2
3
4
5
6
7
*** Test Cases ***
Example
IF ${rc} > 0
Some keyword
ELSE
Another keyword
END
ELSE IF分支

Robot Framework还支持ELSE IF分支,它们有自己的条件,如果初始条件不为真,则评估这些条件。可以有任意数量的ELSE IF分支,它们按照指定的顺序进行。如果其中一个ELSE IF条件为真,则执行其后的块,并忽略剩余的ELSE IF分支。一个可选的ELSE分支可以跟在ELSE IF分支后面,如果所有条件都为假,则执行它。

在下面的例子中,根据${rc}是正数、负数、零还是其他东西(如字符串或None),执行不同的关键字:

1
2
3
4
5
6
7
8
9
10
11
*** Test Cases ***
Example
IF $rc > 0
Positive keyword
ELSE IF $rc < 0
Negative keyword
ELSE IF $rc == 0
Zero keyword
ELSE
Fail Unexpected rc: ${rc}
END

注意

这个例子使用了特殊的rc格式的{rc}变量,以避免在它不是数字的情况下出现评估失败。有关这种语法的更多信息,请参见上述的评估表达式附录。

内联IF

如果需要执行只有一个语句的条件,那么普通的IF/ELSE结构可能有点冗长。它的替代方案是使用内联IF语法,其中要执行的语句直接跟在IF标记和条件后面,不需要END标记。例如,下面的两个关键字是等价的:

1
2
3
4
5
6
7
8
9
10
11
12
*** Keywords ***
Normal IF
IF $condition1
Keyword argument
END
IF $condition2
RETURN
END

Inline IF
IF $condition1 Keyword argument
IF $condition2 RETURN

内联IF语法也支持ELSE和ELSE IF分支:

1
2
3
4
5
6
*** Keywords ***
Inline IF/ELSE
IF $condition Keyword argument ELSE Another Keyword

Inline IF/ELSE IF/ELSE
IF $cond1 Keyword 1 ELSE IF $cond2 Keyword 2 ELSE IF $cond3 Keyword 3 ELSE Keyword 4

如上面的后一个例子所示,带有几个ELSE IF和ELSE分支的内联IF开始变得难以理解。长的内联IF结构可以使用常见的…连续语法分成多行,但使用普通的IF/ELSE结构或将逻辑移动到测试库可能是更好的主意。每个内联IF分支只能包含一个语句。如果需要更多的语句,需要使用普通的IF/ELSE结构。

如果需要用内联IF进行赋值,要赋值的变量或变量必须在开始的IF之前。否则,逻辑与基于关键字返回值赋值变量的逻辑完全相同。如果使用了赋值并且没有运行任何分支,变量将获得None值。

1
2
3
4
5
6
*** Keywords ***
Inline IF/ELSE with assignment
${var} = IF $condition Keyword argument ELSE Another Keyword

Inline IF/ELSE with assignment having multiple variables
${host} ${port} = IF $production Get Production Config ELSE Get Testing Config

注意

内联IF语法是Robot Framework 5.0中的新特性。

嵌套的IF结构

IF结构可以与其他IF结构和FOR循环嵌套。下面的例子使用了高级特性,如FOR-IN-ENUMERATE循环、带有用户关键字的命名只参数和内联Python评估语法 (${{len(${items})}}):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
*** Keywords ***
Log items
[Arguments] @{items} ${log_values}=True
IF not ${items}
Log to console No items.
ELSE IF len(${items}) == 1
IF ${log_values}
Log to console One item: ${items}[0]
ELSE
Log to console One item.
END
ELSE
Log to console ${{len(${items})}} items.
IF ${log_values}
FOR ${index} ${item} IN ENUMERATE @{items} start=1
Log to console Item ${index}: ${item}
END
END
END

*** Test Cases ***
No items
Log items

One item without logging value
Log items xxx log_values=False

Multiple items
Log items a b c
有条件地执行关键字的其他方式

还有其他方法可以有条件地执行关键字:

  • 可以使用变量指定用作套件、测试和关键字的设置或拆卸的关键字的名称。这便于从命令行更改它们。
  • BuiltIn 关键字 Run Keyword 将实际执行的关键字作为参数,因此它可以是一个变量。例如,可以从早期的关键字动态获取变量的值,或者从命令行给出。
  • BuiltIn 关键字 Run Keyword If 和 Run Keyword Unless 分别只在某个表达式为真或假时执行一个命名关键字。一般推荐使用上面解释的新的 IF/ELSE 语法。
  • 另一个 BuiltIn 关键字,Set Variable If,可以根据给定的表达式动态设置变量。
  • 有几个 BuiltIn 关键字允许只在测试用例或测试套件失败或通过时执行一个命名关键字。

TRY/EXCEPT语法

当一个关键字失败时,Robot Framework的默认行为是停止当前的测试并执行可能的清理操作。然而,也可能需要在执行过程中处理这些失败。Robot Framework 5.0引入了原生的TRY/EXCEPT语法来实现这个目的,但也有其他方式来处理错误。

Robot Framework的TRY/EXCEPT语法受到Python的异常处理语法的启发。它有与Python相同的TRY、EXCEPT、ELSE和FINALLY分支,它们的工作方式也大致相同。一个区别是Python使用小写的try、except等,但在Robot Framework中,所有这种语法必须使用大写字母。更大的区别是,在Python中,异常是对象,而在Robot Framework中,处理的是字符串形式的错误消息。

使用EXCEPT捕获异常

基本的TRY/EXCEPT语法可以用来处理基于错误消息的失败:

1
2
3
4
5
6
7
8
*** Test Cases ***
First example
TRY
Some Keyword
EXCEPT Error message
Error Handler Keyword
END
Keyword Outside

在上面的例子中,如果Some Keyword通过,EXCEPT分支不会运行,执行会在TRY/EXCEPT结构之后继续。如果关键字以错误消息Error message(区分大小写)失败,那么执行EXCEPT分支。如果EXCEPT分支成功,执行会在TRY/EXCEPT结构之后继续。如果它失败,测试失败并且剩余的关键字不会被执行。如果Some Keyword以任何其他异常失败,那么该失败不会被处理,测试失败而不执行剩余的关键字。

可以有多个EXCEPT分支。在这种情况下,它们会一个接一个地进行匹配,第一个匹配的分支会被执行。一个EXCEPT也可以有多个消息来匹配,如果其中任何一个消息匹配,那么这样的分支会被执行。在所有这些情况下,消息可以使用变量来指定,除了字面字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
*** Test Cases ***
Multiple EXCEPT branches
TRY
Some Keyword
EXCEPT Error message # Try matching this first.
Error Handler 1
EXCEPT Another error # Try this if above did not match.
Error Handler 2
EXCEPT ${message} # Last match attempt, this time using a variable.
Error Handler 3
END

Multiple messages with one EXCEPT
TRY
Some Keyword
EXCEPT Error message Another error ${message} # Match any of these.
Error handler
END

也可以有一个没有消息的EXCEPT,在这种情况下,它匹配任何错误。只能有一个这样的EXCEPT,它必须在可能的其他EXCEPT分支之后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Test Cases ***
Match any error
TRY
Some Keyword
EXCEPT # Match any error.
Error Handler
END

Match any after testing more specific errors
TRY
Some Keyword
EXCEPT Error message # Try matching this first
Error Handler 1
EXCEPT # Match any that did not match the above.
Error Handler 2
END

注意

无法捕获由无效语法引起的异常。

使用模式匹配错误

默认情况下,使用EXCEPT匹配错误需要完全匹配。这可以通过将配置选项type=作为except子句的参数来改变。该选项的有效值是GLOB、REGEXP或START(不区分大小写),分别用于进行全局模式匹配、正则表达式匹配或仅匹配错误的开头。使用值LITERAL具有与默认行为相同的效果。如果一个EXCEPT有多个消息,这个选项适用于所有的消息。该选项的值可以用变量来定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
*** Variables ***
${MATCH TYPE} regexp

*** Test Cases ***
Glob pattern
TRY
Some Keyword
EXCEPT ValueError: * type=GLOB
Error Handler 1
EXCEPT [Ee]rror ?? occurred ${pattern} type=glob
Error Handler 2
END

Regular expression
TRY
Some Keyword
EXCEPT ValueError: .* type=${MATCH TYPE}
Error Handler 1
EXCEPT [Ee]rror \\d+ occurred type=Regexp # Backslash needs to be escaped.
Error Handler 2
END

Match start
TRY
Some Keyword
EXCEPT ValueError: ${beginning} type=start
Error Handler
END

Explicit exact match
TRY
Some Keyword
EXCEPT ValueError: invalid literal for int() with base 10: 'ooops' type=LITERAL
Error Handler
EXCEPT Error 13 occurred type=LITERAL
Error Handler 2
END

注意

记住,正则表达式中经常使用的反斜杠字符在Robot Framework数据中是转义字符。因此,在正则表达式中使用它时需要用另一个反斜杠来转义。

捕获错误消息

当使用模式匹配错误,以及在没有任何消息匹配任何错误的情况下使用EXCEPT时,通常需要知道实际发生的错误。Robot Framework通过在EXCEPT语句的末尾添加AS ${var},使得可以将错误消息捕获到一个变量中。

1
2
3
4
5
6
7
8
9
10
11
*** Test Cases ***
Capture error
TRY
Some Keyword
EXCEPT ValueError: * type=GLOB AS ${error}
Error Handler 1 ${error}
EXCEPT [Ee]rror \\d+ (Invalid|Bad) usage type=REGEXP AS ${error}
Error Handler 2 ${error}
EXCEPT AS ${error}
Error Handler 3 ${error}
END
使用ELSE在没有错误时执行关键字

可选的ELSE分支使得在没有错误的情况下可以执行关键字。只能有一个ELSE分支,并且只有在一个或多个EXCEPT分支之后才允许:

1
2
3
4
5
6
7
8
9
10
11
12
*** Test Cases ***
ELSE branch
TRY
Some Keyword
EXCEPT X
Log Error 'X' occurred!
EXCEPT Y
Log Error 'Y' occurred!
ELSE
Log No error occurred!
END
Keyword Outside

在上面的例子中,如果Some Keyword通过,那么执行ELSE分支,如果它以消息X或Y失败,那么运行适当的EXCEPT分支。在所有这些情况下,执行在整个TRY/EXCEPT/ELSE结构之后继续。如果Some Keyword以任何其他方式失败,EXCEPT和ELSE分支不会运行,TRY/EXCEPT/ELSE结构失败。

要处理有任何错误和没有错误的情况,可以使用没有任何消息的EXCEPT与ELSE结合使用:

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Handle everything
TRY
Some Keyword
EXCEPT AS ${err}
Log Error occurred: ${err}
ELSE
Log No error occurred!
END
使用FINALLY无论是否有错误都执行关键字

可选的FINALLY分支使得在有错误和没有错误的情况下都可以执行关键字。因此,它们适合在关键字执行后进行清理,类似于清理操作。只能有一个FINALLY分支,它必须始终在最后。它们可以与EXCEPT和ELSE分支结合使用,也可以使用TRY/FINALLY结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
TRY
Some keyword
EXCEPT
Log Error occurred!
ELSE
Log No error occurred.
FINALLY
Log Always executed.
END

TRY/FINALLY
Open Connection
TRY
Use Connection
FINALLY
Close Connection
END
其他处理错误的方式

还有其他方法可以有条件地执行关键字:

  • BuiltIn关键字Run Keyword And Expect Error执行一个命名的关键字,并期望它以指定的错误消息失败。它基本上与使用指定消息的TRY/EXCEPT相同。指定错误消息的语法也是相同的,除了这个关键字默认使用全局模式匹配,而不是精确匹配。一般推荐使用原生的TRY/EXCEPT功能,除非需要支持不支持它的旧版本的Robot Framework。
  • BuiltIn关键字Run Keyword And Ignore Error执行一个命名的关键字,并返回其状态作为字符串PASS或FAIL,以及可能的返回值或错误消息。它基本上与使用TRY/EXCEPT/ELSE相同,以便EXCEPT捕获所有错误。推荐使用原生语法,除非需要支持旧版本的Robot Framework。
  • BuiltIn关键字Run Keyword And Return Status执行一个命名的关键字,并返回其状态作为布尔值true或false。它是前述的Run Keyword And Ignore Error的包装器。现在推荐使用原生语法。
  • 测试清理和关键字清理可以用于清理活动,类似于FINALLY分支。
  • 当关键字在基于Python的库中实现时,所有Python的错误处理特性都是现成可用的。这是推荐的方法,特别是如果需要的逻辑变得更复杂。

高级特性

处理同名关键字

使用Robot Framework的关键字可以是库关键字或用户关键字。前者来自标准库或外部库,后者要么在使用它们的同一文件中创建,要么从资源文件中导入。当使用许多关键字时,它们中的一些具有相同的名称是很常见的,本节描述了如何处理这些情况下可能的冲突。

关键字作用域

当只使用关键字名称并且有几个具有该名称的关键字时,Robot Framework会尝试根据其作用域确定哪个关键字具有最高优先级。关键字的作用域是根据创建关键字的方式确定的:

  • 在当前执行的套件文件中创建为用户关键字。这些关键字具有最高优先级,即使在其他地方有其他同名关键字,它们也总是被使用。
  • 在资源文件中创建并直接或间接从另一个资源文件导入。这是第二高的优先级。
  • 在外部测试库中创建。如果没有同名的用户关键字,这些关键字将被使用。然而,如果在标准库中有一个同名的关键字,将显示警告。
  • 在标准库中创建。这些关键字具有最低优先级。
明确指定关键字

仅凭作用域并不是一个足够的解决方案,因为在几个库或资源中可能有同名的关键字,因此,它们提供了一种机制,只使用最高优先级的关键字。在这种情况下,可以使用关键字的全名,其中关键字名称以资源或库的名称为前缀,点号作为分隔符。

对于库关键字,长格式只意味着使用LibraryName.Keyword Name格式。例如,可以使用OperatingSystem.Run作为OperatingSystem库中的Run关键字,即使在其他地方有另一个Run关键字。如果库在模块或包中,必须使用完整的模块或包名称(例如,com.company.Library.Some Keyword)。如果在导入库时给库指定了自定义名称,那么在完整的关键字名称中也必须使用指定的名称。

资源文件在完整的关键字名称中指定,类似于库名称。资源的名称是从资源文件的基本名称中派生出来的,不包括文件扩展名。例如,可以使用myresources.Example作为myresources.html资源文件中的Example关键字。注意,如果几个资源文件有相同的基本名称,这种语法将不起作用。在这种情况下,必须重命名文件或关键字。关键字的全名与普通关键字名称一样,不区分大小写、空格和下划线。

明确指定库和资源之间的优先级

如果关键字之间存在多个冲突,指定所有关键字的长格式可能会很麻烦。使用长格式也使得无法创建根据可用的库或资源的不同而工作不同的动态测试用例或用户关键字。解决这两个问题的一个方法是使用BuiltIn库中的关键字Set Library Search Order显式指定关键字优先级。

注意

尽管关键字在其名称中有库这个词,但它也适用于资源文件。如上所述,资源中的关键字总是比库中的关键字具有更高的优先级。

Set Library Search Order接受一个有序的库和资源列表作为参数。当测试数据中的关键字名称与多个关键字匹配时,选择包含关键字的第一个库或资源,并使用该关键字实现。如果在任何指定的库或资源中都找不到关键字,执行失败,冲突的方式与设置搜索顺序时相同。

有关更多信息和示例,请参阅关键字的文档。

超时

有时候,关键字可能需要异常长的时间来执行,或者无限期地挂起。Robot Framework允许为测试用例和用户关键字设置超时,如果一个测试或关键字在指定的时间内没有完成,当前正在执行的关键字将被强制停止。

以这种方式停止关键字可能会使库、测试环境或被测试系统处于不稳定状态,只有在没有更安全的选项可用时,才推荐使用超时。一般来说,库应该实现为关键字不能挂起,或者它们有自己的超时机制。

测试用例超时

测试用例的超时可以通过在设置部分使用Test Timeout设置,或者在单个测试用例中使用[Timeout]设置来设置。Test Timeout为该套件中的所有测试用例定义了默认超时,而[Timeout]应用于特定的测试用例,并覆盖可能的默认值。

使用空的[Timeout]意味着测试没有超时,即使使用了Test Timeout。也可以为此目的使用显式的NONE值。如果其值为零或负数,超时也会被有效地忽略。

无论测试超时是在哪里定义的,给它的值都包含超时的持续时间。持续时间必须以Robot Framework的时间格式给出,即直接以秒为单位,如10,或以1分钟30秒的格式。超时也可以指定为变量,使得可以例如从命令行给出它们。

如果有超时并且它过期,当前正在运行的关键字将被停止,测试用例失败。如果发生测试超时,作为测试清理部分执行的关键字不会被中断,但测试仍然被标记为失败。如果清理中的关键字可能挂起,可以使用用户关键字超时来停止它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
*** Settings ***
Test Timeout 2 minutes

*** Test Cases ***
Default timeout
[Documentation] Default timeout from Settings is used.
Some Keyword argument

Override
[Documentation] Override default, use 10 seconds timeout.
[Timeout] 10
Some Keyword argument

Variables
[Documentation] It is possible to use variables too.
[Timeout] ${TIMEOUT}
Some Keyword argument

No timeout
[Documentation] Empty timeout means no timeout even when Test Timeout has been used.
[Timeout]
Some Keyword argument

No timeout 2
[Documentation] Disabling timeout with NONE works too and is more explicit.
[Timeout] NONE
Some Keyword argument
用户关键字超时

可以使用[Timeout]设置为用户关键字设置超时。语法与测试用例超时完全相同,但用户关键字超时没有任何默认值。如果使用变量指定用户关键字超时,也可以将值作为关键字参数给出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Keywords ***
Hardcoded
[Arguments] ${arg}
[Timeout] 1 minute 42 seconds
Some Keyword ${arg}

Configurable
[Arguments] ${arg} ${timeout}
[Timeout] ${timeout}
Some Keyword ${arg}

Run Keyword with Timeout
[Arguments] ${keyword} @{args} &{kwargs} ${timeout}=1 minute
[Documentation] Wrapper that runs another keyword with a configurable timeout.
[Timeout] ${timeout}
Run Keyword ${keyword} @{args} &{kwargs}

用户关键字超时在该用户关键字的执行期间适用。如果整个关键字的总时间超过超时值,当前正在执行的关键字将被停止。用户关键字超时也适用于测试用例的清理过程,而测试超时则不适用。

如果测试用例和其中的一些关键字(或几个嵌套关键字)都有超时,活动超时是剩余时间最少的那个。

注意

在早期的Robot Framework版本中,可以指定一个自定义错误消息,以便在超时过期时使用。这个功能在Robot Framework 3.0.1中被弃用,并在Robot Framework 3.2中被移除。

并行执行关键字

当需要并行执行时,必须在测试库级别实现,以便库在后台执行代码。通常,这意味着库需要一个像Start Something这样的关键字来开始执行并立即返回,以及另一个像Get Results From Something这样的关键字来等待结果可用并返回它。参见Process库关键字Start Process和Wait For Process的示例。

执行测试用例

基本使用 Robot Framework 的测试用例从命令行执行,其结果默认为 XML 格式的输出文件以及 HTML 报告和日志。执行后,可以使用 Rebot 工具合并输出文件并进行其他后处理。

测试用例执行

基本使用

开始执行测试

概要
1
2
3
robot [options] data
python -m robot [options] data
python path/to/robot/ [options] data

执行通常是使用安装时创建的 robot 命令开始的。或者,也可以使用选定的 Python 解释器执行已安装的 robot 模块。这在 Robot Framework 已在多个 Python 版本下安装的情况下特别方便。最后,如果知道已安装的 robot 目录在哪里,也可以使用 Python 执行它。

无论执行方法如何,要执行的测试数据的路径(或路径)都作为命令后的参数给出。此外,可以使用不同的命令行选项以多种方式更改测试执行或生成的输出。

指定要执行的测试数据

指定要执行的测试数据 Robot Framework 的测试用例在文件和目录中创建,通过将文件或目录的路径给到选定的运行脚本来执行它们。路径可以是绝对的,或者更常见的,相对于从其中执行测试的目录。给定的文件或目录创建顶级测试套件,该套件默认从文件或目录名获取其名称。以下示例中说明了不同的执行可能性。请注意,在这些示例中,以及在本节中的其他示例中,只使用了 robot 脚本,但其他执行方法也可以类似地使用。

1
2
3
robot tests.robot
robot path/to/my_tests/
robot c:\robot\tests.robot

注意

执行目录时,所有以点(.)或下划线(_)开头的文件和目录都会被忽略,且默认只执行 .robot 扩展名的文件。有关更多详细信息,请参阅选择要解析的文件部分。

也可以一次给出多个测试用例文件或目录的路径,用空格分隔。在这种情况下,Robot Framework 会自动创建顶级测试套件,指定的文件和目录成为其子测试套件。创建的测试套件的名称是通过将子套件名称用和号(&)和空格连接在一起得到的。例如,下面第一个示例中的顶级套件的名称是 My Tests & Your Tests。这些自动创建的名称通常很长且复杂。在大多数情况下,因此最好使用 –name 选项来覆盖它,如下面的第二个示例所示:

1
2
robot my_tests.robot your_tests.robot
robot --name Example path/to/tests/pattern_*.robot

从 Robot Framework 6.1 开始,也可以为自动创建的顶级套件定义测试套件初始化文件。给出 init 文件的路径与给出测试用例文件的方式类似:

1
robot __init__.robot my_tests.robot other_tests.robot

使用命令行选项

Robot Framework 提供了许多命令行选项,可以用来控制如何执行测试用例以及生成哪些输出。本节将解释选项语法,以及实际存在的选项。它们如何被使用将在本章的其他部分进行讨论。

使用选项

当使用选项时,它们必须始终在运行脚本和数据源之间给出。例如:

1
2
robot -L debug my_tests.robot
robot --include smoke --variable HOST:10.0.0.42 path/to/tests/
短选项和长选项

选项总是有一个长名称,如 --name,最常用的选项也有一个短名称,如 -N。除此之外,只要它们是唯一的,长选项可以被缩短。例如,--logle DEBUG 是有效的,而 --lo log.html 则不行,因为前者只匹配 --loglevel,但后者匹配了多个选项。在手动执行测试用例时,短选项和缩短的选项是实用的,但在启动脚本中,建议使用长选项,因为它们更容易理解。

长选项名称是不区分大小写和连字符的,这有助于以易于阅读的格式编写选项名称。例如,--SuiteStatLevel--suite-stat-level 等同于,但比 --suitestatlevel 更易于阅读。

注意

Robot Framework 6.1 中新增了长选项不区分连字符。

设置选项值

大多数选项需要一个值,该值在选项名称之后给出。短选项和长选项都接受用空格从选项名称分隔的值,如 --include tag-i tag。对于长选项,分隔符也可以是等号,例如 --include=tag,对于短选项,分隔符可以省略,如 -itag

有些选项可以指定多次。例如,--variable VAR1:value --variable VAR2:another 设置了两个变量。如果多次使用只接受一个值的选项,最后给出的值是有效的。

禁用不接受值的选项

不接受值的选项可以通过再次使用相同的选项并添加或删除无前缀来禁用。无论选项使用多少次,最后的选项都有优先权。例如,--dryrun --dryrun --nodryrun --nostatusrc --statusrc 将不会激活 dry-run 模式,并将返回正常的状态 rc。

简单模式

许多命令行选项接受参数作为简单模式。这些类似于全局的模式根据以下规则进行匹配:

  • * 匹配任何字符串,甚至是空字符串。
  • ? 匹配任何单个字符。
  • [abc] 匹配括号中的一个字符。
  • [!abc] 匹配不在括号中的一个字符。
  • [a-z] 匹配括号中范围内的一个字符。
  • [!a-z] 匹配不在括号中范围内的一个字符。

与通常的全局模式不同,路径分隔符 /\ 以及换行符 \n 由上述通配符匹配。

除非另有说明,模式匹配对大小写、空格和下划线不敏感。

示例:

1
2
3
--test Example*        # 匹配以 'Example' 开头的测试名称。
--test Example[1-2] # 匹配 'Example1' 和 'Example2' 测试。
--include f?? # 匹配以 'f' 开头且长度为三个字符的标签的测试。

上述示例中的所有匹配对大小写、空格和下划线不敏感。例如,第二个示例也会匹配名为 example 1 的测试。

如果匹配的文本恰好包含一些通配符字符,并且需要将它们按字面意义匹配,那么可以通过使用 [...] 语法来实现。模式 [*] 匹配字面 * 字符,[?] 匹配 ?[[] 匹配 [。单独的 [] 不需要转义。

注意

Robot Framework 3.1 中新增了像 [abc][!a-z] 这样的括号支持。

标签模式

大多数与标签相关的选项接受参数作为标签模式。它们支持与简单模式相同的通配符(例如 examp??ex*le),但它们还支持下面解释的 AND、OR 和 NOT 运算符。这些运算符可以用于将两个或更多个独立的标签或模式组合在一起。

AND 或 &

如果所有的单个模式都匹配,那么整个模式就匹配。AND 和 & 是等价的:

1
2
--include fooANDbar     # 匹配包含标签 'foo' 和 'bar' 的测试。
--exclude xx&yy&zz # 匹配包含标签 'xx'、'yy' 和 'zz' 的测试。
OR

如果任何单个模式匹配,那么整个模式就匹配:

1
2
--include fooORbar      # 匹配包含标签 'foo' 或 'bar' 的测试。
--exclude xxORyyORzz # 匹配包含任何标签 'xx'、'yy' 或 'zz' 的测试。
NOT

如果左侧的模式匹配,但右侧的模式不匹配,那么整个模式就匹配。如果多次使用,第一个 NOT 之后的所有模式都不能匹配:

1
2
--include fooNOTbar     # 匹配包含标签 'foo' 但不包含标签 'bar' 的测试。
--exclude xxNOTyyNOTzz # 匹配包含标签 'xx' 但不包含标签 'yy' 或 'zz' 的测试。

模式也可以以 NOT 开始,在这种情况下,如果 NOT 后的模式不匹配,那么模式就匹配:

1
2
--include NOTfoo        # 匹配不包含标签 'foo' 的测试
--include NOTfooANDbar # 匹配不包含标签 'foo' 和 'bar' 的测试

上述运算符也可以一起使用。运算符的优先级,从高到低,是 AND、OR 和 NOT:

1
2
3
--include xANDyORz      # 匹配包含标签 'x' 和 'y',或标签 'z' 的测试。
--include xORyNOTz # 匹配包含标签 'x' 或 'y',但不包含标签 'z' 的测试。
--include xNOTyANDz # 匹配包含标签 'x',但不包含标签 'y' 和 'z' 的测试。

虽然标签匹配本身是不区分大小写的,但所有运算符都是区分大小写的,必须用大写字母写。如果标签本身恰好包含大写的 AND、OR 或 NOT,它们需要使用小写字母指定,以避免意外的运算符使用:

1
2
3
--include port          # 匹配包含标签 'port' 的测试,不区分大小写
--include PORT # 匹配包含标签 'P' 或 'T' 的测试,不区分大小写
--exclude handoverORportNOTnotification
ROBOT_OPTIONS 和 REBOT_OPTIONS 环境变量

环境变量 ROBOT_OPTIONSREBOT_OPTIONS 可以用来分别为测试执行和结果后处理指定默认选项。选项及其值必须定义为一个空格分隔的列表,并且它们被放在命令行上的任何显式选项之前。这些环境变量的主要用途是为某些选项设置全局默认值,以避免每次运行测试或使用 Rebot 时都需要重复它们。

1
2
3
4
export ROBOT_OPTIONS="--outputdir results --tagdoc 'mytag:Example doc with spaces'"
robot tests.robot
export REBOT_OPTIONS="--reportbackground blue:red:yellow"
rebot --name example output.xml

测试结果

命令行输出

测试执行的最直观的输出是在命令行中显示的输出。所有执行的测试套件和测试用例,以及它们的状态,都会实时显示在那里。下面的示例显示了执行一个只有两个测试用例的简单测试套件的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
==============================================================================
示例测试套件
==============================================================================
第一个测试 :: 可能的测试文档 | 通过 |
------------------------------------------------------------------------------
第二个测试 | 失败 |
这里显示错误消息
==============================================================================
示例测试套件 | 失败 |
2个测试,1个通过,1个失败
==============================================================================
输出: /path/to/output.xml
报告: /path/to/report.html
日志: /path/to/log.html

每当测试用例中的顶级关键字结束时,控制台上也会有一个通知。如果关键字通过,则使用绿色的点,如果失败,则使用红色的F。这些标记被写到行的末尾,当测试本身结束时,它们被测试状态覆盖。如果将控制台输出重定向到文件,将禁用写入标记。

生成的输出文件

命令行输出非常有限,通常需要单独的输出文件来调查测试结果。如上面的示例所示,默认情况下会生成三个输出文件。第一个是XML格式的,包含了关于测试执行的所有信息。第二个是更高级别的报告,第三个是更详细的日志文件。这些文件和其他可能的输出文件在不同的输出文件部分中讨论得更详细。

返回代码

运行脚本使用返回代码向运行它们的系统通信总体测试执行状态。当执行成功开始并且没有测试失败时,返回代码为零。下表解释了所有可能的返回代码。

可能的返回代码

RC 解释
0 所有测试通过。
1-249 返回失败的测试数量。
250 250个或更多的失败。
251 打印了帮助或版本信息。
252 无效的测试数据或命令行选项。
253 测试执行被用户停止。
255 意外的内部错误。

返回代码应该在执行后始终容易获得,这使得自动确定总体执行状态变得容易。例如,在bash shell中,返回代码在特殊变量$?中,在Windows中,它在%ERRORLEVEL%变量中。如果使用一些外部工具来运行测试,查阅其文档以了解如何获取返回代码。

即使有失败,也可以使用--NoStatusRC命令行选项将返回代码设置为0。例如,在连续集成服务器中,可能需要在确定测试执行的总体状态之前对结果进行后处理,这可能很有用。

注意

Rebot也使用相同的返回代码。

执行过程中的错误和警告

在测试执行过程中,可能会出现意外的问题,比如无法导入库或资源文件,或者关键字已被弃用。根据严重程度,这些问题被分类为错误或警告,并被写入控制台(使用标准错误流),在日志文件的单独 “Test Execution Errors” 部分中显示,并写入 Robot Framework 自己的系统日志。通常,这些错误和警告是由 Robot Framework 本身生成的,但库也可以记录错误和警告。下面的示例说明了日志文件中的错误和警告是什么样子的。

1
2
20090322 19:58:42.528	ERROR	文件 '/home/robot/tests.robot' 中的 'Setting' 表格的第 2 行元素出错:资源文件 'resource.robot' 不存在
20090322 19:58:43.931 WARN 关键字 'SomeLibrary.Example Keyword' 已被弃用。请使用关键字 `Other Keyword`。

参数文件

参数文件允许将所有或部分命令行选项和参数放置在一个外部文件中进行读取。这避免了命令行中存在的问题字符。如果需要大量的选项或参数,参数文件也可以防止在命令行中使用的命令变得过长。

参数文件通过 --argumentfile(-A)选项以及可能的其他命令行选项进行使用。

注意

与其他长命令行选项不同,--argumentfile 不能以缩短的格式(如 --argumentf)给出。

参数文件语法

参数文件可以包含命令行选项和测试数据的路径,每行一个选项或数据源。支持短选项和长选项,但推荐使用后者,因为它们更容易理解。参数文件可以包含任何字符,无需转义,但行首和行尾的空格会被忽略。此外,空行和以井号(#)开头的行也会被忽略:

1
2
3
4
5
--doc 这是一个示例(其中"特殊字符"是可以的!)
--metadata X:带空格的值
--variable VAR:Hello, world!
# 这是一个注释
path/to/my/tests

在上述示例中,选项和它们的值之间的分隔符是一个空格。可以使用等号(=)或任意数量的空格。例如,以下三行是相同的:

1
2
3
--name An Example
--name=An Example
--name An Example

如果参数文件包含非ASCII字符,它们必须使用UTF-8编码保存。

使用参数文件

参数文件可以单独使用,以便它们包含所有的选项和测试数据的路径,或者与其他选项和路径一起使用。当一个参数文件与其他参数一起使用时,它的内容被放置到原始参数列表的与参数文件选项相同的位置。这意味着参数文件中的选项可以覆盖它之前的选项,它的选项可以被它之后的选项覆盖。可以多次甚至递归地使用 --argumentfile 选项:

1
2
3
4
robot --argumentfile all_arguments.robot
robot --name Example --argumentfile other_options_and_paths.robot
robot --argumentfile default_options.txt --name Example my_tests.robot
robot -A first.txt -A second.txt -A third.txt tests.robot
从标准输入读取参数文件

可以使用特殊的参数文件名 STDIN 从标准输入流而不是文件中读取参数。当使用脚本生成参数时,这可能很有用:

1
2
generate_arguments.sh | robot --argumentfile STDIN
generate_arguments.sh | robot --name Example --argumentfile STDIN tests.robot

获取帮助和版本信息

无论是在执行测试用例还是在后处理输出时,都可以通过 --help(-h)选项获取命令行帮助。这些帮助文本有一个简短的概述,并简要解释了可用的命令行选项。

所有的运行脚本也支持使用 --version 选项获取版本信息。这些信息还包含 Python 版本和平台类型:

1
2
3
4
5
$ robot --version
Robot Framework 7.0 (Python 3.12.1 on darwin)

C:\>rebot --version
Rebot 6.1.1 (Python 3.11.0 on win32)

创建启动脚本

测试用例通常由持续集成系统或其他一些机制自动执行。在这种情况下,需要有一个脚本来启动测试执行,可能还需要以某种方式对输出进行后处理。类似的脚本在手动运行测试时也很有用,特别是当需要大量的命令行选项或设置测试环境比较复杂时。

在类 UNIX 环境中,shell 脚本提供了一种简单但强大的机制来创建自定义的启动脚本。Windows 批处理文件也可以使用,但它们更有限,通常也更复杂。一个平台无关的替代方案是使用 Python 或其他高级编程语言。无论使用哪种语言,都建议使用长选项名称,因为它们比短名称更容易理解。

Shell 脚本示例

在这个示例中,登录目录中的相同网络测试在不同的浏览器上执行,并使用 Rebot 在之后合并结果。该脚本也接受命令行选项本身,并使用方便的 $* 变量简单地将它们转发给 robot 命令:

1
2
3
4
#!/bin/bash
robot --name Firefox --variable BROWSER:Firefox --output out/fx.xml --log none --report none $* login
robot --name IE --variable BROWSER:IE --output out/ie.xml --log none --report none $* login
rebot --name Login --outputdir out --output login.xml out/fx.xml out/ie.xml
批处理文件示例

使用批处理文件实现上述 shell 脚本示例也不是很复杂。注意,批处理文件的参数可以使用 %* 转发给执行的命令:

1
2
3
4
@echo off
robot --name Firefox --variable BROWSER:Firefox --output out\fx.xml --log none --report none %* login
robot --name IE --variable BROWSER:IE --log none --output out\ie.xml --report none %* login
rebot --name Login --outputdir out --output login.xml out\fx.xml out\ie.xml

注意

在 Robot Framework 3.1 之前,robot 和 rebot 命令在 Windows 上是作为批处理文件实现的,而在另一个批处理文件中使用它们需要在整个命令前加上 call。

Python 示例

当启动脚本变得更复杂时,使用 shell 脚本或批处理文件实现它们就不那么方便了。这尤其是在需要两种变体并且需要两次实现相同逻辑的情况下。在这种情况下,通常最好切换到 Python。可以使用 subprocess 模块从 Python 执行 Robot Framework,但通常使用 Robot Framework 自己的程序化 API 更方便。最容易使用的 API 是 robot.run_clirobot.rebot_cli,它们接受与 robot 和 rebot 命令相同的命令行参数。

以下示例实现了与前面的 shell 脚本和批处理文件示例相同的逻辑。在 Python 中,脚本本身的参数可以在 sys.argv 中获取:

1
2
3
4
5
6
7
8
#!/usr/bin/env python
import sys
from robot import run_cli, rebot_cli

common = ['--log', 'none', '--report', 'none'] + sys.argv[1:] + ['login']
run_cli(['--name', 'Firefox', '--variable', 'BROWSER:Firefox', '--output', 'out/fx.xml'] + common, exit=False)
run_cli(['--name', 'IE', '--variable', 'BROWSER:IE', '--output', 'out/ie.xml'] + common, exit=False)
rebot_cli(['--name', 'Login', '--outputdir', 'out', 'out/fx.xml', 'out/ie.xml'])

注意

需要 exit=False,因为默认情况下 run_cli 会以正确的返回代码退出到系统。rebot_cli 也是如此,但在上述示例中这是可以的。

使 *.robot 文件可执行

在类 UNIX 的操作系统上,可以通过赋予它们执行权限并添加像这样的 shebang 来使 *.robot 文件可执行:

1
2
3
4
5
#!/usr/bin/env robot

*** Test Cases ***
Example
Log to console Executing!

如果上述内容位于一个名为 example.robot 的文件中,并且该文件是可执行的,那么它可以像下面这样从命令行执行。从 Robot Framework 3.2 开始,单独执行的文件可以有任何扩展名,或者根本没有扩展名,所以如果文件只是命名为 example,同样也可以工作。

1
./example.robot

这个技巧在执行目录时不起作用,但在执行单个文件时可能很方便。在自动化任务时,这可能更常用一些,而不是在自动化测试时。

调试问题

测试用例可能会失败,因为被测试的系统不正常工作,在这种情况下,测试发现了一个 bug,或者因为测试本身有 bug。解释失败的错误消息显示在命令行输出和报告文件中,有时错误消息本身就足以找出问题。然而,更多的时候,需要日志文件,因为它们还有其他日志消息,并且它们显示了哪个关键字实际上失败了。

当失败是由被测试的应用程序引起的,错误消息和日志消息应该足以理解导致它的原因。如果不是这样,测试库没有提供足够的信息,需要进行增强。在这种情况下,如果可能,手动运行相同的测试也可能揭示有关问题的更多信息。

由测试用例本身或它们使用的关键字引起的失败有时可能很难调试。例如,如果错误消息告诉一个关键字使用了错误数量的参数,修复问题显然很容易,但如果一个关键字缺失或以意外的方式失败,找到根本原因可能会更难。寻找更多信息的第一个地方是日志文件中的执行错误部分。例如,关于失败的测试库导入的错误可能很好地解释了为什么由于缺少关键字而导致测试失败。

如果日志文件默认情况下没有提供足够的信息,可以使用较低的日志级别执行测试。例如,显示失败发生在代码中哪里的回溯是使用 DEBUG 级别记录的,当问题出在单个库关键字时,这些信息是无价的。

记录的回溯不包含 Robot Framework 本身内部方法的信息。如果怀疑错误是由框架的 bug 导致的,可以通过将环境变量 ROBOT_INTERNAL_TRACES 设置为任何非空值来启用显示内部跟踪。

如果日志文件仍然没有足够的信息,启用 syslog 并查看它提供的信息是个好主意。也可以向测试用例添加一些关键字来查看正在发生什么。特别是 BuiltIn 关键字 Log 和 Log Variables 是有用的。如果其他方法都不起作用,总是可以从邮件列表或其他地方寻求帮助。

使用 Python 调试器 (pdb)

也可以使用 Python 标准库中的 pdb 模块来设置断点并交互式地调试正在运行的测试。通常通过在想要进入调试器的位置插入:

1
import pdb; pdb.set_trace()

这种方式调用 pdb 在 Robot Framework 中无法正确工作,因为在关键字执行期间重定向了标准输出流。相反,可以使用以下方式:

1
import sys, pdb; pdb.Pdb(stdout=sys.__stdout__).set_trace()

在 Python 库中,或者作为替代,可以直接在测试用例中使用:

1
Evaluate    pdb.Pdb(stdout=sys.__stdout__).set_trace()    modules=sys, pdb

测试执行

本节描述了如何执行从解析的测试数据创建的测试套件结构,如何确定测试状态,以及如果有失败,如何继续执行测试用例,以及如何优雅地停止整个测试执行。

执行流程

执行的套件和测试

测试用例总是在测试套件中执行。从套件文件创建的测试套件直接拥有测试,而从目录创建的套件拥有子测试套件,这些子套件要么拥有测试,要么拥有它们自己的子套件。默认情况下,执行的套件中的所有测试都会运行,但可以使用选项 --test--suite--include--exclude 来选择测试。不包含测试的套件将被忽略。

执行从顶级测试套件开始。如果套件有测试,它们将逐一执行,如果套件有套件,它们将以深度优先的顺序递归执行。当执行单个测试用例时,它包含的关键字将按顺序运行。通常,如果任何关键字失败,当前测试的执行就会结束,但也可以在失败后继续。以下各节将讨论确切的执行顺序以及可能的设置和拆卸如何影响执行。

设置和拆卸

设置和拆卸可以在测试套件、测试用例和用户关键字级别使用。

套件设置

如果测试套件有一个设置,它将在其测试和子套件之前执行。如果套件设置通过,测试执行将正常继续。如果失败,套件及其子套件包含的所有测试用例都将被标记为失败。子测试套件中的测试和可能的套件设置和拆卸不会被执行。

套件设置通常用于设置测试环境。因为如果套件设置失败,测试就不会运行,所以使用套件设置来验证环境是否处于可以执行测试的状态是很容易的。

套件拆卸

如果测试套件有一个拆卸,它将在所有测试用例和子套件之后执行。无论测试状态如何,甚至如果匹配的套件设置失败,套件拆卸都会被执行。如果套件拆卸失败,之后在报告和日志中,套件中的所有测试都将被标记为失败。

套件拆卸主要用于在执行后清理测试环境。为了确保所有这些任务都完成,拆卸中使用的所有关键字都会被执行,即使其中一些关键字失败。

测试设置

可能的测试设置在测试用例的关键字之前执行。如果设置失败,关键字将不会被执行。测试设置的主要用途是为特定的测试用例设置环境。

测试拆卸

可能的测试拆卸在测试用例执行后执行。无论测试状态如何,甚至如果测试设置失败,都会执行它。

与套件拆卸类似,测试拆卸主要用于清理活动。它们也会被完全执行,即使其中一些关键字失败。

用户关键字设置

用户关键字设置在执行关键字主体之前执行。如果设置失败,主体将不会被执行。关键字设置和主体中的第一个关键字之间没有太大的区别。

注意

用户关键字设置是在 Robot Framework 7.0 中新增的。

用户关键字拆卸

用户关键字拆卸在关键字以其他方式执行后运行,无论状态如何。即使其中一些关键字失败,用户关键字拆卸也会被完全执行。

执行顺序

测试套件中的测试用例按照它们在测试用例文件中定义的顺序执行。高级测试套件内的测试套件按照文件或目录名的不区分大小写的字母顺序执行。如果从命令行给出了多个文件和/或目录,它们将按照给出的顺序执行。

如果需要在目录内使用特定的测试套件执行顺序,可以将前缀如 01 和 02 添加到文件和目录名中。如果这些前缀与套件的基本名称用两个下划线分隔,那么在生成的测试套件名称中不会包含这些前缀:

1
2
01__my_suite.robot -> My Suite
02__another_suite.robot -> Another Suite

如果套件内的测试套件的字母顺序有问题,一个好的解决方法是按照所需的顺序单独给出它们。这很容易导致启动命令过长,但参数文件允许每行列出一个文件,这样很好。

也可以使用 --randomize 选项随机化执行顺序。

测试和套件状态

本节解释了测试如何获得 PASS、FAIL 或 SKIP 状态,以及如何根据测试状态确定套件状态。

注意

SKIP 状态是在 Robot Framework 4.0 中新增的。

PASS

如果一个测试被执行并且它包含的所有关键字都没有失败,那么它将获得 PASS 状态。

提前通过的测试

通常情况下,所有的关键字都会被执行,但也可以使用 BuiltIn 关键字 Pass Execution 和 Pass Execution If 来停止执行,并且不运行剩余的关键字。

以下是 Pass Execution 和 Pass Execution If 在不同情况下的行为:

  • 当它们用在任何设置或拆卸(套件、测试或关键字)中时,这些关键字会使该设置或拆卸通过。可能的已启动关键字的拆卸会被执行。测试执行或状态不会受到其他影响。
  • 当它们在设置或拆卸之外的测试用例中使用时,关键字会使该特定的测试用例通过。可能的测试和关键字拆卸会被执行。
  • 在使用这些关键字之前发生的可能的可继续的失败,以及之后执行的拆卸中的失败,都会导致执行失败。

必须给出一个解释为什么执行被中断的消息,也可以修改测试用例标签。更多的细节和使用示例,请参阅这些关键字的文档。

在测试、设置或拆卸的中间阶段通过执行应该谨慎使用。在最坏的情况下,它可能导致跳过所有可能实际揭示被测试应用程序问题的部分。在执行由于外部因素无法继续的情况下,通常更安全的做法是跳过测试。

FAIL

测试获得 FAIL 状态的最常见原因是它包含的某个关键字失败。关键字本身可以通过引发异常失败,或者关键字可以被错误地调用。其他失败的原因包括语法错误和测试为空。

如果套件设置失败,套件中的测试将在不运行它们的情况下被标记为失败。如果套件拆卸失败,测试将在之后被追溯性地标记为失败。

SKIP

从 Robot Framework 4.0 开始,测试可以获得 SKIP 状态,除了 PASS 和 FAIL。有许多不同的方式可以获得这个状态。

执行前跳过

命令行选项 --skip 可以用来跳过指定的测试,而不需要完全运行它们。它基于标签工作,并支持标签模式,如 examp??tagANDanother。如果多次使用,所有匹配任何指定标签或标签模式的测试都会被跳过:

1
2
3
--skip require-network
--skip windowsANDversion9?
--skip python2.* --skip python3.[0-6]

从 Robot Framework 5.0 开始,也可以通过给测试添加保留标签 robot:skip 来跳过测试用例:

1
2
3
4
*** Test Cases ***
Example
[Tags] robot:skip
Log This is not executed

--skip--exclude 的区别在于,使用后者的测试将被完全排除在执行之外,它们将不会显示在日志和报告中。使用前者,它们被包含在内,但实际上并未执行,它们将在日志和报告中可见。

执行过程中动态跳过

在执行过程中,测试可以通过多种方式获得 SKIP 状态:

  • 在测试用例中的任何地方,包括设置或拆卸,使用 BuiltIn 关键字 Skip。使用 Skip 关键字有两个效果:测试获得 SKIP 状态,测试的其余部分不会被执行。然而,如果测试有一个拆卸,它将被运行。
  • 使用 BuiltIn 关键字 Skip If,它接受一个条件,并在条件为真时跳过测试。
  • 库关键字也可以通过使用特殊的异常来触发跳过行为。这在创建测试库章节的跳过测试部分有解释。
  • 如果使用任何上述方法跳过套件设置,套件中的所有测试都会被跳过,而不执行它们。
  • 如果套件拆卸被跳过,所有的测试将被追溯性地标记为跳过。
自动跳过失败的测试

命令行选项 --skiponfailure 可以用来自动将失败的测试标记为跳过。它基于标签工作,并支持像上面讨论的 --skip 选项那样的标签模式:

1
2
--skiponfailure not-ready
--skiponfailure experimentalANDmobile

从 RF 5.0 开始,可以选择使用保留标签 robot:skip-on-failure 来达到上述相同的效果:

1
2
3
4
*** Test Cases ***
Example
[Tags] robot:skip-on-failure
Fail this test will be marked as skipped instead of failed

这个功能的动机是允许执行那些还没有准备好的测试,或者测试一个还没有准备好的功能。这样的测试不会失败,而是会被标记为跳过,它们的标签可以用来将它们与可能的其他跳过的测试分开。

从关键性迁移到 SKIP

早期的 Robot Framework 版本支持关键性概念,允许将测试标记为关键或非关键。默认情况下,所有测试都是关键的,但可以使用 --critical--noncritical 选项进行配置。关键测试和非关键测试的区别在于,当确定执行的测试套件或整个测试运行的最终状态时,非关键测试不会被包含。实际上,测试状态是二维的,一轴是 PASS 和 FAIL,另一轴是关键性。

非关键的失败测试在许多方面类似于当前的跳过测试。因为这些特性相似,同时拥有 SKIP 和关键性会创建出像非关键 SKIP 这样的奇怪测试状态,所以在 Robot Framework 4.0 中引入 SKIP 状态时,移除了关键性概念。关键性问题的详细解释在提出移除它的问题中。

关键性概念的主要用例是能够运行尚未准备好的测试,或者测试尚未准备好的功能。现在,这个用例由前一节讨论的 skip-on-failure 功能覆盖。

为了简化从关键性到跳过的迁移,旧的 --noncritical 选项在 Robot Framework 4.0 中作为新的 --skiponfailure 的别名工作,同时也保留了旧的 --critical 选项。这两个旧选项都已被弃用,并在 Robot Framework 5.0 中被移除。

套件状态

套件状态完全根据它包含的测试的状态确定:

  • 如果有任何测试失败,套件状态为 FAIL。
  • 如果没有失败,但至少有一个测试通过,套件状态为 PASS。
  • 如果所有测试都已跳过,或者根本没有测试,套件状态为 SKIP。

失败后继续

通常情况下,当测试用例中的任何关键字失败时,测试用例会立即停止。这种行为缩短了测试执行时间,并防止了如果被测试的系统处于不稳定状态时,后续的关键字挂起或者引发其他问题。然而,这有一个缺点,那就是后续的关键字通常会提供更多关于系统状态的信息,而且在某些情况下,这些后续的关键字实际上会负责进行必要的清理活动。因此,Robot Framework 提供了几个即使有失败也可以继续的特性。

自动在拆卸后继续执行

为了确保所有的清理活动都得到了处理,套件、测试和关键字拆卸中自动启用了继续失败模式。实际上,这意味着在拆卸中,所有级别的关键字总是会被执行。

如果不希望这种行为,可以使用特殊的 robot:stop-on-failurerobot:recursive-stop-on-failure 标签来禁用它。

当测试有模板时,执行所有顶级关键字

当使用测试模板时,所有的顶级关键字都会被执行,以确保覆盖所有不同的组合。在这种使用中,继续只限于顶级关键字,而在它们内部,如果有不可继续的失败,执行就会正常结束。

1
2
3
4
5
*** Test Cases ***
Continue with templates
[Template] Should be Equal
this fails
this is run

如果不希望这种行为,可以使用特殊的 robot:stop-on-failurerobot:recursive-stop-on-failure 标签来禁用它。

来自关键字的特殊失败

库关键字使用异常报告失败,可以使用特殊的异常来告诉 Robot Framework,无论失败与否,执行都可以继续。如何创建这些异常在创建测试库部分的可继续失败部分中有解释。

当一个测试结束并且有可继续的失败时,测试将被标记为失败。如果有多于一个的失败,所有的失败都将在最终的错误消息中列举出来:

1
2
3
4
5
Several failures occurred:

1) First error message.

2) Second error message.

如果在可继续的失败之后发生正常的失败,测试执行也会结束。在这种情况下,所有的失败也将在最终的错误消息中列出。

从失败的关键字返回的值,可能被赋值给一个变量,总是 Python 的 None。

Run Keyword And Continue On Failure 关键字

BuiltIn 关键字 Run Keyword And Continue On Failure 允许将任何失败转换为可继续的失败。这些失败由框架以与上述来自库关键字的可继续失败完全相同的方式处理。

1
2
3
4
*** Test Cases ***
Example
Run Keyword and Continue on Failure Should be Equal 1 2
Log This is executed but test fails in the end
使用标签启用失败后继续

作为测试用例或用户关键字的一部分执行的所有关键字,如果它们被标记为 robot:continue-on-failure 标签,则默认认为它们是可继续的。例如,以下两个测试的行为是相同的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*** Test Cases ***
Test 1
Run Keyword and Continue on Failure Should be Equal 1 2
User Keyword 1

Test 2
[Tags] robot:continue-on-failure
Should be Equal 1 2
User Keyword 2

*** Keywords ***
User Keyword 1
Run Keyword and Continue on Failure Should be Equal 3 4
Log This is executed

User Keyword 2
[Tags] robot:continue-on-failure
Should be Equal 3 4
Log This is executed

这些标签也会影响不同控制结构的继续失败模式。例如,下面的测试用例将执行 Do Something 关键字十次,无论它是否成功:

1
2
3
4
5
6
*** Test Cases ***
Example
[Tags] robot:continue-on-failure
FOR ${index} IN RANGE 10
Do Something
END

在测试用例或用户关键字中设置 robot:continue-on-failure 不会将继续失败行为传播到它们调用的用户关键字。如果需要这种递归行为,可以使用 robot:recursive-continue-on-failure 标签。例如,以下示例中的所有关键字都被执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Test Cases ***
Example
[Tags] robot:recursive-continue-on-failure
Should be Equal 1 2
User Keyword 1
Log This is executed

*** Keywords ***
User Keyword 1
Should be Equal 3 4
User Keyword 2
Log This is executed

User Keyword 2
Should be Equal 5 6
Log This is executed

在测试用例中设置 robot:continue-on-failurerobot:recursive-continue-on-failure 不会改变作为 [Setup] 的一部分执行的关键字的失败行为:测试用例被标记为失败,并且不执行任何测试用例关键字。

注意

robot:continue-on-failurerobot:recursive-continue-on-failure 标签是在 Robot Framework 4.1 中新增的。它们在 Robot Framework 6.0 之前的 WHILE 循环中不能正常工作。

使用标签禁用失败后继续

如果需要,可以使用特殊的标签 robot:stop-on-failurerobot:recursive-stop-on-failure 来禁用失败后继续模式。它们在使用标签启用失败后继续以及在拆卸和模板中也起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
*** Test Cases ***
Disable continue-in-failure set using tags
[Tags] robot:recursive-continue-on-failure
Keyword
Keyword # 这将被执行

Disable continue-in-failure in teardown
No Operation
[Teardown] Keyword

Disable continue-in-failure with templates
[Tags] robot:stop-on-failure
[Template] Should be Equal
this fails
this is not run

*** Keywords ***
Keyword
[Tags] robot:stop-on-failure
Should be Equal this fails
Should be Equal this is not run

robot:stop-on-failure 标签只影响使用它的测试用例和用户关键字,并且不会传播到它们调用的用户关键字或它们自己的拆卸。如果需要影响所有调用的用户关键字和拆卸的递归行为,可以使用 robot:recursive-stop-on-failure 标签代替。如果有需要,它的效果可以再次在较低级别的关键字中通过使用 robot:continue-on-failurerobot:recursive-continue-on-failure 标签来禁用。

robot:stop-on-failurerobot:recursive-stop-on-failure 标签不会改变由库关键字或 Run Keyword And Continue On Failure 引起的可继续失败的行为。例如,尽管使用了 robot:stop-on-failure,但这个示例中的两个关键字都会运行:

1
2
3
4
5
*** Test Cases ***
Example
[Tags] robot:stop-on-failure
Run Keyword and Continue on Failure Should be Equal 1 2
Log This is executed regardless the tag

如果 robot:recursive-stop-on-failurerobot:continue-on-failure 在同一个测试或关键字中一起使用,如果有失败,执行将在调用的关键字中停止,但在使用这些标签的测试或关键字中继续。如果 robot:recursive-continue-on-failurerobot:stop-on-failure 在同一个测试或关键字中一起使用,如果有失败,执行将在调用的关键字中继续,但在使用这些标签的测试或关键字中停止。

注意

robot:stop-on-failurerobot:recursive-stop-on-failure 标签是在 Robot Framework 6.0 中新增的。

注意

在同一个测试或关键字中同时使用递归和非递归标签是在 Robot Framework 7.0 中新增的。

TRY/EXCEPT

Robot Framework 5.0 引入了原生的 TRY/EXCEPT 语法,可以用于处理失败:

1
2
3
4
5
6
7
*** 测试用例 ***
示例
TRY
某个关键字
EXCEPT 预期的错误信息
错误处理关键字
END

更多详情请参见单独的 TRY/EXCEPT 语法部分。

内置关键字

有几个内置关键字可以用于执行其他关键字,以便在可能的失败后继续执行:

  • Run Keyword And Expect Error 执行一个关键字并期望它失败,给出指定的错误信息。现在一般推荐使用上述的 TRY/EXCEPT 语法。
  • Run Keyword And Ignore Error 执行一个关键字并忽略可能的错误。它返回状态以及可能的关键字返回值或错误信息。在这种情况下,TRY/EXCEPT 语法通常效果更好。
  • Run Keyword And Warn On FailureRun Keyword And Ignore Error 的包装器,如果执行的关键字失败,它会自动记录警告。
  • Run Keyword And Return Status 执行一个关键字并返回布尔值 True 或 False,取决于它是否通过。

优雅地停止测试执行

有时候,需要在所有测试完成之前停止测试执行,但同时需要生成日志和报告。以下是实现这一目标的不同方法。在所有这些情况下,剩余的测试用例都会被标记为失败。

自动失败的测试会获得 robot:exit 标签,生成的报告将包含 NOT robot:exit 组合标签模式,以便轻松查看那些未被跳过的测试。请注意,发生退出的测试不会获得 robot:exit 标签。

注意

在 Robot Framework 3.1 之前,特殊标签被命名为 robot-exit

按下 Ctrl-C

当在运行测试的控制台中按下 Ctrl-C 时,执行会立即停止,但仍会生成报告和日志。

如果再次按下 Ctrl-C,执行会立即结束,报告和日志不会被创建。

使用信号

在类 UNIX 机器上,可以使用 INT 和 TERM 信号来终止测试执行。这些信号可以通过使用 kill 命令从命令行发送,发送信号也可以很容易地自动化。

使用关键字

执行也可以通过执行的关键字来停止。有一个单独的 Fatal Error 内置关键字用于此目的,自定义关键字在失败时可以使用致命异常。

当第一个测试用例失败时停止

如果使用了 --exitonfailure(-X)选项,如果有任何测试失败,测试执行会立即停止。剩余的测试会被标记为失败,而实际上并没有执行它们。

在解析或执行错误时停止

Robot Framework 将由失败的关键字引起的失败与由例如无效设置或失败的测试库导入引起的错误分开。默认情况下,这些错误被报告为测试执行错误,但错误本身不会导致测试失败或以其他方式影响执行。然而,如果使用了 --exitonerror 选项,所有这样的错误都被认为是致命的,并且执行停止,以便剩余的测试被标记为失败。对于在执行开始之前就遇到的解析错误,这意味着实际上没有运行任何测试。

处理 teardowns

默认情况下,即使使用上述方法之一停止了测试执行,已经开始的测试和套件的 teardowns 也会被执行。这允许无论执行如何结束,都可以运行清理活动。

也可以使用 --skipteardownonexit 选项在执行停止时跳过 teardowns。例如,如果清理任务需要很长时间,这可能会很有用。

任务执行

除了测试自动化之外,Robot Framework 还可以用于其他自动化目的,从 Robot Framework 3.1 开始,可以明确地创建和执行任务。任务执行和测试执行在大多数部分上工作方式相同,本节将解释其中的差异。

通用自动化模式

当 Robot Framework 用于执行一个文件,并且注意到该文件有任务,而不是测试时,它会自动设置自己进入通用自动化模式。这种模式并不改变实际的执行,但是当创建日志和报告时,它们使用的是任务(task),而不是测试(test)。例如,它们的标题是 “任务日志”(Task Log)和 “任务统计”(Task Statistics),而不是 “测试日志”(Test Log)和 “测试统计”(Test Statistics)。

通用自动化模式也可以通过使用 --rpa 选项来启用。在这种情况下,执行的文件可以有测试或任务。或者,可以使用 --norpa 来强制测试自动化模式,即使执行的文件包含任务。如果这两个选项都没有使用,那么执行多个文件,使得一些文件有测试,其他文件有任务,将会是一个错误。

执行模式存储在生成的输出文件中,并由 Rebot 读取,如果输出被后处理。如果需要,也可以在使用 Rebot 时设置模式。

任务相关的命令行选项

执行任务时可以使用所有正常的命令行选项。如果需要选择只执行某些任务,可以使用 --task 代替 --test。此外,还可以使用前面提到的 --rpa 来控制执行模式。

后处理输出

在测试执行期间生成的 XML 输出文件可以在之后由 Rebot 工具进行后处理,Rebot 是 Robot Framework 的一个组成部分。它在测试执行期间自动生成测试报告和日志,单独使用它可以创建自定义报告和日志以及合并结果。

使用 Rebot

概述
1
2
3
rebot [options] outputs
python -m robot.rebot [options] outputs
python path/to/robot/rebot.py [options] outputs

使用 Rebot 的最常见方式是使用 rebot 命令。或者,也可以使用选定的 Python 解释器执行已安装的 robot.rebot 模块或 robot/rebot.py 文件。

指定选项和参数

使用 Rebot 的基本语法与启动测试执行时完全相同,大多数命令行选项也是相同的。主要的区别是 Rebot 的参数是 XML 输出文件,而不是测试数据文件或目录。

Rebot 的返回代码

Rebot 的返回代码与运行测试时完全相同。

控制执行模式

Rebot 注意到已经运行了测试或任务,并默认保留执行模式。该模式影响日志和报告,以便在前一种情况下,它们将使用像 “Test Log” 和 “Test Statistics” 这样的测试术语,在后一种情况下,它们将使用像 “Task Log” 和 “Task Statistics” 这样的任务术语。

Rebot 还支持使用 --rpa--norpa 选项来显式设置执行模式。如果处理多个输出文件并且它们的模式有冲突,这是必要的。

创建报告、日志和输出文件

可以使用 Rebot 创建在测试执行期间自动创建的相同的报告和日志。当然,创建完全相同的文件是没有意义的,但是,例如,有一个包含所有测试用例的报告和只包含一些测试子集的另一个报告可能是有用的:

1
2
3
rebot output.xml
rebot path/to/output_file.xml
rebot --include smoke --name Smoke_Tests c:\results\output.xml

另一个常见的用法是在运行测试时只创建输出文件(可以使用 --log NONE --report NONE 禁用日志和报告生成),并稍后生成日志和报告。例如,可以在不同的环境上执行测试,将输出文件收集到一个中心位置,然后在那里创建报告和日志。

默认情况下,Rebot 不创建 XML 输出文件,但是可以使用 --output(-o)选项创建它们。默认情况下会创建日志和报告,但是如果不需要它们,可以使用值 NONE(不区分大小写)禁用它们:

1
rebot --include smoke --output smoke.xml --log none --report none original.xml          

合并输出

Rebot 的一个重要特性是它能够合并来自不同测试执行轮次的输出。这种能力允许,例如,在不同的环境上运行相同的测试用例,并从所有输出中生成一个总体报告。合并输出非常容易,只需要将多个输出文件作为参数给出:

1
2
rebot output1.xml output2.xml
rebot outputs/*.xml

当输出被合并时,会创建一个新的顶级测试套件,使得给定输出文件中的测试套件成为其子套件。当执行多个测试数据文件或目录时,这种情况也是一样的,而且在这种情况下,顶级测试套件的名称是通过用 & 和空格连接子套件名称来创建的。这些自动生成的名称不是那么好,通常使用 --name 给出更有意义的名称是一个好主意:

1
2
rebot --name Browser_Compatibility firefox.xml opera.xml safari.xml ie.xml
rebot --include smoke --name Smoke_Tests c:\results\*.xml

融合输出

如果相同的测试被重新执行或者一个单独的测试套件被分片执行,像上面讨论的那样合并结果会创建一个不必要的顶级测试套件。在这些情况下,通常更好的做法是融合结果。融合是通过使用 --merge(-R)选项完成的,该选项改变了 Rebot 合并两个或更多输出文件的方式。这个选项本身不需要参数,所有其他的命令行选项都可以正常地与它一起使用:

1
2
rebot --merge original.xml merged.xml
rebot --merge --name Example first.xml second.xml third.xml

当套件被融合时,文档、套件设置和套件拆卸都来自最后融合的套件。所有融合并套件的套件元数据都被保留,以便后面的套件的值具有优先权。

如何合并测试的工作方式在以下讨论两个主要融合用例的部分中进行了解释。

注意

从融合套件获取套件文档和元数据是 Robot Framework 6.0 中的新功能。

重新执行测试并融合结果

在测试过程中,经常需要重新执行一部分测试,例如在修复了被测试系统或测试本身的错误后。这可以通过按名称(--test--suite 选项)、标签(--include--exclude)或以前的状态(--rerunfailed--rerunfailedsuites)选择测试用例来实现。

使用默认的输出融合方法将重新执行的结果与原始结果结合起来效果不太好。主要问题是会得到单独的测试套件,可能已经修复的失败也会显示出来。在这种情况下,最好使用 --merge (-R) 选项告诉 Rebot 合并结果。实际上,这意味着后续测试运行中的测试替换了原始测试。此规则的一个例外是,后续运行中跳过的测试被忽略,保留原始测试。

以下是一个实际的例子,使用 --rerunfailed--merge 一起使用:

1
2
3
robot --output original.xml tests                          # 首先执行所有测试
robot --rerunfailed original.xml --output rerun.xml tests # 然后重新执行失败的测试
rebot --merge original.xml rerun.xml # 最后合并结果

融合的测试的消息包含一个注释,说明结果已被替换。消息还显示了测试的旧状态和消息。

融合的结果必须始终具有相同的顶级测试套件。在融合的输出中未从原始输出中找到的测试和套件被添加到结果输出中。这在实践中是如何工作的将在下一节中讨论。

注意:在 Robot Framework 4.1 中,新的特性是在后续运行中忽略跳过的测试。

融合分段执行的套件

--merge 选项的另一个重要用例是融合使用 --include--exclude 选项等方式分段运行测试套件时得到的结果:

1
2
3
robot --include smoke --output smoke.xml tests   # 首先运行一些测试
robot --exclude smoke --output others.xml tests # 然后运行其他测试
rebot --merge smoke.xml others.xml # 最后融合结果

当像这样融合输出时,结果输出包含了所有给定输出文件中找到的所有测试和套件。如果某个测试在多个输出中找到,最新的结果替换早期的结果,就像在前一节中解释的那样。这种融合策略也要求所有输出中的顶级测试套件相同。

JSON 输出文件

Rebot 可以创建和处理 JSON 格式的输出文件。创建 JSON 输出文件是使用正常的 --output 选项,指定的文件有 .json 扩展名:

1
rebot --output output.json output.xml

当读取输出文件时,JSON 文件会根据扩展名自动识别:

1
2
rebot output.json
rebot output1.json output2.json

当合并或合并结果时,可以混合 JSON 和 XML 文件:

1
2
rebot output1.xml output2.json
rebot --merge original.xml rerun.json

JSON 输出文件的结构在 result.json schema 文件中有文档。

注意

在 Robot Framework 7.0 中,新的特性是支持 JSON 输出文件。

配置执行

本节解释了可以用于配置测试执行或后处理输出的不同命令行选项。与生成的输出文件相关的选项将在下一节中讨论。

选择要解析的文件

执行单个文件

当执行单个文件时,Robot Framework 会尝试解析并运行它们,而不考虑文件名或文件扩展名。使用哪个解析器取决于扩展名:

  • .robot 文件和未被识别的文件使用正常的 Robot Framework 解析器进行解析。
  • .rst.rest 文件使用 reStructuredText 解析器进行解析。
  • .rbt.json 文件使用 JSON 解析器进行解析。
  • 支持自定义解析器的文件由匹配的解析器进行解析。

示例:

1
2
3
4
robot example.robot    # 标准的 Robot Framework 解析器。
robot example.tsv # 必须与标准解析器兼容。
robot example.rst # reStructuredText 解析器。
robot x.robot y.rst # 使用适当的解析器解析两个文件。
包含和排除文件

当执行一个目录时,文件和目录按照以下规则进行解析:

  • 所有以点(.)或下划线(_)开头的文件和目录都被忽略。
  • .robot 文件使用正常的 Robot Framework 解析器进行解析。
  • .robot.rst 文件使用 reStructuredText 解析器进行解析。
  • .rbt 文件使用 JSON 解析器进行解析。
  • 支持自定义解析器的文件由匹配的解析器进行解析。
  • 其他文件被忽略,除非使用 --parseinclude--extension 选项启用了解析它们,这些选项将在后续部分中讨论。

选择要解析的文件

根据名称或路径选择文件

在执行目录时,可以使用 --parseinclude (-I) 选项根据文件的名称或路径来解析特定的文件。这个选项的语义稍有不同,具体取决于它使用的值:

  • 如果值只是一个文件名,如 example.robot,那么所有目录中匹配该名称的文件都将被解析。
  • 要匹配特定目录中的特定文件,可以将文件作为相对或绝对路径给出,如 path/to/tests.robot
  • 如果值是一个目录的路径,那么该目录内的所有文件都会被解析,递归地。

示例:

1
2
3
4
robot --parseinclude example.robot tests       # 解析 `tests` 下的 `example.robot` 文件。
robot -I example_*.robot -I ???.robot tests # 解析 `tests` 下匹配 `example_*.robot` 或 `???.robot` 的文件。
robot -I tests/example.robot tests # 只解析 `tests/example.robot`。
robot --parseinclude tests/example tests # 递归解析 `tests/example` 目录下的文件。

--parseinclude 一起使用的值不区分大小写,并支持像 example_*.robot 这样的 glob 模式。然而,与 Robot Framework 通常如何工作的模式相比,有两个小的不同:

  • * 只匹配一个路径段。例如,path/*/tests.robot 匹配 path/to/tests.robot,但不匹配 path/to/nested/tests.robot
  • ** 可以用来启用递归匹配。例如,path/**/tests.robot 匹配 path/to/tests.robotpath/to/nested/tests.robot

如果模式包含一个扩展名,那么即使默认情况下不会解析具有该扩展名的文件,也会解析它。使用哪个解析器取决于使用的扩展名:

  • .rst.rest 文件使用 reStructuredText 解析器进行解析。
  • .json 文件使用 JSON 解析器进行解析。
  • 其他文件使用正常的 Robot Framework 解析器进行解析。

注意

当使用像 *.robot 这样的模式,并且在执行目录中存在匹配该模式的文件时,shell 可能会在调用 Robot Framework 之前解析该模式,传递给它的值是文件名,而不是原始模式。在这种情况下,需要引用或转义模式,如 '*.robot'\*.robot

--parseinclude 是 Robot Framework 6.1 中的新特性。

根据扩展名选择文件

除了使用上一节中讨论的 --parseinclude 选项外,还可以使用 --extension (-F) 选项来启用解析默认情况下不解析的文件。匹配扩展名不区分大小写,可以省略前导点。如果需要解析多种类型的文件,可以使用冒号 : 来分隔扩展名:

1
2
robot --extension rst path/to/tests    # 只解析 *.rst 文件。
robot -F robot:rst path/to/tests # 解析 *.robot 和 *.rst 文件。

以上等同于以下的 --parseinclude 使用:

1
2
robot --parseinclude *.rst path/to/tests
robot -I *.robot -I *.rst path/to/tests

因为 --parseinclude 选项更强大,覆盖了 --extension 选项的所有用例,所以后者可能在未来被弃用。建议用户现在就开始使用 --parseinclude

使用自定义解析器

外部解析器可以解析 Robot Framework 否则无法识别的文件。有关创建和使用此类解析器的更多信息,请参见解析器接口部分。

选择测试用例

Robot Framework 提供了几个命令行选项用于选择要执行的测试用例。当执行任务和后处理 Rebot 的输出时,这些选项也同样适用。

通过测试名称

选择只运行一些测试的最简单方法是使用 --test (-t) 选项。顾名思义,它可以用于通过它们的名称选择测试。给定的名称对大小写、空格和下划线不敏感,也支持简单的模式。该选项可以多次使用以匹配多个测试:

1
2
3
--test Example                   # 只匹配名为 'Example' 的测试。
--test example* # 匹配以 'example' 开头的测试。
--test first --test second # 匹配名为 'first' 或 'second' 的测试。

为了更精确地指定一个测试,可以在测试名称前加上套件名称:

1
2
3
--test mysuite.mytest            # 匹配套件 'mysuite' 中的测试 'mytest'。
--test root.sub.test # 匹配套件 'root' 中的套件 'sub' 中的测试 'test'。
--test *.sub.test # 匹配任何地方的套件 'sub' 中的测试 'test'。

注意,当给定的名称包含套件名称时,它必须匹配从根套件开始的整个套件名称。如上面的最后一个例子所示,使用通配符允许匹配具有父套件的测试。

当只需要选择几个测试时,使用 --test 选项很方便。一个常见的用例是只运行当前正在处理的测试。如果需要选择更多的测试,通常通过套件名称或标签名称选择它们会更容易。

在执行任务时,可以使用 --task 选项作为 --test 的别名。

通过套件名称

也可以使用 --suite (-s) 选项通过套件名称选择测试,该选项选择匹配套件中的所有测试。与 --test 类似,给定的名称对大小写、空格和下划线不敏感,并支持简单的模式。为了更精确地指定一个套件,可以在名称前加上父套件的名称:

1
2
3
4
5
--suite Example                  # 只匹配名为 'Example' 的套件。
--suite example* # 匹配以 'example' 开头的套件。
--suite first --suite second # 匹配名为 'first' 或 'second' 的套件。
--suite root.child # 匹配根套件 'root' 中的套件 'child'。
--suite *.parent.child # 匹配任何地方的父套件 'parent' 下的套件 'child'。

如果名称包含父套件名称,它必须以与 --test 相同的方式匹配整个套件名称。如上面的最后一个例子所示,使用通配符允许匹配具有父套件的套件。

注意

在 Robot Framework 7.0 之前,带有父套件的 --suite 不需要匹配整个套件名称。例如,parent.child 会匹配任何地方的父套件 parent 下的套件 child。如果现在希望这种行为,那么名称必须以通配符为前缀。

如果同时使用 --suite--test 选项,只有指定套件中的指定测试被选择:

1
--suite mysuite --test mytest    # 如果测试 'mytest' 在套件 'mysuite' 中,则匹配。

使用 --suite 选项大致相当于直接执行适当的套件文件或目录。主要的区别是,如果直接运行文件或目录,可能的套件设置和拆卸在更高级别上不会被执行:

1
2
3
4
5
# 根套件是 'Tests',并且可能的设置和拆卸会被运行。
robot --suite example path/to/tests

# 根套件是 'Example',并且可能的更高级别的设置和拆卸会被忽略。
robot path/to/tests/example.robot

在 Robot Framework 6.1 之前,出于性能考虑,不符合 –suite 选项的文件根本不会被解析。然而,当测试套件获得了一个新的 Name 设置,该设置可以覆盖从文件或目录名称获取的默认测试套件名称后,这种优化就不再可能了。因此,如果需要进行这种类型的解析优化,就会新增 –parseinclude 选项,用于显式选择要解析的文件。

通过标签名称

可以使用 --include (-i) 和 --exclude (-e) 选项分别根据标签名称包含和排除测试用例。如果使用了 --include 选项,只有具有匹配标签的测试用例会被选中,而使用 --exclude 选项,具有匹配标签的测试用例将不会被选中。如果两者都使用,只有具有匹配前者选项的标签,且没有匹配后者的标签的测试会被选中:

1
2
3
--include example
--exclude not_ready
--include regression --exclude long_lasting

--include--exclude 都可以多次使用以匹配多个标签。在这种情况下,如果一个测试具有匹配任何包含的标签,并且也没有匹配任何排除的标签,那么该测试就会被选中。

除了指定完全匹配的标签外,还可以使用标签模式,其中 *? 是通配符,ANDORNOT 运算符可以用于将单个标签或模式组合在一起:

1
2
3
4
5
--include feature-4?
--exclude bug*
--include fooANDbar
--exclude xxORyyORzz
--include fooNOTbar

从 RF 5.0 开始,也可以使用保留标签 robot:exclude 来达到与使用 --exclude 选项相同的效果:

1
2
3
4
*** Test Cases ***
Example
[Tags] robot:exclude
Fail This is not executed

通过标签选择测试用例是一种非常灵活的机制,允许许多有趣的可能性:

  • 可以用 smoke 标签标记要在其他测试之前执行的测试子集(通常称为烟雾测试),并使用 --include smoke 执行。
  • 可以用 not_ready 等标签将未完成的测试提交到版本控制,并使用 --exclude not_ready 从测试执行中排除。
  • 测试可以用 sprint-<num> 标签标记,其中 <num> 指定当前冲刺的编号,执行所有测试用例后,可以生成一个只包含某个冲刺的测试的单独报告(例如,rebot --include sprint-42 output.xml)。

--include--exclude 选项可以与前一节中讨论的 --suite--test 一起使用。它们如何一起工作的一般规则如下:

  • 如果使用了 --suite,测试必须在指定的套件中,除此之外还要满足其他选择条件。
  • 如果 --include--test 一起使用,对于一个测试来说,匹配其中任何一个就足够了。
  • 如果使用了 --exclude,匹配它的测试永远不会被选中。

以上规则在以下示例中得到了演示:

1
2
3
4
--suite example --include tag    # 如果测试在套件 'example' 中并且有标签 'tag',则匹配。
--suite example --exclude tag # 如果测试在套件 'example' 中并且没有标签 'tag',则匹配。
--test example --include tag # 如果测试的名称是 'example' 或者它有标签 'tag',则匹配。
--test ex* --exclude tag # 如果测试的名称以 'ex' 开头并且它没有标签 'tag',则匹配。

注意

在 Robot Framework 7.0 之前,使用 --include--test 一起需要测试同时具有匹配的标签和匹配的名称。

重新执行失败的测试用例

命令行选项 --rerunfailed (-R) 可以用来从早期的输出文件中选择所有失败的测试用例进行重新执行。例如,如果运行所有测试需要很长时间,而希望迭代修复失败的测试用例,那么这个选项就很有用。

1
2
robot tests                             # 首先执行所有测试
robot --rerunfailed output.xml tests # 然后重新执行失败的测试

在幕后,这个选项选择了失败的测试,就像它们是使用 --test 选项单独选择的一样。可以使用 --test--suite--include--exclude 选项进一步微调所选测试的列表。

如果输出中没有失败的测试,那么这是一个错误,但是可以使用下面讨论的 --runemptysuite 选项改变这种行为。使用一个不是来自现在运行的相同测试的输出会导致结果未定义。使用一个特殊的值 NONE 作为输出,就像根本没有指定这个选项一样。

提示:可以使用 --merge 命令行选项将重新执行的结果和原始结果合并在一起。

重新执行失败的测试套件

命令行选项 --rerunfailedsuites (-S) 可以用来从早期的输出文件中选择所有失败的套件进行重新执行。像 --rerunfailed (-R) 一样,当完整的测试执行需要很长时间时,这个选项很有用。注意,一个失败的测试套件中的所有测试都将被重新执行,即使是通过的测试。当一个测试套件中的测试相互依赖时,这个选项很有用。

在幕后,这个选项选择了失败的套件,就像它们是使用 --suite 选项单独选择的一样。可以使用 --test--suite--include--exclude 选项进一步微调所选测试的列表。

当没有测试匹配选择时

默认情况下,当没有测试匹配选择条件时,测试执行会失败,错误如下:

1
[ ERROR ] Suite 'Example' with includes 'xxx' contains no test cases.

因为没有生成输出,所以如果测试是自动执行和处理结果的,这种行为可能会有问题。幸运的是,可以使用命令行选项 --RunEmptySuite(不区分大小写)强制在这种情况下也执行套件。结果是创建了正常的输出,但显示执行的测试为零。当执行一个空目录或一个不包含测试的测试用例文件时,也可以使用相同的选项来改变行为。

在使用 Rebot 处理输出文件时,也可能出现类似的情况。可能没有测试匹配使用的过滤条件,或者输出文件一开始就没有测试。默认情况下,执行 Rebot 在这些情况下会失败,但是它有一个单独的 --ProcessEmptySuite 选项,可以用来改变行为。实际上,这个选项在运行测试时的工作方式与 --RunEmptySuite 相同。

注意

使用 --RunEmptySuite--ReRunFailed--ReRunFailedSuites 需要 Robot Framework 5.0.1 或更高版本。

设置元数据

设置套件名称

当 Robot Framework 解析测试数据时,套件名称是从文件和目录名称创建的。然而,可以使用命令行选项 --name (-N) 覆盖顶级测试套件的名称:

1
robot --name "Custom name" tests.robot
设置套件文档

除了在测试数据中定义文档外,还可以使用选项 --doc (-D) 从命令行给出顶级套件的文档。值可以包含简单的 HTML 格式,如果包含空格,必须加引号。

如果给定的文档是指向现有文件的相对或绝对路径,那么实际的文档将从该文件中读取。如果外部指定的文档很长或包含多行,这特别方便。

示例:

1
2
robot --doc "Example documentation" tests.robot
robot --doc doc.txt tests.robot # 如果存在,文档将从 doc.txt 中读取。

注意

在 Robot Framework 4.1 中,从外部文件读取文档是新的特性。

在 Robot Framework 3.1 之前,文档中的下划线会像 --name 选项一样被转换为空格。

设置自由套件元数据

也可以使用选项 --metadata (-M) 从命令行给出自由套件元数据。参数必须以 name:value 的格式给出,其中 name 是要设置的元数据的名称,value 是其值。值可以包含简单的 HTML 格式,如果整个参数包含空格,必须加引号。这个选项可以多次使用以设置多个元数据值。

如果给定的值是指向现有文件的相对或绝对路径,那么实际的值将从该文件中读取。如果值很长或包含多行,这特别方便。如果值应该是一个指向现有文件的路径,而不是从该文件中读取,那么值必须与 name: 部分用空格分隔。

示例:

1
2
3
4
robot --metadata Name:Value tests.robot
robot --metadata "Another Name:Another value, now with spaces" tests.robot
robot --metadata "Read From File:meta.txt" tests.robot # 如果存在,值将从 meta.txt 中读取。
robot --metadata "Path As Value: meta.txt" tests.robot # 值始终按原样使用。

注意

在 Robot Framework 4.1 中,从外部文件读取元数据值是新的特性。

在 Robot Framework 3.1 之前,值中的下划线会像 --name 选项一样被转换为空格。

设置测试标签

命令行选项 --settag (-G) 可以用来为所有执行的测试用例设置给定的标签。这个选项可以多次使用以设置多个标签。

配置搜索库和其他扩展的位置

当 Robot Framework 导入测试库、监听器或其他基于 Python 的扩展时,它使用 Python 解释器从系统中导入包含扩展的模块。查找模块的位置列表被称为模块搜索路径,可以使用本节解释的不同方法配置其内容。

如果指定的路径没有直接匹配任何文件,Robot Framework 也会在导入资源和变量文件时使用 Python 的模块搜索路径。

正确设置模块搜索路径以找到库和其他扩展是成功执行测试的要求。如果需要使用下面解释的方法自定义它,创建一个自定义的启动脚本通常是个好主意。

自动在模块搜索路径中的位置

Python 解释器有自己的标准库,以及一个第三方模块自动安装的目录,在模块搜索路径中。这意味着使用 Python 自己的打包系统打包的测试库会自动安装,以便可以导入它们,无需任何额外的配置。

PYTHONPATH

Python 从 PYTHONPATH 环境变量中读取要添加到模块搜索路径的额外位置。如果想在其中任何一个环境变量中指定多个位置,需要在类 UNIX 机器上用冒号分隔位置(例如,/opt/libs:$HOME/testlibs),在 Windows 上用分号分隔(例如,D:\libs;%HOMEPATH%\testlibs)。

环境变量可以被永久地配置为系统范围,或者只影响某个用户。或者,它们可以在运行命令之前临时设置,这在自定义启动脚本中工作得非常好。

使用 –pythonpath 选项

Robot Framework 有一个单独的命令行选项 --pythonpath (-P),用于向模块搜索路径添加位置。

可以通过用冒号(:)或分号(;)分隔它们,或者多次使用此选项来给出多个位置。如果值包含冒号和分号,那么它将从分号处分割。路径也可以是匹配多个路径的 glob 模式,但是它们通常需要在控制台上使用时进行转义。

示例:

1
2
3
4
5
--pythonpath libs
--pythonpath /opt/testlibs:mylibs.zip:yourlibs
--pythonpath /opt/testlibs --pythonpath mylibs.zip --pythonpath yourlibs
--pythonpath c:\temp;d:\resources
--pythonpath lib/\*.zip # '*' 是转义的

注意:无论操作系统如何,冒号和分号都可以工作。在 Robot Framework 5.0 中,使用分号是新的。

以编程方式配置 sys.path

Python 解释器将它们使用的模块搜索路径存储为字符串列表在 sys.path 属性中。这个列表可以在执行过程中动态更新,下次导入某个东西时会考虑到这些更改。

设置变量

可以使用 --variable (-v) 选项从命令行单独设置变量,或者通过 --variablefile (-V) 选项通过变量文件设置变量。变量和变量文件在单独的章节中有解释,但以下示例说明了如何使用这些选项:

1
2
3
4
5
--variable name:value
--variable OS:Linux --variable IP:10.0.0.42
--variablefile path/to/variables.py
--variablefile myvars.py:possible:arguments:here
--variable ENVIRONMENT:Windows --variablefile c:\resources\windows.py

干运行

Robot Framework 支持所谓的干运行模式,在这种模式下,测试通常会正常运行,但来自测试库的关键字根本不会被执行。干运行模式可以用来验证测试数据;如果干运行通过,数据应该在语法上是正确的。这种模式使用选项 --dryrun 触发。

干运行执行可能会因以下原因失败:

  • 使用未找到的关键字。
  • 使用参数数量错误的关键字。
  • 使用语法无效的用户关键字。

除了这些失败之外,还会显示正常执行错误,例如,当无法解析测试库或资源文件导入时。

可以通过向特定用户关键字添加一个特殊的 robot:no-dry-run 关键字标签来禁用干运行验证。如果一个关键字在干运行模式下由于某种原因失败,但在正常执行时工作正常,这将很有用。

注意

干运行模式不验证变量。

随机化执行顺序

可以使用选项 --randomize <what>[:<seed>] 来随机化测试执行顺序,其中 <what> 是以下之一:

  • tests:每个测试套件内的测试用例以随机顺序执行。
  • suites:所有测试套件以随机顺序执行,但套件内的测试用例按照它们定义的顺序运行。
  • all:测试用例和测试套件都以随机顺序执行。
  • none:测试和套件的执行顺序都不会被随机化。这个值可以用来覆盖之前用 --randomize 设置的值。

可以给出一个自定义的种子来初始化随机生成器。如果想使用和之前相同的顺序重新运行测试,这将很有用。种子作为 --randomize 的值的一部分给出,格式为 <what>:<seed>,它必须是一个整数。如果没有给出种子,它将被随机生成。执行的顶级测试套件自动获取名为 Randomized 的元数据,它告诉随机化了什么和使用了什么种子。

示例:

1
2
robot --randomize tests my_test.robot
robot --randomize all:12345 path/to/tests

编程修改测试数据

如果提供的内置功能在执行前修改测试数据还不够,Robot Framework 允许以编程方式进行自定义修改。这是通过创建一个所谓的预运行修改器并使用 --prerunmodifier 选项激活它来实现的。

预运行修改器应该作为访问者实现,可以遍历可执行的测试套件结构并根据需要修改它。访问者接口是作为 Robot Framework API 文档的一部分进行解释的,可以使用它来修改执行的测试套件、测试用例和关键字。下面的示例应该能给出如何使用预运行修改器以及这个功能有多强大的想法。

当在命令行上使用 --prerunmodifier 选项启用预运行修改器时,可以指定为修改器类的名称或修改器文件的路径。如果修改器作为类名给出,包含该类的模块必须在模块搜索路径中,如果模块名与类名不同,给定的名称必须包含两者,如 module.ModifierClass。如果修改器作为路径给出,类名必须与文件名相同。在大多数情况下,这与导入测试库的方式完全相同。

如果修改器需要参数,像下面的示例那样,它们可以在修改器名称或路径后使用冒号(:)或分号(;)作为分隔符指定。如果值中使用了两者,那么首先使用的那个被认为是实际的分隔符。从 Robot Framework 4.0 开始,参数也支持命名参数语法,以及基于类型提示和默认值的参数转换,就像关键字一样。

如果需要多个预运行修改器,可以通过多次使用 --prerunmodifier 选项来指定。如果在创建日志和报告之前需要类似的修改,可以使用 --prerebotmodifier 选项启用结果的编程修改。

预运行修改器在影响执行的测试套件和测试用例的其他配置之前执行。最重要的是,选择测试用例的选项在修改器之后处理,使得可以使用 --include 等选项,也可以与可能动态添加的测试一起使用。

信息

修改器在命令行上的使用方式与监听器完全相同。请参阅从命令行注册监听器部分以获取更多信息和示例。

示例:选择每个 Xth 测试

第一个示例展示了如何通过预运行修改器从执行的测试套件结构中移除测试。在此示例中,只保留每个 Xth 测试,X 是从命令行给出的,还有一个可选的起始索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"""预运行修改器,仅选择每个 Xth 测试进行执行。

默认从第一个测试开始。每个套件选择测试。
"""

from robot.api import SuiteVisitor

class SelectEveryXthTest(SuiteVisitor):

def __init__(self, x: int, start: int = 0):
self.x = x
self.start = start

def start_suite(self, suite):
"""修改套件的测试,只包含每个 Xth。"""
suite.tests = suite.tests[self.start::self.x]

def end_suite(self, suite):
"""移除测试后为空的套件。"""
suite.suites = [s for s in suite.suites if s.test_count > 0]

def visit_test(self, test):
"""避免访问测试及其关键字以节省一些时间。"""
pass

如果上述预运行修改器在 SelectEveryXthTest.py 文件中,并且该文件在模块搜索路径中,那么可以这样使用:

1
2
3
4
5
# 指定修改器为路径。运行每个第二个测试。
robot --prerunmodifier path/to/SelectEveryXthTest.py:2 tests.robot

# 指定修改器为名称。运行每个第三个测试,从第二个开始。
robot --prerunmodifier SelectEveryXthTest:3:1 tests.robot

注意

基于类型提示的参数转换,如上例中的 x: int,是 Robot Framework 4.0 中的新特性,需要 Python 3。

示例:通过名称排除测试

第二个示例也移除了测试,这次是基于给定的名称模式。实际上,它的工作方式类似于内置的 –test 选项的负版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"""预运行修改器,通过它们的名称排除测试。

要排除的测试是通过使用模式指定的,该模式既不区分大小写也不区分空格,
并支持 '*'(匹配任何内容)和 '?'(匹配单个字符)作为通配符。
"""

from robot.api import SuiteVisitor
from robot.utils import Matcher

class ExcludeTests(SuiteVisitor):

def __init__(self, pattern):
self.matcher = Matcher(pattern)

def start_suite(self, suite):
"""移除与给定模式匹配的测试。"""
suite.tests = [t for t in suite.tests if not self._is_excluded(t)]

def _is_excluded(self, test):
return self.matcher.match(test.name) or self.matcher.match(test.longname)

def end_suite(self, suite):
"""移除测试后为空的套件。"""
suite.suites = [s for s in suite.suites if s.test_count > 0]

def visit_test(self, test):
"""避免访问测试及其关键字以节省一些时间。"""
pass

假设上述修改器在名为 ExcludeTests.py 的文件中,那么可以这样使用:

1
2
3
4
5
# 排除名为 'Example' 的测试。
robot --prerunmodifier path/to/ExcludeTests.py:Example tests.robot

# 排除所有以 'something' 结尾的测试。
robot --prerunmodifier path/to/ExcludeTests.py:*something tests.robot

示例:禁用设置和拆卸

有时在调试测试时,禁用设置或拆卸可能很有用。这可以通过编辑测试数据来实现,但预运行修改器使得临时进行单次运行变得容易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"""禁用套件和测试设置和拆卸的预运行修改器。"""

from robot.api import SuiteVisitor

class SuiteSetup(SuiteVisitor):

def start_suite(self, suite):
suite.setup = None

class SuiteTeardown(SuiteVisitor):

def start_suite(self, suite):
suite.teardown = None

class TestSetup(SuiteVisitor):

def start_test(self, test):
test.setup = None

class TestTeardown(SuiteVisitor):

def start_test(self, test):
test.teardown = None

假设上述修改器都在名为 disable.py 的文件中,并且此文件在模块搜索路径中,可以按以下方式禁用设置和拆卸:

1
2
3
4
5
# 禁用套件拆卸。
robot --prerunmodifier disable.SuiteTeardown tests.robot

# 通过两次使用 '--prerunmodifier' 来禁用测试设置和拆卸。
robot --prerunmodifier disable.TestSetup --prerunmodifier disable.TestTeardown tests.robot

注意

在 Robot Framework 4.0 之前,通过中间关键字属性访问设置和拆卸,例如,套件设置被禁用像 suite.keywords.setup = None。

控制控制台输出

有多种命令行选项可以控制如何在控制台上报告测试执行情况。

控制台输出类型

使用 –console 选项设置整体控制台输出类型。它支持以下不区分大小写的值:

  • verbose:每个测试套件和测试用例都单独报告。这是默认设置。
  • dotted:只显示 . 表示通过的测试,F 表示失败的测试,s 表示跳过的测试,x 表示因测试执行退出而跳过的测试。执行后,失败的测试会单独列出。此输出类型使得即使有很多测试,也能轻松看到执行过程中是否有任何失败。
  • quiet:除错误和警告外,没有任何输出。
  • none:完全没有输出。在创建自定义输出时(例如,使用监听器)很有用。

单独的便利选项 –dotted (-.) 和 –quiet 是 –console dotted 和 –console quiet 的快捷方式。

示例:

1
2
robot --console quiet tests.robot
robot --dotted tests.robot
控制台宽度

可以使用 –consolewidth (-W) 选项设置控制台中测试执行输出的宽度。默认宽度为78个字符。

提示

在许多类 UNIX 机器上,可以使用方便的 $COLUMNS 环境变量,如 –consolewidth $COLUMNS。

控制台颜色

–consolecolors (-C) 选项用于控制是否应在控制台输出中使用颜色。颜色使用 ANSI 颜色实现,除了在 Windows 上,默认情况下使用 Windows API。

此选项支持以下不区分大小写的值:

  • auto:当输出写入控制台时启用颜色,但当它们被重定向到文件或其他地方时不启用。这是默认设置。
  • on:当输出被重定向时也使用颜色。在 Windows 上不起作用。
  • ansi:与 on 相同,但在 Windows 上也使用 ANSI 颜色。例如,当将输出重定向到理解 ANSI 颜色的程序时很有用。
  • off:禁用颜色。
控制台标记

当使用详细输出并在测试用例的顶级关键字结束时,控制台上会显示特殊标记 .(成功)和 F(失败)。标记允许在高级别跟踪测试执行,当测试用例结束时,它们会被擦除。

可以使用 –consolemarkers (-K) 选项配置何时使用标记。它支持以下不区分大小写的值:

  • auto:当标准输出写入控制台时启用标记,但当它被重定向到文件或其他地方时不启用。这是默认设置。
  • on:始终使用标记。
  • off:禁用标记。

设置监听器

监听器可以用来监视测试执行。当它们从命令行中使用时,它们是使用 –listener 命令行选项指定的。值可以是监听器的路径或监听器的名称。有关导入监听器和一般使用它们的更多详细信息,请参见监听器接口部分。

输出文件

当执行测试时,会创建多个输出文件,所有这些文件都与测试结果有关。本节讨论创建哪些输出,如何配置它们的创建位置,以及如何微调它们的内容。

不同的输出文件

本节解释可以创建哪些不同的输出文件以及如何配置它们的创建位置。输出文件是使用命令行选项配置的,这些选项获取作为参数的输出文件的路径。可以使用特殊值 NONE(不区分大小写)来禁用创建某个特定的输出文件。

输出目录

所有输出文件都可以使用绝对路径设置,在这种情况下,它们将被创建到指定的位置,但在其他情况下,路径被认为是相对于输出目录的。默认的输出目录是开始执行的目录,但可以使用 –outputdir (-d) 选项进行更改。此选项设置的路径再次相对于执行目录,但自然也可以作为绝对路径给出。无论如何获取到单个输出文件的路径,如果其父目录尚不存在,将自动创建。

输出文件

输出文件包含所有以机器可读的 XML 格式的测试执行结果。日志、报告和 xUnit 文件通常基于它们生成,并且它们也可以与 Rebot 一起进行组合和其他后处理。

信息

在测试执行的过程中生成报告和 xUnit 文件不需要在执行后处理输出文件。因此,运行测试时禁用日志生成可以节省内存。

命令行选项 –output (-o) 确定相对于输出目录创建输出文件的路径。当运行测试时,输出文件的默认名称是 output.xml。

使用 Rebot 对输出进行后处理时,除非明确使用了 –output 选项,否则不会创建新的输出文件。

在运行测试时,可以通过给 –output 选项赋予特殊值 NONE 来禁用创建输出文件。如果不需要任何输出,那么应该使用 –output NONE –report NONE –log NONE 明确禁用所有输出。

XML 输出文件结构在 robot.xsd 架构文件中有文档记录。

信息

从 Robot Framework 7.0 开始,Rebot 可以读取和写入 JSON 输出文件。计划是在未来增强对 JSON 输出文件的支持,以便它们可以在执行期间就创建。有关更多详细信息,请参阅问题 #3423。

旧版输出文件格式

在 Robot Framework 7.0 中,输出文件格式发生了一些向后不兼容的更改。为了使得可以使用新版本的 Robot Framework 与尚未更新以支持新格式的外部工具一起使用,有一个 –legacyoutput 选项,该选项生成与 Robot Framework 6.x 及更早版本兼容的输出文件。Robot Framework 本身可以处理旧格式和新格式的输出文件。

日志文件

日志文件以 HTML 格式包含有关执行的测试用例的详细信息。它们具有层次结构,显示测试套件、测试用例和关键字详细信息。几乎每次需要详细调查测试结果时,都需要日志文件。尽管日志文件也有统计信息,但报告更适合获取更高级别的概述。

命令行选项 --log (-l) 确定创建日志文件的位置。除非使用特殊值 NONE,否则总是会创建日志文件,其默认名称为 log.html

示例日志文件的开始:

示例日志文件,可见关键字详细信息:

示例日志文件,跳过和通过的测试:

报告文件

报告文件以 HTML 格式包含测试执行结果的概述。它们基于标签和执行的测试套件有统计信息,以及所有执行的测试用例的列表。当生成报告和日志时,报告有链接到日志文件,以便轻松导航到更详细的信息。从报告中很容易看到整体的测试执行状态,因为如果所有测试都通过,其背景颜色为绿色,如果有任何测试失败,背景颜色为亮红色。背景也可以是黄色,这意味着所有测试都被跳过。

命令行选项 –report (-r) 确定创建报告文件的位置。与日志文件类似,除非使用 NONE 作为值,否则总是会创建报告,其默认名称为 report.html。

成功测试执行的示例报告文件:

失败测试执行的示例报告文件:

XUnit 兼容结果文件

XUnit 结果文件包含以 XUnit 兼容的 XML 格式的测试执行摘要。因此,这些文件可以作为理解 XUnit 报告的外部工具的输入。例如,Jenkins 持续集成服务器支持基于 XUnit 兼容结果生成统计信息。

提示

Jenkins 还有一个单独的 Robot Framework 插件。

除非明确使用命令行选项 –xunit (-x),否则不会创建 XUnit 输出文件。此选项需要一个路径作为值,该路径是相对于输出目录的生成的 XUnit 文件。

在 Robot Framework 5.0 中,XUnit 输出文件发生了重大变化。它们现在包含每个套件的单独的 元素, 元素具有时间戳属性,套件文档和元数据存储为 元素。

调试文件

调试文件是在测试执行期间编写的纯文本文件。所有从测试库获取的消息都写入它们,以及关于开始和结束的测试套件、测试用例和关键字的信息。调试文件可以用于监视测试执行。这可以使用,例如,一个单独的 fileviewer.py 工具,或者在类 UNIX 系统中,简单地使用 tail -f 命令。

除非明确使用命令行选项 –debugfile (-b),否则不会创建调试文件。

时间戳输出文件

Robot Framework 本身生成的所有输出文件都可以使用选项 –timestampoutputs (-T) 自动添加时间戳。当使用此选项时,将在每个文件的扩展名和基本名称之间放置一个格式为 YYYYMMDD-hhmmss 的时间戳。例如,下面的示例将创建像 output-20080604-163225.xml 和 mylog-20080604-163225.html 这样的输出文件:

1
robot --timestampoutputs --log mylog.html --report NONE tests.robot
设置标题

日志和报告的默认标题是通过在顶级测试套件的名称前加上 Test Log 或 Test Report 来生成的。可以使用选项 –logtitle 和 –reporttitle 分别从命令行给出自定义标题。

示例:

1
robot --logtitle "Smoke Test Log" --reporttitle "Smoke Test Report" --include smoke my_tests/

注意

在 Robot Framework 3.1 之前,给定标题中的下划线被转换为空格。现在,空格需要像上面的示例那样被转义或引用。

设置背景颜色

默认情况下,如果有失败,报告文件的背景为红色,如果有通过的测试和可能跳过的一些测试,背景为绿色,如果所有测试都被跳过或没有运行任何测试,背景为黄色。可以使用 –reportbackground 命令行选项自定义这些颜色,该选项接受两个或三个用冒号分隔的颜色作为参数:

1
2
3
--reportbackground blue:red
--reportbackground blue:red:orange
--reportbackground #00E:#E00

如果指定了两种颜色,第一种将替代默认的绿色(通过)颜色,第二种将替代默认的红色(失败)。这允许,例如,使用蓝色代替绿色,使得背景更容易为色盲人士分辨。

如果指定了三种颜色,前两种具有与以前相同的语义,最后一种替换默认的黄色(跳过)颜色。

指定的颜色用作 body 元素的背景 CSS 属性的值。该值按原样使用,可以是 HTML 颜色名称(例如,red)、十六进制值(例如,#f00 或 #ff0000)或 RGB 值(例如,rgb(255,0,0))。默认的绿色、红色和黄色分别使用十六进制值 #9e9、#f66 和 #fed84f 指定。

日志级别

可用的日志级别

日志文件中的消息可以有不同的日志级别。一些消息是由 Robot Framework 本身编写的,但也可以执行关键字以使用不同级别的信息进行记录。可用的日志级别有:

  • FAIL:当关键字失败时使用。只能由 Robot Framework 本身使用。
  • WARN:用于显示警告。它们也会在控制台和日志文件的测试执行错误部分中显示,但它们不会影响测试用例的状态。
  • INFO:正常消息的默认级别。默认情况下,此级别以下的消息不会显示在日志文件中。
  • DEBUG:用于调试目的。例如,对于记录库在内部正在做什么很有用。当关键字失败时,将自动使用此级别记录一个回溯,显示代码中失败发生的位置。
  • TRACE:更详细的调试级别。关键字参数和返回值会自动使用此级别进行记录。

设置日志级别

默认情况下,INFO 级别以下的日志消息不会被记录,但可以使用 –loglevel (-L) 选项从命令行更改此阈值。此选项接受任何可用的日志级别作为参数,该级别成为新的阈值级别。还可以使用特殊值 NONE 来完全禁用日志记录。

也可以在使用 Rebot 对输出进行后处理时使用 –loglevel 选项。这允许,例如,首先使用 TRACE 级别运行测试,然后使用 INFO 级别生成较小的日志文件以供正常查看。默认情况下,执行期间包含的所有消息也将包含在 Rebot 中。执行期间忽略的消息无法恢复。

更改日志级别的另一种可能性是在测试数据中使用 BuiltIn 关键字 Set Log Level。它接受与 –loglevel 选项相同的参数,并且它还返回旧的级别,以便稍后可以恢复,例如,在测试拆卸中。

可见的日志级别

如果日志文件包含 DEBUG 或 TRACE 级别的消息,则在右上角显示一个可见的日志级别下拉菜单。这允许用户从视图中移除所选级别以下的消息。这在运行 TRACE 级别的测试时尤其有用。

默认情况下,下拉菜单将设置为日志文件中的最低级别,因此所有消息都会显示。可以使用 –loglevel 选项更改默认的可见日志级别,方法是在正常日志级别后面用冒号给出默认值:

1
--loglevel DEBUG:INFO

在上面的示例中,测试使用 DEBUG 级别运行,但日志文件中的默认可见级别是 INFO。

分割日志

通常,日志文件只是一个单独的 HTML 文件。当测试用例的数量增加时,文件的大小可能会变得如此大,以至于在浏览器中打开它变得不便或甚至不可能。因此,可以使用 –splitlog 选项将日志的部分内容分割到需要时可以透明地加载到浏览器中的外部文件中。

分割日志的主要好处是,单个日志部分非常小,即使测试数据的数量非常大,也可以打开和浏览日志文件。一个小缺点是日志文件占用的总大小会增加。

从技术上讲,与每个测试用例相关的测试数据被保存到与主日志文件相同的文件夹中的 JavaScript 文件中。这些文件的名称如 log-42.js,其中 log 是主日志文件的基本名称,42 是递增的索引。

注意

在复制日志文件时,需要复制所有的 log-*.js 文件,否则一些信息将会丢失。

配置统计信息

有几个命令行选项可以用来配置和调整不同输出文件中的“按标签统计”、“按套件统计”和“按标签详细测试”表格的内容。所有这些选项都在执行测试用例和后处理输出时起作用。

配置显示的套件统计信息

当执行更深层次的套件结构时,在“按套件统计”表中显示所有测试套件级别可能会使表格有些难以阅读。默认情况下显示所有套件,但可以使用命令行选项 –suitestatlevel 控制显示的套件级别,该选项接受要显示的套件级别作为参数:

1
--suitestatlevel 3
包含和排除标签统计信息

当使用许多标签时,“按标签统计”表可能会变得相当拥挤。如果发生这种情况,可以使用命令行选项 –tagstatinclude 和 –tagstatexclude 来选择要显示的标签,类似于使用 –include 和 –exclude 来选择测试用例:

1
2
3
--tagstatinclude some-tag --tagstatinclude another-tag
--tagstatexclude owner-*
--tagstatinclude prefix-* --tagstatexclude prefix-13
生成组合标签统计信息

命令行选项 –tagstatcombine 可用于生成聚合标签,这些标签将多个标签的统计信息组合在一起。组合标签使用标签模式指定,其中 * 和 ? 作为通配符受支持,AND、OR 和 NOT 运算符可用于将单个标签或模式组合在一起。

以下示例说明了使用不同模式创建组合标签统计信息,下面的图表显示了生成的“按标签统计”表的一部分:

1
2
3
--tagstatcombine owner-*
--tagstatcombine smokeANDmytag
--tagstatcombine smokeNOTowner-janne*

如上例所示,默认情况下,添加的组合统计的名称只是给定的模式。如果这还不够好,可以在模式后面用冒号 (:) 分隔给出自定义名称:

1
--tagstatcombine "prio1ORprio2:High priority tests"

注意

在 Robot Framework 3.1 之前,自定义名称中的下划线被转换为空格。现在,空格需要像上面的示例那样被转义或引用。

从标签名称创建链接

可以使用命令行选项 –tagstatlink 向“按标签统计”表添加外部链接。此选项的参数以 tag:link:name 的格式给出,其中 tag 指定要将链接分配给的标签,link 是要创建的链接,name 是要给链接的名称。

tag 可能是一个单一的标签,但更常见的是一个简单的模式,其中 * 匹配任何内容,? 匹配任何单个字符。当 tag 是一个模式时,可以使用语法 %N 在链接和标题中使用通配符的匹配,其中 “N” 是从 1 开始的匹配索引。

以下示例说明了此选项的使用,下面的图表显示了当使用这些选项执行示例测试数据时,生成的“按标签统计”表的一部分:

1
2
3
--tagstatlink mytag:http://www.google.com:Google
--tagstatlink example-bug-*:http://example.com
--tagstatlink owner-*:mailto:%[email protected]?subject=Acceptance_Tests:Send_Mail
为标签添加文档

可以使用命令行选项 –tagdoc 给标签添加文档,该选项接受以 tag:doc 格式的参数。tag 是要分配文档的标签的名称,它也可以是匹配多个标签的简单模式。doc 是分配的文档。它可以包含简单的 HTML 格式。

给定的文档将在“按标签详细测试”表中与匹配的标签一起显示,并作为这些标签在“按标签统计”表中的工具提示显示。如果一个标签获得多个文档,它们将被组合在一起,并用和号分隔。

示例:

1
2
3
--tagdoc mytag:Example
--tagdoc "regression:*See* http://info.html"
--tagdoc "owner-*:Original author"

注意

在 Robot Framework 3.1 之前,文档中的下划线被转换为空格。现在,空格需要像上面的示例那样被转义或引用。

移除和展平关键字

输出文件的大部分内容来自关键字及其日志消息。当创建更高级别的报告时,可能根本不需要日志文件,此时关键字及其消息只会无谓地占用空间。日志文件本身也可能变得过大,尤其是如果它们包含 FOR 循环或其他重复某些关键字多次的结构。

在这些情况下,可以使用命令行选项 –removekeywords 和 –flattenkeywords 来丢弃或展平不必要的关键字。它们可以在执行测试用例和后处理输出时使用。在执行期间使用时,它们只影响日志文件,而不影响 XML 输出文件。使用 rebot 时,它们会影响日志和可能生成的新的输出 XML 文件。

移除关键字

–removekeywords 选项完全移除关键字及其消息。它有以下操作模式,可以多次使用以启用多个模式。包含错误或警告的关键字不会被移除,除非使用 ALL 模式。

  • ALL:无条件地从所有关键字中移除数据。
  • PASSED:从通过的测试用例中移除关键字数据。在大多数情况下,使用此选项创建的日志文件包含足够的信息来调查可能的失败。
  • FOR:除最后一个之外,从 FOR 循环中移除所有通过的迭代。
  • WHILE:除最后一个之外,从 WHILE 循环中移除所有通过的迭代。
  • WUKS:除最后一个之外,从 BuiltIn 关键字 Wait Until Keyword Succeeds 中移除所有失败的关键字。
  • **NAME:**:无论关键字状态如何,都从与给定模式匹配的所有关键字中移除数据。该模式与关键字的全名匹配,前缀为可能的库或资源文件名称,如 MyLibrary.Keyword Name。该模式对大小写、空格和下划线不敏感,并支持使用 *, ? 和 [] 作为通配符的简单模式。
  • **TAG:**:从标签与给定模式匹配的关键字中移除数据。标签对大小写和空格不敏感,可以使用标签模式指定,其中 *, ? 和 [] 作为通配符受支持,AND、OR 和 NOT 运算符可以用于将单个标签或模式组合在一起。可以同时使用库关键字标签和用户关键字标签。

示例:

1
2
3
4
rebot --removekeywords all --output removed.xml output.xml
robot --removekeywords passed --removekeywords for tests.robot
robot --removekeywords name:HugeKeyword --removekeywords name:resource.* tests.robot
robot --removekeywords tag:huge tests.robot

移除关键字是在解析输出文件并基于它生成内部模型之后完成的。因此,它并不像展平关键字那样减少内存使用。

展平关键字

–flattenkeywords 选项会展平匹配的关键字。实际上,这意味着匹配的关键字会递归地获取其子关键字的所有日志消息,而子关键字则被否则丢弃。展平支持以下模式:

  • FOR:完全展平 FOR 循环。
  • WHILE:完全展平 WHILE 循环。
  • ITERATION:展平单个 FOR 和 WHILE 循环的迭代。
  • FORITEM:ITERATION 的已弃用别名。
  • **NAME:**:展平与给定模式匹配的关键字。模式匹配规则与使用 NAME: 模式移除关键字时相同。
  • **TAG:**:展平标签与给定模式匹配的关键字。模式匹配规则与使用 TAG: 模式移除关键字时相同。

示例:

1
2
robot --flattenkeywords name:HugeKeyword --flattenkeywords name:resource.* tests.robot
rebot --flattenkeywords foritem --output flattened.xml original.xml

展平关键字是在最初解析输出文件并基于它生成内部模型时完成的。这可以节省大量的内存,尤其是在关键字结构深度嵌套的情况下。

在执行时间内展平关键字

从 Robot Framework 6.1 开始,可以在执行时间内启用关键字展平。这只能在用户关键字级别上通过定义保留标签 robot:flatten 作为关键字标签来完成。使用此标签的工作方式与前一章中描述的命令行选项类似,例如,除日志消息外,所有内容都从具有该标签的关键字下移除。一个重要的区别是,在这种情况下,移除的内容根本不会写入输出文件,因此无法在以后的时间内访问。

一些示例

1
2
3
4
5
6
7
8
9
10
11
*** Keywords ***
Flattening affects this keyword and all it's children
[Tags] robot:flatten
Log something
FOR ${i} IN RANGE 2
Log The message is preserved but for loop iteration is not
END

*** Settings ***
# Flatten content of all uer keywords
Keyword Tags robot:flatten

自动展开关键字

默认情况下,通过的关键字在日志文件中是关闭的。因此,除非展开它们,否则它们包含的信息不可见。如果某些关键字有重要的信息,应在打开日志文件时可见,可以使用 –expandkeywords 选项将关键字设置为在日志文件中自动展开,类似于失败的关键字。展开支持以下模式:

  • **NAME:**:展开与给定模式匹配的关键字。模式匹配规则与使用 NAME: 模式移除关键字时相同。
  • **TAG:**:展开标签与给定模式匹配的关键字。模式匹配规则与使用 TAG: 模式移除关键字时相同。

如果需要展开匹配不同名称或模式的关键字,可以多次使用 –expandkeywords。

示例:

1
2
robot --expandkeywords name:SeleniumLibrary.CapturePageScreenshot tests.robot
rebot --expandkeywords tag:example --expandkeywords tag:another output.xml

注意

–expandkeywords 选项是在 Robot Framework 3.2 中新增的。

设置执行的开始和结束时间

当使用 Rebot 合并输出时,可以使用选项 –starttime 和 –endtime 分别设置合并测试套件的开始和结束时间。这很方便,因为默认情况下,合并的套件没有这些值。当给出开始和结束时间时,也会根据它们计算经过的时间。否则,经过的时间是通过将子测试套件的经过时间加在一起得到的。

也可以使用上述选项在使用 Rebot 时为单个套件设置开始和结束时间。对单个输出使用这些选项总是会影响套件的经过时间。

时间必须以时间戳的格式给出,格式为 YYYY-MM-DD hh:mm:ss.mil,其中所有分隔符都是可选的,可以省略从毫秒到小时的部分。例如,2008-06-11 17:59:20.495 等同于 20080611-175920.495 和 20080611175920495,而仅仅是 20080611 也可以工作。

示例:

1
2
3
rebot --starttime 20080611-17:59:20.495 output1.xml output2.xml
rebot --starttime 20080611-175920 --endtime 20080611-180242 *.xml
rebot --starttime 20110302-1317 --endtime 20110302-11418 myoutput.xml

限制报告中错误消息的长度

如果一个测试用例失败并且有一个长的错误消息,报告中显示的消息会自动从中间剪切,以保持报告更易于阅读。默认情况下,超过 40 行的消息会被剪切,但可以使用 –maxerrorlines 命令行选项进行配置。此选项的最小值为 10,也可以使用特殊值 NONE 来显示完整的消息。

完整的错误消息始终作为失败关键字的消息在日志文件中可见。

注意

–maxerrorlines 选项是在 Robot Framework 3.1 中新增的。

编程修改结果

如果提供的内置功能不足以修改结果,Robot Framework 使得可以通过编程方式进行自定义修改。这是通过创建一个模型修改器并使用 –prerebotmodifier 选项激活它来实现的。

此功能几乎完全像可以使用 –prerunmodifier 选项启用的测试数据的编程修改一样工作。明显的区别是这次修改器操作的是结果模型,而不是运行模型。例如,以下修改器将所有超过允许时间的通过测试标记为失败:

1
2
3
4
5
6
7
8
9
10
11
from robot.api import SuiteVisitor

class ExecutionTimeChecker(SuiteVisitor):

def __init__(self, max_seconds: float):
self.max_milliseconds = max_seconds * 1000

def visit_test(self, test):
if test.status == 'PASS' and test.elapsedtime > self.max_milliseconds:
test.status = 'FAIL'
test.message = 'Test execution took too long.'

如果上述修改器位于 ExecutionTimeChecker.py 文件中,例如可以这样使用它:

1
2
3
4
5
6
# 在运行测试时指定修改器作为路径。最大时间是42秒。
robot --prerebotmodifier path/to/ExecutionTimeChecker.py:42 tests.robot

# 在使用 Rebot 时以名称指定修改器。最大时间是3.14秒。
# ExecutionTimeChecker.py 必须在模块搜索路径中。
rebot --prerebotmodifier ExecutionTimeChecker:3.14 output.xml

如果需要多个模型修改器,可以通过多次使用 –prerebotmodifier 选项来指定它们。在执行测试时,可以同时使用 –prerunmodifier 和 –prerebotmodifier 选项。

注意

基于类型提示(如上例中的 max_seconds: float)的参数转换是在 Robot Framework 4.0 中新增的,并且需要 Python 3。

系统日志

Robot Framework 有自己的纯文本系统日志,其中写入了关于以下信息:

  • 处理和跳过的测试数据文件
  • 导入的测试库、资源文件和变量文件
  • 执行的测试套件和测试用例
  • 创建的输出

通常,用户永远不需要这些信息,但在调查测试库或 Robot Framework 本身的问题时,它可能会有用。默认情况下不会创建系统日志,但可以通过设置环境变量 ROBOT_SYSLOG_FILE 以包含所选文件的路径来启用它。

系统日志具有与普通日志文件相同的日志级别,但除了 FAIL,它具有 ERROR 级别。可以使用 ROBOT_SYSLOG_LEVEL 环境变量更改要使用的阈值级别,如下例所示。可能的意外错误和警告除了在控制台和普通日志文件中写入外,还写入系统日志。

1
2
3
4
5
6
#!/bin/bash

export ROBOT_SYSLOG_FILE=/tmp/syslog.txt
export ROBOT_SYSLOG_LEVEL=DEBUG

robot --name Syslog_example path/to/tests

扩展 Robot Framework

创建测试库

Robot Framework 的实际测试能力由测试库提供。有许多现有的库,其中一些甚至与核心框架捆绑在一起,但仍然经常需要创建新的库。这项任务并不复杂,因为如本章所示,Robot Framework 的库 API 是简单直接的。

介绍

支持的编程语言

Robot Framework 本身是用 Python 编写的,自然地,扩展它的测试库可以使用相同的语言实现。也可以使用 Python C API 用 C 实现库,尽管通常使用 ctypes 模块从 Python 库与 C 代码交互更容易。

使用 Python 实现的库也可以作为包装器来实现使用其他编程语言实现的功能。这种方法的一个好例子是 Remote 库,另一个广泛使用的方法是作为单独的进程运行外部脚本或工具。

不同的测试库 API

Robot Framework 有三种不同的测试库 API。

  • 静态 API:最简单的方法是拥有直接映射到关键字名称的函数/方法的模块或类。关键字也接受与实现它们的方法相同的参数。关键字通过异常报告失败,通过写入标准输出进行日志记录,并可以使用 return 语句返回值。
  • 动态 API:动态库是实现一个方法以获取它们实现的关键字的名称,以及另一个方法以给定的参数执行一个命名的关键字的类。关键字的名称以及如何执行它们可以在运行时动态确定,但报告状态、日志记录和返回值的方式与静态 API 中的方式相同。
  • 混合 API:这是静态 API 和动态 API 之间的混合。库是具有告知它们实现了哪些关键字的方法的类,但这些关键字必须直接可用。除了发现实现了哪些关键字之外,其他所有事情都与静态 API 相似。

本章将描述所有这些 API。一切都基于静态 API 的工作方式,因此首先讨论其功能。然后在各自的部分中讨论动态库 API 和混合库 API 与其的区别。

创建测试库类或模块

测试库可以实现为 Python 模块或类。

库名称

当导入库时使用的测试库的名称与实现它的模块或类的名称相同。例如,如果有一个 Python 模块 MyLibrary(即,文件 MyLibrary.py),它将创建一个名为 MyLibrary 的库。

Python 类总是在模块内部。如果实现库的类的名称与模块的名称相同,Robot Framework 允许在导入库时省略类名。例如,MyLib.py 文件中的类 MyLib 可以仅用名为 MyLib 的库。这也适用于子模块,因此,例如,如果 parent.MyLib 模块有类 MyLib,那么只使用 parent.MyLib 导入它就可以了。如果模块名称和类名称不同,必须使用模块和类名称来使用库,例如 mymodule.MyLibrary 或 parent.submodule.MyLib。

信息

如果库名称真的很长,建议使用 AS 给库一个更简单的别名。

提供库的参数

所有作为类实现的测试库都可以接受参数。这些参数在库名称后的 Setting 部分中指定,当 Robot Framework 创建导入库的实例时,它将它们传递给其构造函数。作为模块实现的库不能接受任何参数,因此尝试使用这些结果会出错。

库所需的参数数量与库的构造函数接受的参数数量相同。默认值和变量参数的数量与关键字参数的工作方式类似。传递给库的参数,以及库名称本身,可以使用变量指定,因此可以例如从命令行更改它们。

1
2
3
*** Settings ***
Library MyLibrary 10.0.0.1 8080
Library AnotherLib ${VAR}

用于上述示例的库的示例实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from example import Connection

class MyLibrary:

def __init__(self, host, port=80):
self._conn = Connection(host, int(port))

def send_message(self, message):
self._conn.send(message)
class AnotherLib:

def __init__(self, environment):
self.environment = environment

def do_something(self):
if self.environment == 'test':
# 在测试环境中做一些事情
else:
# 在其他环境中做一些事情

库范围

作为类实现的库可以有一个内部状态,可以通过关键字和库的构造函数的参数来改变。因为状态可以影响关键字的实际行为,所以确保一个测试用例中的更改不会意外地影响其他测试用例是很重要的。这种类型的依赖关系可能会创建难以调试的问题,例如,当添加新的测试用例并且它们不一致地使用库时。

Robot Framework 尝试保持测试用例彼此独立:默认情况下,它为每个测试用例创建新的测试库实例。然而,这种行为并不总是可取的,因为有时测试用例应该能够共享一个公共状态。此外,并非所有的库都有状态,创建它们的新实例根本就不需要。

测试库可以使用类属性 ROBOT_LIBRARY_SCOPE 控制何时创建新的库。此属性必须是一个字符串,可以有以下三个值:

  • TEST:为每个测试用例创建一个新实例。可能的套件设置和套件拆卸共享另一个实例。

在 Robot Framework 3.2 之前,此值为 TEST CASE,但现在推荐使用 TEST。因为所有无法识别的值都被视为与 TEST 相同,所以两个值在所有版本中都可以工作。出于同样的原因,如果库更多地针对 RPA 使用而不是测试,也可以使用 TASK 值。如果未设置 ROBOT_LIBRARY_SCOPE 属性,TEST 也是默认值。

  • SUITE:为每个测试套件创建一个新实例。最低级别的测试套件(从测试用例文件创建并包含测试用例)有自己的实例,更高级别的套件都有自己的实例,用于可能的设置和拆卸。

在 Robot Framework 3.2 之前,此值为 TEST SUITE。该值仍然有效,但推荐使用 SUITE,适用于针对 Robot Framework 3.2 及更高版本的库。

  • GLOBAL:在整个测试执行过程中只创建一个实例,并由所有测试用例和测试套件共享。从模块创建的库始终是全局的。

注意

如果一个库被多次导入并带有不同的参数,那么无论范围如何,每次都会创建一个新实例。

当使用具有状态的库使用 SUITE 或 GLOBAL 范围时,建议库有一些特殊的关键字用于清理状态。然后,这个关键字可以在套件设置或拆卸中使用,以确保下一个测试套件中的测试用例可以从已知状态开始。例如,SeleniumLibrary 使用 GLOBAL 范围来启用在不同的测试用例中使用相同的浏览器,而无需重新打开它,它还有 Close All Browsers 关键字用于轻松关闭所有打开的浏览器。

使用 SUITE 范围的示例库:

1
2
3
4
5
6
7
8
9
10
11
12
class ExampleLibrary:
ROBOT_LIBRARY_SCOPE = 'SUITE'

def __init__(self):
self._counter = 0

def count(self):
self._counter += 1
print(self._counter)

def clear_counter(self):
self._counter = 0
库版本

当一个测试库被使用时,Robot Framework 尝试确定其版本。然后,这些信息被写入系统日志以提供调试信息。库文档工具 Libdoc 也将这些信息写入它生成的关键字文档。

版本信息是从属性 ROBOT_LIBRARY_VERSION 中读取的,就像从 ROBOT_LIBRARY_SCOPE 中读取库范围一样。如果 ROBOT_LIBRARY_VERSION 不存在,将尝试从 version 属性中读取信息。这些属性必须是类或模块属性,取决于库是作为类还是模块实现的。

使用 version 的示例模块:

1
2
3
4
__version__ = '0.1'

def keyword():
pass

文档格式

库文档工具 Libdoc 支持多种格式的文档。如果想使用除 Robot Framework 自己的文档格式以外的其他格式,可以在源代码中使用 ROBOT_LIBRARY_DOC_FORMAT 属性指定格式,就像使用自己的 ROBOT_LIBRARY_* 属性设置范围和版本一样。

文档格式的可能值(不区分大小写)为 ROBOT(默认)、HTML、TEXT(纯文本)和 reST(reStructuredText)。使用 reST 格式需要在生成文档时安装 docutils 模块。

以下示例说明了如何设置使用 reStructuredText 格式的文档格式。有关一般性地记录测试库的更多信息,请参见文档库部分和 Libdoc 章节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""一个用于*文档格式*演示目的的库。

此文档是使用 reStructuredText__ 创建的。这里有一个链接
到唯一的\`Keyword\`。

__ http://docutils.sourceforge.net
"""

ROBOT_LIBRARY_DOC_FORMAT = 'reST'


def keyword():
"""**这里什么都没有**。甚至下面的表格也没有。

======= ===== =====
Table here has
nothing to see.
======= ===== =====
"""
pass
库充当监听器

监听器接口允许外部监听器获取关于测试执行的通知。例如,当套件、测试和关键字开始和结束时,它们会被调用。有时,获取这样的通知对于测试库也是有用的,它们可以使用 ROBOT_LIBRARY_LISTENER 属性注册一个自定义监听器。此属性的值应为要使用的监听器的实例,可能是库本身。

有关更多信息和示例,请参见 Libraries as listeners 部分。

@library 装饰器

配置作为类实现的库的一种简单方法是使用 robot.api.deco.library 类装饰器。它允许使用可选参数 scope、version、converter、doc_format 和 listener 分别配置库的范围、版本、自定义参数转换器、文档格式和监听器。当使用这些参数时,它们会自动设置匹配的 ROBOT_LIBRARY_SCOPE、ROBOT_LIBRARY_VERSION、ROBOT_LIBRARY_CONVERTERS、ROBOT_LIBRARY_DOC_FORMAT 和 ROBOT_LIBRARY_LISTENER 属性:

1
2
3
4
5
6
7
8
from robot.api.deco import library

from example import Listener


@library(scope='GLOBAL', version='3.2b1', doc_format='reST', listener=Listener())
class Example:
# ...

@library 装饰器还通过默认将 ROBOT_AUTO_KEYWORDS 参数设置为 False 来禁用自动关键字发现。这意味着必须使用 @keyword 装饰器装饰方法以将它们公开为关键字。如果只需要那种行为并且不需要进一步的配置,装饰器也可以像这样不带括号使用:

1
2
3
4
5
6
from robot.api.deco import library


@library
class Example:
# ...

如果需要,可以使用 auto_keywords 参数启用自动关键字发现:

1
2
3
4
5
6
from robot.api.deco import library


@library(scope='GLOBAL', auto_keywords=True)
class Example:
# ...

@library 装饰器只有在使用相应的参数 scope、version、converters、doc_format 和 listener 时才设置类属性 ROBOT_LIBRARY_SCOPE、ROBOT_LIBRARY_VERSION、ROBOT_LIBRARY_CONVERTERS、ROBOT_LIBRARY_DOC_FORMAT 和 ROBOT_LIBRARY_LISTENER。ROBOT_AUTO_KEYWORDS 属性始终被设置。当设置属性时,它们会覆盖可能存在的类属性。

注意

@library 装饰器是在 Robot Framework 3.2 中新增的,converters 参数是在 Robot Framework 5.0 中新增的。

创建关键字

哪些方法被视为关键字

当使用静态库 API 时,Robot Framework 使用内省来找出库类或模块实现了哪些关键字。默认情况下,它排除了以下划线开头的方法和函数。所有未被忽略的方法和函数都被视为关键字。例如,下面的库实现了一个名为 My Keyword 的关键字。

1
2
3
4
5
6
7
class MyLibrary:

def my_keyword(self, arg):
return self._helper_method(arg)

def _helper_method(self, arg):
return arg.upper()

限制公共方法成为关键字

通常,自动将所有公共方法和函数视为关键字工作得很好,但有些情况下这是不希望的。还有一些情况下,当没有预期时会创建关键字。例如,当以类实现库时,可能会惊讶地发现可能的基类中的方法也被视为关键字。当以模块实现库时,导入到模块命名空间的函数变成关键字可能会更令人惊讶。

本节解释如何防止方法和函数成为关键字。

基于类的库

当库以类的形式实现时,可以通过将 ROBOT_AUTO_KEYWORDS 属性设置为具有 false 值的类,告诉 Robot Framework 不要自动将方法公开为关键字:

1
2
class Example:
ROBOT_AUTO_KEYWORDS = False

当像这样设置 ROBOT_AUTO_KEYWORDS 属性时,只有明确用 @keyword 装饰器装饰的方法或者具有 robot_name 属性的方法才会成为关键字。@keyword 装饰器也可以用于为关键字设置自定义名称、标签和参数类型。

尽管可以显式地将 ROBOT_AUTO_KEYWORDS 属性设置为类,但使用 @library 装饰器将其默认设置为 False 更方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from robot.api.deco import keyword, library


@library
class Example:

@keyword
def this_is_keyword(self):
pass

@keyword('This is keyword with custom name')
def xxx(self):
pass

def this_is_not_keyword(self):
pass

注意

使用 ROBOT_AUTO_KEYWORDS 属性和 @library 装饰器限制哪些方法成为关键字是在 Robot Framework 3.2 中新增的。

另一种明确指定库实现了哪些关键字的方法是使用动态或混合库 API。

基于模块的库

当以模块的形式实现库时,模块命名空间中的所有函数都会成为关键字。这也适用于导入的函数,这可能会引起令人不悦的惊喜。例如,如果下面的模块被用作库,它将包含一个预期的关键字 Example Keyword,但也会包含一个关键字 Current Thread。

1
2
3
4
5
from threading import current_thread


def example_keyword():
print('Running in thread "%s".' % current_thread().name)

避免导入的函数成为关键字的一种简单方法是只导入模块(例如,import threading)并通过模块使用函数(例如 threading.current_thread())。或者,可以在导入时给函数一个以下划线开头的别名(例如,from threading import current_thread as _current_thread)。

限制哪些函数成为关键字的更明确的方法是使用 Python 本身用于类似目的的模块级别的 all 属性。如果使用它,只有列出的函数可以是关键字。例如,下面的库只实现了一个关键字 Example Keyword:

1
2
3
4
5
6
7
8
9
10
11
from threading import current_thread


__all__ = ['example_keyword']


def example_keyword():
print('Running in thread "%s".' % current_thread().name)

def this_is_not_keyword():
pass

如果库很大,当添加、删除或重命名关键字时,维护 all 属性可能是一项相当大的任务。另一种明确标记哪些函数是关键字的方法是使用 ROBOT_AUTO_KEYWORDS 属性,就像它可以用于基于类的库一样。当此属性设置为 false 值时,只有明确用 @keyword 装饰器装饰的函数才会成为关键字。例如,这个库也只实现了一个关键字 Example Keyword:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from threading import current_thread

from robot.api.deco import keyword


ROBOT_AUTO_KEYWORDS = False


@keyword
def example_keyword():
print('Running in thread "%s".' % current_thread().name)

def this_is_not_keyword():
pass

注意

使用 ROBOT_AUTO_KEYWORDS 限制哪些函数成为关键字是 Robot Framework 3.2 中的新功能。

使用 @not_keyword 装饰器

可以使用 @not_keyword 装饰器显式地将模块中的函数和类中的方法标记为“非关键字”。当库以模块的形式实现时,这个装饰器也可以用来避免导入的函数成为关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from threading import current_thread

from robot.api.deco import not_keyword


not_keyword(current_thread) # Don't expose `current_thread` as a keyword.


def example_keyword():
print('Running in thread "%s".' % current_thread().name)

@not_keyword
def this_is_not_keyword():
pass

使用 @not_keyword 装饰器避免函数或方法成为关键字的方式几乎与使用 @library 装饰器禁用自动关键字发现或将 ROBOT_AUTO_KEYWORDS 设置为 false 值的方式相反。使用哪一个取决于上下文。

注意

@not_keyword 装饰器是 Robot Framework 3.2 中的新功能。

关键字名称

在测试数据中使用的关键字名称与方法名称进行比较,以找到实现这些关键字的方法。名称比较不区分大小写,也忽略空格和下划线。例如,方法 hello 映射到关键字名称 Hello、hello 或甚至 h e l l o。同样,do_nothing 和 doNothing 方法都可以在测试数据中用作 Do Nothing 关键字。

以下是以模块形式实现的示例库 MyLibrary.py 文件:

1
2
3
4
5
def hello(name):
print("Hello, %s!" % name)

def do_nothing():
pass

下面的示例说明了如何使用上面的示例库。如果想自己试试,确保库在模块搜索路径中。

1
2
3
4
5
6
7
*** Settings ***
Library MyLibrary

*** Test Cases ***
My Test
Do Nothing
Hello world
设置自定义名称

可以为关键字公开一个不同的名称,而不是默认的关键字名称,该名称映射到方法名称。这可以通过将方法的 robot_name 属性设置为所需的自定义名称来实现:

1
2
3
4
def login(username, password):
# ...

login.robot_name = 'Login via user panel'

*** Test Cases *** My Test Login Via User Panel {username}username{password} 与上述示例中显式设置 robot_name 属性不同,通常最容易使用 @keyword 装饰器:

1
2
3
4
5
6
from robot.api.deco import keyword


@keyword('Login via user panel')
def login(username, password):
# ...

如果没有参数使用此装饰器,将对公开的关键字名称没有影响,但仍会设置 robot_name 属性。这允许标记方法以公开为关键字,而实际上不改变关键字名称。具有 robot_name 属性的方法也会创建关键字,即使方法名称本身以下划线开头。

设置自定义关键字名称还可以启用库关键字使用嵌入式参数语法接受参数。

关键字标签

库关键字和用户关键字可以有标签。库关键字可以通过在方法上设置 robot_tags 属性为所需标签的列表来定义它们。与设置自定义名称一样,最容易使用 @keyword 装饰器设置此属性:

1
2
3
4
5
6
7
8
9
10
from robot.api.deco import keyword


@keyword(tags=['tag1', 'tag2'])
def login(username, password):
# ...

@keyword('Custom name', ['tags', 'here'])
def another_example():
# ...

设置标签的另一种选项是在关键字文档的最后一行使用 Tags: 前缀并用逗号分隔给出它们。例如:

1
2
3
4
5
6
def login(username, password):
"""Log user in to SUT.

Tags: tag1, tag2
"""
# ...
关键字参数

对于静态和混合 API,关键字需要多少个参数的信息直接从实现它的方法中获取。使用动态库 API 的库有其他方式来共享这些信息,因此这一部分对它们来说并不相关。

最常见也是最简单的情况是,关键字需要确切的参数数量。在这种情况下,方法只需接受确切的那些参数。例如,实现没有参数的关键字的方法也不接受参数,实现一个参数的关键字的方法也接受一个参数,依此类推。

示例关键字接受不同数量的参数:

1
2
3
4
5
6
7
8
def no_arguments():
print("Keyword got no arguments.")

def one_argument(arg):
print("Keyword got one argument '%s'." % arg)

def three_arguments(a1, a2, a3):
print("Keyword got three arguments '%s', '%s' and '%s'." % (a1, a2, a3))

关键字的默认值

关键字的某些参数具有默认值通常是很有用的。

在 Python 中,一个方法总是有且只有一个实现,可能的默认值在方法签名中指定。下面的语法对所有 Python 程序员来说都是熟悉的:

1
2
3
4
5
def one_default(arg='default'):
print("Argument has value %s" % arg)

def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
print("Got arguments %s, %s and %s" % (arg1, arg2, arg3))

上面的第一个示例关键字可以使用零个或一个参数。如果没有给出参数,arg 将获得值 default。如果有一个参数,arg 将获得该值,如果使用超过一个参数调用关键字将失败。在第二个示例中,总是需要一个参数,但第二个和第三个参数有默认值,所以可以使用一个到三个参数的关键字。

1
2
3
4
5
6
7
*** Test Cases ***
Defaults
One Default
One Default argument
Multiple Defaults required arg
Multiple Defaults required arg optional
Multiple Defaults required arg optional 1 optional 2
变量数量的参数(*varargs)

Robot Framework 也支持接受任意数量参数的关键字。

Python 支持接受任意数量参数的方法。相同的语法在库中也适用,如下面的示例所示,它还可以与指定参数的其他方式结合使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def any_arguments(*args):
print("Got arguments:")
for arg in args:
print(arg)

def one_required(required, *others):
print("Required: %s\nOthers:" % required)
for arg in others:
print(arg)

def also_defaults(req, def1="default 1", def2="default 2", *rest):
print(req, def1, def2, rest)
*** Test Cases ***
Varargs
Any Arguments
Any Arguments argument
Any Arguments arg 1 arg 2 arg 3 arg 4 arg 5
One Required required arg
One Required required arg another arg yet another
Also Defaults required
Also Defaults required these two have defaults
Also Defaults 1 2 3 4 5 6
自由关键字参数(**kwargs)

Robot Framework 支持 Python 的 **kwargs 语法。如何使用接受自由关键字参数(也称为自由命名参数)的关键字在创建测试用例部分进行了讨论。在这一部分,将看看如何创建这样的关键字。

如果已经熟悉 Python 中 kwargs 的工作方式,那么理解它们如何在 Robot Framework 测试库中工作就相当简单。下面的示例展示了基本功能:

1
2
3
4
5
6
7
def example_keyword(**stuff):
for name, value in stuff.items():
print(name, value)
*** Test Cases ***
Keyword Arguments
Example Keyword hello=world # Logs 'hello world'.
Example Keyword foo=1 bar=42 # Logs 'foo 1' and 'bar 42'.

基本上,关键字调用末尾使用 name=value 语法的所有参数,以及不匹配任何其他参数的参数,都作为 kwargs 传递给关键字。为了避免像 foo=quux 这样的字面值作为自由关键字参数,必须像 foo=quux 这样进行转义。

下面的示例说明了普通参数、varargs 和 kwargs 是如何一起工作的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def various_args(arg=None, *varargs, **kwargs):
if arg is not None:
print('arg:', arg)
for value in varargs:
print('vararg:', value)
for name, value in sorted(kwargs.items()):
print('kwarg:', name, value)
*** Test Cases ***
Positional
Various Args hello world # Logs 'arg: hello' and 'vararg: world'.

Named
Various Args arg=value # Logs 'arg: value'.

Kwargs
Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
Various Args c=3 a=1 b=2 # Same as above. Order does not matter.

Positional and kwargs
Various Args 1 2 kw=3 # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.

Named and kwargs
Various Args arg=value hello=world # Logs 'arg: value' and 'kwarg: hello world'.
Various Args hello=world arg=value # Same as above. Order does not matter.

有关使用与上述示例完全相同的签名的真实世界示例,请参见 Process 库中的 Run Process 和 Start Keyword 关键字。

仅关键字参数

从 Robot Framework 3.1 开始,可以在不同的关键字中使用仅命名参数。这种支持是由 Python 的仅关键字参数提供的。仅关键字参数在可能的 *varargs 之后或在不需要 *varargs 时在专用的 * 标记之后指定。可能的 **kwargs 在仅关键字参数之后指定。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)

def strip_spaces(word, *, left=True, right=True):
if left:
word = word.lstrip()
if right:
word = word.rstrip()
return word
*** Test Cases ***
Example
Sort Words Foo bar baZ
Sort Words Foo bar baZ case_sensitive=True
Strip Spaces ${word} left=False
仅位置参数

Python 支持所谓的仅位置参数,使得可以指定一个参数只能作为位置参数给出,而不能像 name=value 那样作为命名参数给出。仅位置参数在普通参数之前指定,并且在它们之后必须使用特殊的 / 标记:

1
2
def keyword(posonly, /, normal):
print(f"Got positional-only argument {posonly} and normal argument {normal}.")

上述关键字可以像这样使用:

1
2
3
4
5
6
*** Test Cases ***
Example
# 仅位置参数和普通参数作为位置参数使用。
Keyword foo bar
# 普通参数也可以命名。
Keyword foo normal=bar

如果一个仅位置参数使用了一个包含等号的值,如 example=usage,即使等号前的部分与参数名称匹配,也不会被认为是命名参数语法。然而,这个规则只适用于在没有其他参数在它之前使用命名参数语法的情况下,仅位置参数在其正确位置使用。

1
2
3
4
5
6
*** Test Cases ***
Example
# 在这种情况下,仅位置参数获取字面值 `posonly=foo`。
Keyword posonly=foo normal=bar
# 这将失败。
Keyword normal=bar posonly=foo

从 Robot Framework 4.0 开始,完全支持仅位置参数。在早期版本中,也可以将它们作为位置参数使用,但将它们作为命名参数使用会在 Python 端引发错误。

参数转换

默认情况下,定义在 Robot Framework 测试数据中的参数作为 Unicode 字符串传递给关键字。然而,也有几种使用非字符串值的方法:

  • 变量可以包含任何类型的对象作为值,作为参数的变量按原样传递给关键字。
  • 关键字本身可以将它们接受的参数转换为其他类型。
  • 可以使用函数注解或 @keyword 装饰器显式指定参数类型。在这些情况下,Robot Framework 会自动转换参数。
  • 也会根据关键字默认值进行自动转换。
  • 库可以注册自定义参数转换器。

基于函数注解、使用 @keyword 装饰器指定的类型以及参数默认值的自动参数转换都是 Robot Framework 3.1 中的新功能。支持的转换部分指定了在这些情况下支持哪些参数转换。

在 Robot Framework 4.0 之前,只有当给定的参数是字符串时才进行自动转换。现在,无论参数类型如何,都会进行转换。

手动参数转换

如果没有向 Robot Framework 指定类型信息,所有未作为变量传递的参数都作为 Unicode 字符串给出。这包括以下情况:

1
2
3
*** Test Cases ***
Example
Example Keyword 42 False

关键字内部总是可以转换作为字符串传递的参数。在简单的情况下,这意味着使用 int() 或 float() 将参数转换为数字,但也可能进行其他类型的转换。在处理布尔值时,必须小心,因为所有非空字符串,包括字符串 False,都被 Python 视为真。Robot Framework 自己的 robot.utils.is_truthy() 实用程序很好地处理了这个问题,因为它将像 FALSE、NO 和 NONE(不区分大小写)这样的字符串视为假:

1
2
3
4
5
6
7
from robot.utils import is_truthy


def example_keyword(count, case_insensitive):
count = int(count)
if is_truthy(case_insensitive):
# ...

关键字还可以通过 robot.api.TypeInfo 类及其 convert 方法使用 Robot Framework 的参数转换功能。如果需要的转换逻辑更复杂,或者需要比简单使用 int() 提供的更好的错误报告,这可能会很有用。

1
2
3
4
5
6
7
from robot.api import TypeInfo


def example_keyword(count, case_insensitive):
count = TypeInfo.from_type(int).convert(count)
if TypeInfo.from_type(bool).convert(case_insensitive):
# ...

信息

通常建议使用类型提示或其他方式指定类型,并让 Robot Framework 自动处理参数转换。只有在特殊情况下才需要手动参数转换。

注意

robot.api.TypeInfo 是在 Robot Framework 7.0 中新增的。

使用函数注解指定参数类型

从 Robot Framework 3.1 开始,如果可用参数类型信息并且类型被识别,传递给关键字的参数将自动转换。指定类型的最自然方式是使用 Python 函数注解。例如,前面的示例中的关键字可以如下实现,参数将自动转换:

1
2
3
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
# ...

请参阅下面的支持的转换部分,了解自动转换的类型列表以及这些类型接受的值。如果一个具有支持类型之一的参数给出了无法转换的值,那么这是一个错误。只注解一部分参数是可以的。

用其他类型注解参数不是错误,也可以使用注解进行其他类型的操作。在这些情况下不进行转换,但注解仍然显示在由 Libdoc 生成的文档中。

关键字也可以有返回类型注解,使用签名末尾的 -> 符号指定,如 def example() -> int:。这个信息在执行过程中不用于任何事情,但从 Robot Framework 7.0 开始,它由 Libdoc 显示用于文档目的。

使用 @keyword 装饰器指定参数类型

指定显式参数类型的另一种方式是使用 @keyword 装饰器。从 Robot Framework 3.1 开始,它接受一个可选的 types 参数,可以用来以字典的形式指定参数类型,将参数名称映射到类型,或者以列表的形式基于位置映射参数到类型。下面的示例展示了实现与前面示例中相同的关键字的这些方法:

1
2
3
4
5
6
7
8
9
10
11
12
from robot.api.deco import keyword


@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
if case_insensitive:
# ...

@keyword(types=[int, bool])
def example_keyword(count, case_insensitive=True):
if case_insensitive:
# ...

无论使用哪种方法,都不必为所有参数指定类型。当以列表形式指定类型时,可以使用 None 标记某个参数没有类型信息,并且可以完全省略末尾的参数。例如,这两个关键字都只为第二个参数指定了类型:

1
2
3
4
5
6
7
@keyword(types={'second': float})
def example1(first, second, third):
# ...

@keyword(types=[None, float])
def example2(first, second, third):
# ...

从 Robot Framework 7.0 开始,可以通过在类型字典中使用键 ‘return’ 和适当的类型来指定关键字返回类型。这个信息在执行过程中不用于任何事情,但它由 Libdoc 显示用于文档目的。

如果使用 @keyword 装饰器指定了任何类型,那么该关键字从注解中获取的类型信息将被忽略。像 @keyword(types=None) 这样设置类型为 None 将完全禁用类型转换,因此也会忽略从默认值获取的类型信息。

基于默认值的隐式参数类型

如果没有使用注解或 @keyword 装饰器显式获取类型信息,Robot Framework 3.1 及更高版本会尝试基于可能的参数默认值获取它。在这个示例中,count 和 case_insensitive 分别获取类型 int 和 bool:

1
2
3
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
# ...

当基于默认值隐式获取类型信息时,参数转换本身并不像从显式获取的信息那样严格:

  • 转换也可能尝试到其他”相似”的类型。例如,如果转换为整数失败,将尝试浮点数转换。
  • 转换失败不是错误,在这些情况下,关键字获取原始值。
  • 如果一个参数有一个显式类型和一个默认值,首先尝试基于显式类型进行转换。如果失败,那么将尝试基于默认值进行转换。在这种特殊情况下,基于默认值的转换是严格的,转换失败会导致错误。

如果不希望基于默认值进行参数转换,可以使用 @keyword 装饰器像 @keyword(types=None) 这样禁用整个参数转换。

注意

在 Robot Framework 4.0 之前,只有当参数没有显式类型时,才会根据默认值进行转换。

支持的转换

下表列出了 Robot Framework 3.1 及更高版本将参数转换为的类型。这些特性适用于所有转换:

  • 类型可以使用函数注解或 @keyword 装饰器显式指定。
  • 如果没有显式指定,类型可以从参数默认值隐式获取。
  • 无论给定参数的类型如何,都会进行转换。如果参数类型与预期类型不兼容,转换失败。
  • 如果类型已经显式指定,转换失败会导致错误。如果根据默认值获取类型,给定参数将按原样使用。

注意

如果一个参数既有类型提示又有默认值,首先尝试基于类型提示进行转换,然后,如果失败,基于默认值类型进行转换。这种行为在未来可能会改变,以便只有在参数没有类型提示时才根据默认值进行转换。这将改变像 arg: list = None 这样的情况的转换行为,其中不再尝试 None 转换。强烈建议库创建者像 arg: list | None = None 这样显式指定默认值类型。

可以使用具体类型(例如,list)、使用抽象基类(ABC)(例如,Sequence)或使用这些类型的子类(例如,MutableSequence)来指定要使用的类型。也支持在 typing 模块中映射到支持的具体类型或 ABC 的类型(例如,List)。在所有这些情况下,参数都转换为具体类型。

除了使用实际类型(例如,int),还可以使用字符串(例如,‘int’)指定类型,一些类型也有别名(例如,‘integer’)。匹配类型到名称和别名不区分大小写。

“接受”列指定了哪些给定的参数类型被转换。如果给定的参数已经具有预期的类型,不会进行转换。其他类型会导致转换失败。

类型 ABC 别名 接受 解释 示例
bool boolean str, int, float, None 字符串 TRUE, YES, ON 和 1 转换为 True,空字符串以及 FALSE, NO, OFF 和 0 转换为 False,字符串 NONE 转换为 None。其他字符串和其他接受的值按原样传递,允许关键字在需要时特殊处理它们。所有字符串比较都不区分大小写。True 和 false 字符串可以本地化。参见支持的翻译的翻译附录。 TRUE (转换为 True) off (转换为 False) example (按原样使用)
int Integral integer, long str, float 使用内置函数 int 进行转换。只有当它们可以精确表示为整数时才接受浮点数。例如,接受 1.0 而不接受 1.1。如果将字符串转换为整数失败,并且根据默认值隐式获取类型,则尝试转换为浮点数。从 Robot Framework 4.1 开始,可以通过在值前加上 0x、0o 和 0b 来使用十六进制、八进制和二进制数字。从 Robot Framework 4.1 开始,可以使用空格和下划线作为数字分组的视觉分隔符。从 Robot Framework 7.0 开始,只要它们的小数部分为零,就接受表示浮点数的字符串。这包括使用科学记数法,如 1e100。 42 -1 10 000 000 1e100 0xFF 0o777 0b1010 0xBAD_C0FFEE {1}1{1.0}
float Real double str, Real 使用内置的 float 进行转换。从 Robot Framework 4.1 开始,可以使用空格和下划线作为数字分组的视觉分隔符。 3.14 2.9979e8 10 000.000 01 10_000.000_01
Decimal str, int, float 使用 Decimal 类进行转换。当需要精确表示小数时,推荐使用 Decimal 而不是 float。从 Robot Framework 4.1 开始,可以使用空格和下划线作为数字分组的视觉分隔符。 3.14 10 000.000 01 10_000.000_01
str string, unicode 任何 所有参数都转换为 Unicode 字符串。在 Robot Framework 4.0 中新增。
bytes str, bytearray 字符串被转换为字节,以便每个 Unicode 码点低于 256 的直接映射到匹配的字节。不允许使用更高的码点。 good hyvä (转换为 hyv\xe4) \x00 (空字节)
bytearray str, bytes 与字节的转换相同,但结果是 bytearray。
datetime str, int, float 预期字符串是 ISO 8601 格式的时间戳,如 YYYY-MM-DD hh:mm:ss.mmmmmm,其中任何非数字字符都可以用作分隔符,或者可以完全省略分隔符。此外,只有日期部分是必需的,所有可能缺失的时间组件都被视为零。整数和浮点数被认为是自 Unix 纪元以来的秒数。 2022-02-09T16:39:43.632269 2022-02-09 16:39 2022-02-09 ${1644417583.632269} (Epoch 时间)
date str 与 datetime 的字符串转换相同,但所有时间组件都应省略或为零。 2018-09-12
timedelta str, int, float 预期字符串表示一个时间间隔,格式为 Robot Framework 支持的一种时间格式:数字时间、时间字符串或”计时器”字符串。整数和浮点数被认为是秒。 42 (42 秒) 1 分钟 2 秒 01:02 (与上面相同)
Path PathLike str 字符串被转换为 pathlib.Path 对象。在 Windows 上,/ 自动转换为 \。在 Robot Framework 6.0 中新增。 /tmp/absolute/path relative/path/to/file.ext name.txt
Enum str 指定的类型必须是枚举(Enum 或 Flag 的子类),给定的参数必须匹配其成员名称。匹配成员名称对大小写、空格、下划线和连字符不敏感,但精确匹配优先于规范化匹配。在 Robot Framework 7.0 中新增忽略连字符。枚举文档和成员在由 Libdoc 自动生成的文档中自动显示。 NORTH (Direction.NORTH) north west (Direction.NORTH_WEST)
IntEnum str, int 指定的类型必须是基于整数的枚举(IntEnum 或 IntFlag 的子类),给定的参数必须匹配其成员名称或值。匹配成员名称的方式与 Enum 相同。值可以是整数,也可以是可以转换为整数的字符串。枚举文档和成员在由 Libdoc 自动生成的文档中自动显示。在 Robot Framework 4.1 中新增。 OFF (PowerState.OFF) 1 (PowerState.ON)
Literal 任何 只接受指定的值。值可以是字符串、整数、字节、布尔值、枚举和 None,使用的参数使用特定于值类型的转换逻辑进行转换。字符串对大小写、空格、下划线和连字符不敏感,但精确匹配优先于规范化匹配。Literal 提供了与 Enum 类似的功能,但不支持自定义文档。在 Robot Framework 7.0 中新增。 OFF on
None str 字符串 NONE(不区分大小写)被转换为 Python None 对象。其他值会导致错误。 None
Any 任何 接受任何值。不进行转换。在 Robot Framework 6.1 中新增。
list Sequence sequence str, Sequence 字符串必须是 Python 列表字面量。它们使用 ast.literal_eval 函数转换为实际的列表。它们可以包含 ast.literal_eval 支持的任何值,包括列表和其他容器。如果使用的类型提示是 list(例如,arg: list),那么不是列表的序列将被转换为列表。如果类型提示是通用的 Sequence,那么序列将不经转换地使用。在 Robot Framework 7.0 中新增序列别名。 [‘one’, ‘two’] [(‘one’, 1), (‘two’, 2)]
tuple str, Sequence 与 list 相同,但字符串参数必须是元组字面量。 (‘one’, ‘two’)
set Set str, Container 与 list 相同,但字符串参数必须是集合字面量或 set() 来创建一个空集合。 {1, 2, 3, 42} set()
frozenset str, Container 与 set 相同,但结果是一个 frozenset。 {1, 2, 3, 42} frozenset()
dict Mapping dictionary, mapping, map str, Mapping 与 list 相同,但字符串参数必须是字典字面量。在 Robot Framework 7.0 中新增映射别名。 {‘a’: 1, ‘b’: 2} {‘key’: 1, ‘nested’: {‘key’: 2}}
TypedDict str, Mapping 与 dict 相同,但字典项也转换为指定的类型,不允许包含类型规范中未包含的项。在 Robot Framework 6.0 中新增。普通的 dict 转换在之前使用。 {‘width’: 1600, ‘enabled’: True}

注意

注意

从 Robot Framework 5.0 开始,已转换的类型会自动显示在 Libdoc 输出中。

在 Robot Framework 4.0 之前,大多数类型支持将字符串 NONE(不区分大小写)转换为 Python None。这种支持已被移除,只有当参数具有 None 作为显式类型或默认值时,才进行 None 转换。

指定多个可能的类型

从 Robot Framework 4.0 开始,可以指定一个参数有多个可能的类型。在这种情况下,会根据每种类型尝试参数转换,如果所有这些转换都失败,那么整个转换就会失败。

当使用函数注解时,指定一个参数有多个可能的类型的自然语法是使用 Union:

1
2
3
4
5
from typing import Union


def example(length: Union[int, float], padding: Union[int, str, None] = None):
...

当使用 Python 3.10 或更高版本时,可以使用原生的 type1 | type2 语法:

1
2
def example(length: int | float, padding: int | str | None = None):
...

Robot Framework 7.0 增强了对联合语法的支持,以便也可以像 ‘type1 | type2’ 这样的”字符串类型”联合工作。这种语法也适用于旧的 Python 版本:

1
2
def example(length: 'int | float', padding: 'int | str | None' = None):
...

另一种方法是将类型指定为元组。这在注解中不推荐使用,因为其他工具不支持这种语法,但它与 @keyword 装饰器配合使用效果很好:

1
2
3
4
5
6
from robot.api.deco import keyword


@keyword(types={'length': (int, float), 'padding': (int, str, None)})
def example(length, padding=None):
...

在上述示例中,length 参数将首先被转换为整数,如果失败,则转换为浮点数。padding 将首先被转换为整数,然后转换为字符串,最后转换为 None。

如果给定的参数具有其中一个接受的类型,那么不进行转换,参数按原样使用。例如,如果 length 参数获取值 1.5 作为浮点数,它将不会被转换为整数。注意,使用非字符串值(如浮点数)作为参数需要使用变量,如这些示例给出的给 length 参数不同值所示:

1
2
3
4
5
6
*** Test Cases ***
Conversion
Example 10 # 参数是字符串。转换为整数。
Example 1.5 # 参数是字符串。转换为浮点数。
Example ${10} # 参数是整数。按原样接受。
Example ${1.5} # 参数是浮点数。按原样接受。

如果接受的类型之一是字符串,那么如果给定的参数是字符串,则不进行转换。如下面的示例给出的给 padding 参数不同值所示,在这些情况下,也可以使用变量传递其他类型:

1
2
3
4
5
6
7
*** Test Cases ***
Conversion
Example 1 big # 参数是字符串。按原样接受。
Example 1 10 # 参数是字符串。按原样接受。
Example 1 ${10} # 参数是整数。按原样接受。
Example 1 ${None} # 参数是 `None`。按原样接受。
Example 1 ${1.5} # 参数是浮点数。转换为整数。

如果给定的参数没有任何接受的类型,将按照类型指定的顺序尝试转换。如果任何转换成功,结果值将被使用,而不尝试剩余的转换。如果没有单个转换成功,整个转换失败。

如果 Robot Framework 不识别指定的类型,那么原始参数值将按原样使用。例如,对于这个关键字,首先会尝试将参数转换为整数,但如果失败,关键字将获取原始参数:

1
2
def example(argument: Union[int, Unrecognized]):
...

从 Robot Framework 6.1 开始,如果未识别的类型列在识别的类型之前,如 Union[Unrecognized, int],上述逻辑也适用。在这种情况下,也会尝试 int 转换,如果失败,参数将按原样传递。在早期的 Robot Framework 版本中,不会尝试 int 转换。

泛型的类型转换

对于泛型,也可以使用参数化语法,如 list[int]dict[str, int]。当使用这种语法时,首先将给定值转换为基类型,然后将各个项目转换为嵌套类型。不同的泛型类型的转换规则如下:

  • 对于列表,只能有一种类型,如 list[float]。所有列表项都转换为该类型。
  • 对于元组,可以有任意数量的类型,如 tuple[int, int]tuple[str, int, bool]。作为参数的元组预期具有完全相同的项目数量,并且它们被转换为匹配的类型。
  • 要创建同质元组,可以使用一个类型和省略号,如 tuple[int, ...]。在这种情况下,元组可以有任意数量的项目,它们都被转换为指定的类型。
  • 对于字典,必须有两种类型,如 dict[str, int]。字典键使用前一种类型转换,值使用后一种类型转换。
  • 对于集合,只能有一种类型,如 set[float]。转换逻辑与列表相同。
  • 使用原生的 list[int] 语法需要 Python 3.9 或更高版本。如果需要支持早期的 Python 版本,可以使用来自 typing 模块的匹配类型,如 List[int],或使用 “字符串类型” 语法,如 'list[int]'

注意

在 Robot Framework 6.0 中新增了对泛型的嵌套类型转换的支持。同样的语法也适用于早期版本,但参数只转换为基类型,嵌套类型不用于任何事情。

在 Robot Framework 7.0 中新增了对 “字符串类型” 参数化泛型的支持。

自定义参数转换器

除了像前面的章节中解释的那样自动进行参数转换,Robot Framework 还支持自定义参数转换。这个功能有两个主要的用途:

  • 覆盖框架提供的标准参数转换器。
  • 为自定义类型和其他不支持开箱即用的类型添加参数转换。

参数转换器是函数或其他可调用的对象,它们获取用于数据的参数,并在参数传递给关键字之前将它们转换为所需的格式。通过将 ROBOT_LIBRARY_CONVERTERS 属性(区分大小写)设置为将所需类型映射到转换器的字典,为库注册转换器。当以模块的方式实现库时,这个属性必须设置在模块级别,对于基于类的库,它必须是类属性。对于以类实现的库,也可以使用 @library 装饰器的 converters 参数。下面的章节中的示例说明了这两种方法。

注意

在 Robot Framework 5.0 中新增了自定义参数转换器。

覆盖默认转换器

假设想要创建一个接受日期对象的关键字,用于芬兰的用户,其中常用的日期格式是 dd.mm.yyyy。使用方式可能如下所示:

1
2
3
*** Test Cases ***
Example
Keyword 25.1.2022

自动参数转换支持日期,但它期望它们是 yyyy-mm-dd 格式,所以它不会工作。解决方案是创建一个自定义转换器,并注册它来处理日期转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from datetime import date


# 转换器函数。
def parse_fi_date(value):
day, month, year = value.split('.')
return date(int(year), int(month), int(day))


# 为指定类型注册转换器函数。
ROBOT_LIBRARY_CONVERTERS = {date: parse_fi_date}


# 使用自定义转换器的关键字。转换器基于参数类型解析。
def keyword(arg: date):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')
转换错误

如果尝试使用像 invalid 这样的无效参数使用上述关键字,它会失败并产生这个错误:

1
ValueError: Argument 'arg' got value 'invalid' that cannot be converted to date: not enough values to unpack (expected 3, got 1)

这个错误不太有信息量,也没有告诉任何关于预期格式的信息。Robot Framework 不能自动提供更多的信息,但转换器本身可以增强以验证输入。如果输入无效,转换器应该引发一个带有适当消息的 ValueError。在这种特殊情况下,有几种方法可以验证输入,但使用正则表达式可以验证输入在正确的位置有点(.)和日期部分包含正确数量的数字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from datetime import date
import re


def parse_fi_date(value):
# 使用正则表达式验证输入,如果无效则引发 ValueError。
match = re.match(r'(\d{1,2})\.(\d{1,2})\.(\d{4})$', value)
if not match:
raise ValueError(f"Expected date in format 'dd.mm.yyyy', got '{value}'.")
day, month, year = match.groups()
return date(int(year), int(month), int(day))


ROBOT_LIBRARY_CONVERTERS = {date: parse_fi_date}


def keyword(arg: date):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

使用上述转换器代码,使用参数 invalid 的关键字失败会有更多有用的错误消息:

1
ValueError: Argument 'arg' got value 'invalid' that cannot be converted to date: Expected date in format 'dd.mm.yyyy', got 'invalid'.

限制值类型

默认情况下,Robot Framework 会尝试使用所有给定参数的转换器,无论它们的类型如何。这意味着,如果之前的示例关键字被用于包含字符串以外的变量,转换代码将在 re.match 调用中失败。例如,尝试使用参数 ${42} 会导致以下错误:

1
ValueError: Argument 'arg' got value '42' (integer) that cannot be converted to date: TypeError: expected string or bytes-like object

这种错误情况可以自然地在转换器代码中通过检查值类型来处理,但是如果转换器只接受某些类型,通常更容易直接限制值为该类型。做到这一点只需要向转换器添加适当的类型提示:

1
2
def parse_fi_date(value: str):
# ...

注意,这个类型提示不用于在调用转换器之前转换值,它用于严格限制可以使用的类型。使用上述添加后,使用 ${42} 调用关键字将导致以下错误:

1
ValueError: Argument 'arg' got value '42' (integer) that cannot be converted to date.

如果转换器可以接受多种类型,可以将类型指定为 Union。例如,如果想要增强的关键字,以便也接受整数,使它们被视为自 Unix 纪元以来的秒数,可以这样改变转换器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from datetime import date
import re
from typing import Union


# 接受字符串和整数。
def parse_fi_date(value: Union[str, int]):
# 整数单独转换。
if isinstance(value, int):
return date.fromtimestamp(value)
match = re.match(r'(\d{1,2})\.(\d{1,2})\.(\d{4})$', value)
if not match:
raise ValueError(f"Expected date in format 'dd.mm.yyyy', got '{value}'.")
day, month, year = match.groups()
return date(int(year), int(month), int(day))


ROBOT_LIBRARY_CONVERTERS = {date: parse_fi_date}


def keyword(arg: date):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

转换自定义类型

在之前的示例中,日期对象只能以 dd.mm.yyyy 的格式给出。如果需要支持不同格式的日期,比如下面的例子,这将无法工作:

1
2
3
4
5
*** Test Cases ***
Example
Finnish 25.1.2022
US 1/25/2022
ISO 8601 2022-01-22

解决这个问题的方法是创建自定义类型,而不是覆盖默认的日期转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from datetime import date
import re
from typing import Union

from robot.api.deco import keyword, library


# 自定义类型。扩展了一个现有的类型,但这不是必需的。
class FiDate(date):

# 转换器函数实现为类方法。它也可以是一个普通的函数,但这样所有的代码都在同一个类中。
@classmethod
def from_string(cls, value: str):
match = re.match(r'(\d{1,2})\.(\d{1,2})\.(\d{4})$', value)
if not match:
raise ValueError(f"Expected date in format 'dd.mm.yyyy', got '{value}'.")
day, month, year = match.groups()
return cls(int(year), int(month), int(day))


# 另一个自定义类型。
class UsDate(date):

@classmethod
def from_string(cls, value: str):
match = re.match(r'(\d{1,2})/(\d{1,2})/(\d{4})$', value)
if not match:
raise ValueError(f"Expected date in format 'mm/dd/yyyy', got '{value}'.")
month, day, year = match.groups()
return cls(int(year), int(month), int(day))


# 使用 '@library' 装饰器注册转换器。
@library(converters={FiDate: FiDate.from_string, UsDate: UsDate.from_string})
class Library:

# 使用支持 'dd.mm.yyyy' 格式的自定义转换器。
@keyword
def finnish(self, arg: FiDate):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

# 使用支持 'mm/dd/yyyy' 格式的自定义转换器。
@keyword
def us(self, arg: UsDate):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

# 使用与 IS0-8601 兼容的默认转换。
@keyword
def iso_8601(self, arg: date):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

# 接受不同格式的日期。
@keyword
def any(self, arg: Union[FiDate, UsDate, date]):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

严格的类型验证

如果参数开始时就是指定的类型,那么根本不会使用转换器。因此,使用不接受任何值的自定义转换器可以轻松启用严格的类型验证。例如,Example 关键字只接受 StrictType 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class StrictType:
pass


def strict_converter(arg):
raise TypeError(f'Only StrictType instances accepted, got {type(arg).__name__}.')


ROBOT_LIBRARY_CONVERTERS = {StrictType: strict_converter}


def example(argument: StrictType):
assert isinstance(argument, StrictType)

为了方便,Robot Framework 允许将转换器设置为 None 以获得相同的效果。例如,这段代码的行为与上述代码完全相同:

1
2
3
4
5
6
7
8
9
class StrictType:
pass


ROBOT_LIBRARY_CONVERTERS = {StrictType: None}


def example(argument: StrictType):
assert isinstance(argument, StrictType)

注意

在 Robot Framework 6.0 中新增了使用 None 作为严格转换器。在早期版本中需要使用显式的转换器函数。

从转换器访问测试库

从 Robot Framework 6.1 开始,可以从转换器函数访问库实例。这允许定义依赖于库状态的动态类型转换。例如,如果库可以配置为测试特定的区域设置,可能会使用库状态来确定如何解析日期,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from datetime import date
import re


def parse_date(value, library):
# 使用正则表达式验证输入,如果无效则引发 ValueError。
# 使用库状态中的区域设置来确定解析格式。
if library.locale == 'en_US':
match = re.match(r'(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<year>\d{4})$', value)
format = 'mm/dd/yyyy'
else:
match = re.match(r'(?P<day>\d{1,2})\.(?P<month>\d{1,2})\.(?P<year>\d{4})$', value)
format = 'dd.mm.yyyy'
if not match:
raise ValueError(f"Expected date in format '{format}', got '{value}'.")
return date(int(match.group('year')), int(match.group('month')), int(match.group('day')))


ROBOT_LIBRARY_CONVERTERS = {date: parse_date}


def keyword(arg: date):
print(f'year: {arg.year}, month: {arg.month}, day: {arg.day}')

转换器函数的库参数是可选的,即如果转换器函数只接受一个参数,库参数将被省略。通过使转换器函数只接受可变参数,例如 def parse_date(*varargs),可以实现类似的结果。

转换器文档

转换器的信息会自动添加到由 Libdoc 生成的输出中。这些信息包括类型的名称、接受的值(如果使用类型提示指定)和文档。类型信息会自动链接到使用这些类型的所有关键字。

默认情况下,文档是从转换器函数中获取的。如果它没有任何文档,文档就从类型中获取。因此,上一个示例中向转换器添加文档的这两种方法产生的结果是相同的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class FiDate(date):

@classmethod
def from_string(cls, value: str):
"""日期格式为 ``dd.mm.yyyy``。"""
# ...


class UsDate(date):
"""日期格式为 ``mm/dd/yyyy``。"""

@classmethod
def from_string(cls, value: str):
# ...

通常推荐添加文档,以向用户提供更多关于转换的信息。特别重要的是要记录为现有类型注册的转换器函数,因为它们自己的文档在这种情况下可能不太有用。

@keyword 装饰器

尽管 Robot Framework 自动获取了关于关键字的大量信息,如它们的名称和参数,但有时需要进一步配置这些信息。这通常最容易通过使用 robot.api.deco.keyword 装饰器来完成。它有几个有用的用法,这些用法在其他地方已经详细解释,这里只列出作为参考:

  • 当使用 @library 装饰器或其他方式禁用了自动关键字发现时,将方法和函数公开为关键字。
  • 为关键字设置自定义名称。当使用嵌入式参数语法时,这尤其有用。
  • 设置关键字标签。
  • 设置类型信息以启用自动参数类型转换。也支持完全禁用参数转换。
  • 当使用动态库 API 或混合库 API 时,标记要公开为关键字的方法。
@not_keyword 装饰器

robot.api.deco.not_keyword 装饰器可以用于禁用函数或方法成为关键字。

使用自定义装饰器

在实现关键字时,有时使用 Python 装饰器修改它们是有用的。然而,装饰器通常会修改函数签名,因此可能会使 Robot Framework 的内省在确定关键字接受哪些参数时感到困惑。当使用 Libdoc 创建库文档和使用像 RIDE 这样的外部工具时,这尤其有问题。避免这个问题的最简单方法是使用 functools.wraps 装饰装饰器本身。其他解决方案包括使用像 decorator 和 wrapt 这样的外部模块,它们允许创建完全保留签名的装饰器。

注意

在 Robot Framework 3.2 中新增了对使用 functools.wraps 装饰的装饰器的 “解包” 支持。

嵌入参数到关键字名称

库关键字也可以接受嵌入参数,就像用户关键字一样。这部分主要介绍创建此类关键字的 Python 语法,嵌入参数的语法本身在用户关键字文档中详细介绍。

具有嵌入参数的库关键字需要有一个自定义名称,通常使用 @keyword 装饰器设置。匹配嵌入参数的值作为位置参数传递给实现关键字的函数或方法。如果函数或方法接受更多参数,它们可以作为正常的位置或命名参数传递给关键字。参数名称不需要匹配嵌入参数名称,但这通常是一个好的约定。

接受嵌入参数的关键字:

1
2
3
4
5
6
7
8
9
from robot.api.deco import keyword

@keyword('Select ${animal} from list')
def select_animal_from_list(animal):
...

@keyword('Number of ${animals} should be')
def number_of_animals_should_be(animals, count):
...

使用上述关键字的测试:

1
2
3
4
5
6
7
8
*** Test Cases ***
Embedded arguments
Select cat from list
Select dog from list

Embedded and normal arguments
Number of cats should be 2
Number of dogs should be count=3

如果指定了类型信息,自动参数转换也适用于嵌入参数:

1
2
3
@keyword('Add ${quantity} copies of ${item} to cart')
def add_copies_to_cart(quantity: int, item: str):
...

注意

在 Robot Framework 7.0 中,混合嵌入参数和普通参数的支持是新的。

异步关键字

从 Robot Framework 6.1 开始,可以像运行普通函数一样运行原生异步函数(由 async def 创建):

1
2
3
4
5
6
import asyncio
from robot.api.deco import keyword

@keyword
async def this_keyword_waits():
await asyncio.sleep(5)

可以使用 asyncio.get_running_loop()asyncio.get_event_loop() 获取循环的引用。在修改循环运行方式时要小心,它是全局资源。例如,永远不要调用 loop.close(),因为这将使得无法运行任何进一步的协程。如果有任何函数或资源需要事件循环,即使没有显式使用 await,也必须将函数定义为 async 才能使用事件循环。

更多功能示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import asyncio
from robot.api.deco import keyword

async def task_async():
await asyncio.sleep(5)

@keyword
async def examples():
tasks = [task_async() for _ in range(10)]
results = await asyncio.gather(*tasks)

background_task = asyncio.create_task(task_async())
await background_task

# If running with Python 3.10 or higher
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(task_async())
task2 = tg.create_task(task_async())

注意

Robot Framework 会等待函数完成。如果想要一个运行很长时间的任务,例如,使用 asyncio.create_task()。管理任务并保存引用以避免被垃圾回收是责任。如果事件循环关闭并且任务仍在挂起,将打印一条消息到控制台。

如果关键字的执行由于某种原因不能继续,例如信号停止,Robot Framework 将取消异步任务及其所有子任务。其他异步任务将正常运行。

与 Robot Framework 通信

在调用实现关键字的方法后,它可以使用任何机制与被测试系统进行通信。然后,它也可以向 Robot Framework 的日志文件发送消息,返回可以保存到变量的信息,最重要的是,报告关键字是否通过。

报告关键字状态

报告关键字状态只需使用异常即可完成。如果执行的方法引发异常,关键字状态为 FAIL,如果正常返回,则状态为 PASS。

可以使用标准异常(如 AssertionError、ValueError 和 RuntimeError)报告正常执行失败和错误。然而,在后续部分解释的一些特殊情况中,需要特殊的异常。

错误消息

在日志、报告和控制台中显示的错误消息是从异常类型和其消息创建的。对于通用异常(例如,AssertionError、Exception 和 RuntimeError),只使用异常消息,对于其他异常,消息以 ExceptionType: Actual message 的格式创建。

也可以避免在非通用异常的失败消息前添加异常类型。这是通过向异常添加一个特殊的 ROBOT_SUPPRESS_NAME 属性并赋值为 True 来完成的。

Python:

1
2
class MyError(RuntimeError):
ROBOT_SUPPRESS_NAME = True

在所有情况下,对用户来说,异常消息尽可能具有信息性是很重要的。

错误消息中的 HTML

也可以通过在消息开始处添加文本 HTML 来获得 HTML 格式的错误消息:

1
raise AssertionError("*HTML* <a href='robotframework.org'>Robot Framework</a> rulez!!")

这种方法既可以在库中引发异常时使用,如上例所示,也可以在用户在测试数据中提供错误消息时使用。

自动剪切长消息

如果错误消息超过 40 行,它将自动从中间剪切,以防止报告变得过长且难以阅读。完整的错误消息总是显示在失败关键字的日志消息中。

跟踪回溯

异常的跟踪回溯也使用 DEBUG 日志级别记录。这些消息在日志文件中默认不可见,因为它们对普通用户来说很少有趣。在开发库时,通常最好使用 –loglevel DEBUG 运行测试。

Robot Framework 提供的异常

Robot Framework 提供了一些库可以用来报告失败和其他事件的异常。这些异常通过 robot.api 包暴露,并包含以下内容:

Failure

报告验证失败。使用此异常与使用标准的 AssertionError 没有实际区别。使用此异常的主要好处是其名称与其他提供的异常一致。

Error

报告执行错误。与系统行为不符的失败通常应使用 Failure 异常或标准的 AssertionError 报告。例如,如果关键字使用不正确,可以使用此异常。除了与其他提供的异常名称一致外,使用此异常与使用标准的 RuntimeError 没有实际区别。

ContinuableFailure

报告验证失败但允许继续执行。有关更多信息,请参见下面的 “可继续的失败” 部分。

SkipExecution

标记已执行的测试或任务为跳过。有关更多信息,请参见下面的 “跳过测试” 部分。

FatalError

报告停止整个执行的错误。有关更多信息,请参见下面的 “停止测试执行” 部分。

注意:所有这些异常都是在 Robot Framework 4.0 中新增的。除跳过测试(这也是 Robot Framework 4.0 中的新功能)外,早期版本中的其他功能可以通过其他方式获得。

可继续的失败

即使存在失败,也可以继续测试执行。做到这一点的最简单方法是使用提供的 robot.api.ContinuableFailure 异常:

1
2
3
4
5
6
from robot.api import ContinuableFailure

def example_keyword():
if something_is_wrong():
raise ContinuableFailure('Something is wrong but execution can continue.')
...

另一种方法是创建一个自定义异常,该异常具有设置为 True 值的特殊 ROBOT_CONTINUE_ON_FAILURE 属性。下面的示例进行了演示。

1
2
class MyContinuableError(RuntimeError):
ROBOT_CONTINUE_ON_FAILURE = True
跳过测试

可以使用库关键字跳过测试。做到这一点的最简单方法是使用提供的 robot.api.SkipExecution 异常:

1
2
3
4
5
6
from robot.api import SkipExecution

def example_keyword():
if test_should_be_skipped():
raise SkipExecution('Cannot proceed, skipping test.')
...

另一种方法是创建一个自定义异常,该异常具有设置为 True 值的特殊 ROBOT_SKIP_EXECUTION 属性。下面的示例进行了演示。

1
2
class MySkippingError(RuntimeError):
ROBOT_SKIP_EXECUTION = True

停止测试执行

可以使测试用例失败,从而停止整个测试执行。实现这一点的最简单方法是使用提供的 robot.api.FatalError 异常:

1
2
3
4
5
6
from robot.api import FatalError

def example_keyword():
if system_is_not_running():
raise FatalError('System is not running!')
...

除了使用 robot.api.FatalError 异常外,还可以创建一个具有设置为 True 值的特殊 ROBOT_EXIT_ON_FAILURE 属性的自定义异常。下面的示例进行了说明。

1
2
class MyFatalError(RuntimeError):
ROBOT_EXIT_ON_FAILURE = True
记录信息

异常消息不是向用户提供信息的唯一方式。除此之外,方法还可以通过简单地写入标准输出流(stdout)或标准错误流(stderr)向日志文件发送消息,甚至可以使用不同的日志级别。另一个,通常更好的,记录可能性是使用编程日志 API。

默认情况下,方法写入标准输出的所有内容都作为单个条目写入日志文件,日志级别为 INFO。写入标准错误的消息在其他方面处理类似,但在关键字执行完成后,它们会被回显到原始 stderr。因此,如果需要在执行测试的控制台上看到一些消息,可以使用 stderr。

使用日志级别

要使用 INFO 以外的其他日志级别,或创建多个消息,可以通过在消息中明确指定日志级别来实现,格式为 LEVEL 实际日志消息。在这种格式中,LEVEL 必须在行的开头,LEVEL 必须是可用的具体日志级别 TRACE、DEBUG、INFO、WARN 或 ERROR,或伪日志级别 HTML 或 CONSOLE。伪级别可以分别用于记录 HTML 和记录到控制台。

错误和警告

带有 ERROR 或 WARN 级别的消息会自动写入控制台和日志文件中的单独的 “Test Execution Errors” 部分。这使得这些消息比其他消息更加可见,并允许使用它们向用户报告重要但非关键的问题。

记录 HTML

库通常记录的所有内容都将转换为可以安全表示为 HTML 的格式。例如,foo 将在日志中完全按照该方式显示,而不是 foo。如果库想要使用格式化、链接、显示图像等,它们可以使用特殊的伪日志级别 HTML。Robot Framework 将这些消息直接以 INFO 级别写入日志,因此它们可以使用任何它们想要的 HTML 语法。注意,需要谨慎使用此功能,因为例如,一个放置不当的 标签可能会严重破坏日志文件。

在使用公共日志 API 时,各种日志方法有可选的 html 属性,可以设置为 True 以启用 HTML 格式的日志。

时间戳

默认情况下,通过标准输出或错误流记录的消息在执行的关键字结束时获取其时间戳。这意味着时间戳不准确,特别是对于运行时间较长的关键字的问题调试可能会有问题。

关键字有可能在需要时向它们记录的消息中添加准确的时间戳。时间戳必须以 Unix 纪元以来的毫秒数给出,并且必须放在日志级别后面,用冒号与其分隔:

1
2
*INFO:1308435758660* Message with timestamp
*HTML:1308435758661* <b>HTML</b> message with timestamp

如下面的示例所示,添加时间戳很容易。然而,使用编程日志 API 获取准确的时间戳更容易。显式添加时间戳的一个大好处是,这种方法也适用于远程库接口。

1
2
3
4
import time

def example_keyword():
print('*INFO:%d* Message with timestamp' % (time.time()*1000))

记录到控制台

库有多种选项可以将消息写入控制台。如前所述,警告和所有写入标准错误流的消息都会同时写入日志文件和控制台。这两种选项都有一个限制,即消息只有在当前执行的关键字完成后才会结束到控制台。

从 Robot Framework 6.1 开始,库可以使用伪日志级别 CONSOLE 将消息同时记录到日志文件和控制台:

1
2
def my_keyword(arg):
print('*CONSOLE* Message both to log and to console.')

这些消息将以与 HTML 伪日志级别类似的方式使用 INFO 级别记录到日志文件。使用此方法时,消息只有在关键字执行结束后才会记录到控制台。

另一种选项是将消息写入 sys.__stdout__sys.__stderr__。使用此方法时,消息会立即写入控制台,并且根本不会写入日志文件:

1
2
3
4
import sys

def my_keyword(arg):
print('Message only to console.', file=sys.__stdout__)

最后一个选项是使用公共日志 API。在这种方法中,消息也会立即写入控制台:

1
2
3
4
5
6
7
from robot.api import logger

def log_to_console(arg):
logger.console('Message only to console.')

def log_to_console_and_log_file(arg):
logger.info('Message both to log and to console.', also_console=True)
日志示例

在大多数情况下,INFO 级别就足够了。低于它的级别,DEBUG 和 TRACE,对于写入调试信息很有用。这些消息通常不会显示,但是它们可以帮助调试库本身可能存在的问题。WARN 或 ERROR 级别可以用来使消息更加可见,HTML 对于需要任何类型的格式化都很有用。当消息需要同时显示在控制台和日志文件中时,可以使用 CONSOLE 级别。

以下示例阐明了如何使用不同级别进行日志记录的工作方式。

1
2
3
4
5
6
7
8
9
10
print('Hello from a library.')
print('*WARN* Warning from a library.')
print('*ERROR* Something unexpected happen that may indicate a problem in the test.')
print('*INFO* Hello again!')
print('This will be part of the previous message.')
print('*INFO* This is a new message.')
print('*INFO* This is <b>normal text</b>.')
print('*CONSOLE* This logs into console and log file.')
print('*HTML* This is <b>bold</b>.')
print('*HTML* <a href="http://robotframework.org">Robot Framework</a>')
1
2
3
4
5
6
7
8
9
16:18:42.123	INFO	Hello from a library. 
16:18:42.123 WARN Warning from a library.
16:18:42.123 ERROR Something unexpected happen that may indicate a problem in the test.
16:18:42.123 INFO Hello again! This will be part of the previous message.
16:18:42.123 INFO This is a new message.
16:18:42.123 INFO This is <b>normal text</b>.
16:18:42.123 INFO This logs into console and log file.
16:18:42.123 INFO This is bold.
16:18:42.123 INFO Robot Framework

程序化日志 API

程序化 API 提供了比使用标准输出和错误流更清晰的方式来记录信息。

公共日志 API

Robot Framework 有一个基于 Python 的日志 API,用于将消息写入日志文件和控制台。测试库可以像 logger.info('My message') 这样使用这个 API,而不是像 print('*INFO* My message') 那样通过标准输出进行日志记录。除了程序化接口使用起来更清晰外,这个 API 还有一个好处,即日志消息具有准确的时间戳。

公共日志 API 在 https://robot-framework.readthedocs.org 的 API 文档部分有详细的文档。下面是一个简单的使用示例:

1
2
3
4
5
6
7
from robot.api import logger

def my_keyword(arg):
logger.debug('Got argument %s' % arg)
do_something()
logger.info('<i>This</i> is a boring example', html=True)
logger.console('Hello, console!')

一个明显的限制是,使用这个日志 API 的测试库依赖于 Robot Framework。如果 Robot Framework 没有运行,消息会自动重定向到 Python 的标准日志模块。

使用 Python 的标准日志模块

除了新的公共日志 API 外,Robot Framework 还提供了对 Python 的标准日志模块的内置支持。这个工作方式是,模块的根记录器接收到的所有消息都会自动传播到 Robot Framework 的日志文件。此 API 也会产生具有准确时间戳的日志消息,但不支持记录 HTML 消息或将消息写入控制台。一个很大的好处,也由下面的简单示例说明,是使用这个日志 API 不会创建对 Robot Framework 的依赖。

1
2
3
4
5
6
import logging

def my_keyword(arg):
logging.debug('Got argument %s' % arg)
do_something()
logging.info('This is a boring example')

日志模块的日志级别与 Robot Framework 稍有不同。它的 DEBUG、INFO、WARNING 和 ERROR 级别直接映射到匹配的 Robot Framework 日志级别,CRITICAL 映射到 ERROR。自定义日志级别映射到比自定义级别小的最接近的标准级别。例如,INFO 和 WARNING 之间的级别映射到 Robot Framework 的 INFO 级别。

在库初始化期间进行日志记录

库也可以在测试库导入和初始化期间进行日志记录。这些消息不会像正常的日志消息那样出现在日志文件中,而是写入到系统日志中。这允许记录关于库初始化的任何类型的有用的调试信息。使用 WARN 或 ERROR 级别记录的消息也会在日志文件中的测试执行错误部分中可见。

在导入和初始化期间进行日志记录可以使用标准输出和错误流以及程序化日志 API。这两种方法都在下面进行了演示。

在导入期间使用日志 API 记录库:

1
2
3
4
5
6
from robot.api import logger

logger.debug("Importing library")

def keyword():
# ...

注意

如果在初始化期间,即在 Python 的 __init__ 中,记录了一些东西,那么根据库的范围,消息可能会被记录多次。

返回值

关键字与核心框架进行反向通信的最后一种方式是返回从被测试系统中检索或通过其他方式生成的信息。返回的值可以赋值给测试数据中的变量,然后作为其他关键字(甚至来自不同测试库)的输入使用。

值是通过方法中的 return 语句返回的。通常,一个值被赋值给一个标量变量,如下例所示。此示例还说明了可以返回任何对象并使用扩展变量语法访问对象属性的可能性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from mymodule import MyObject


def return_string():
return "Hello, world!"

def return_object(name):
return MyObject(name)
*** Test Cases ***
Returning one value
${string} = Return String
Should Be Equal ${string} Hello, world!
${object} = Return Object Robot
Should Be Equal ${object.name} Robot

关键字也可以返回值,以便它们可以一次赋值给多个标量变量,赋值给一个列表变量,或赋值给标量变量和一个列表变量。所有这些用法都需要返回的值是列表或类似列表的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def return_two_values():
return 'first value', 'second value'

def return_multiple_values():
return ['a', 'list', 'of', 'strings']
*** Test Cases ***
Returning multiple values
${var1} ${var2} = Return Two Values
Should Be Equal ${var1} first value
Should Be Equal ${var2} second value
@{list} = Return Two Values
Should Be Equal @{list}[0] first value
Should Be Equal @{list}[1] second value
${s1} ${s2} @{li} = Return Multiple Values
Should Be Equal ${s1} ${s2} a list
Should Be Equal @{li}[0] @{li}[1] of strings
检测 Robot Framework 是否正在运行

从 Robot Framework 6.1 开始,通过使用 BuiltIn 库的 robot_runningdry_run_active 属性,很容易检测 Robot Framework 是否正在运行,以及 dry-run 模式是否激活。一个相对常见的用例是,库初始化器可能希望避免在执行期间不使用库但是被初始化(例如,由 Libdoc)时做一些工作:

1
2
3
4
5
6
7
8
from robot.libraries.BuiltIn import BuiltIn

class MyLibrary:

def __init__(self):
builtin = BuiltIn()
if builtin.robot_running and not builtin.dry_run_active:
# Do some initialization that only makes sense during real execution.

有关使用 BuiltIn 库作为程序化 API 的更多信息,包括使用 robot_running 的另一个示例,请参见 “使用 BuiltIn 库” 部分。

使用线程时的通信

如果库使用线程,它通常应该只从主线程与框架进行通信。如果工作线程有例如需要报告的失败或需要记录的东西,它应该首先将信息传递给主线程,然后主线程可以使用本节解释的异常或其他机制与框架进行通信。

当线程在其他关键字运行时在后台运行时,这尤其重要。在这种情况下与框架进行通信的结果是未定义的,最坏的情况可能导致崩溃或输出文件损坏。如果关键字在后台启动了一些东西,应该有另一个关键字检查工作线程的状态并相应地报告收集到的信息。

使用程序化日志 API 的正常日志方法记录的非主线程的消息将被静默忽略。

还有一个在单独的 robotbackgroundlogger 项目中的 BackgroundLogger,具有与标准 robot.api.logger 类似的 API。正常的日志方法会忽略来自非主线程的消息,但 BackgroundLogger 会保存后台消息,以便稍后可以记录到 Robot 的日志中。

分发测试库

文档化库

没有关于其包含哪些关键字以及这些关键字的作用的文档的测试库是相当无用的。为了便于维护,强烈建议将库文档包含在源代码中并从中生成。基本上,这意味着使用 docstrings,如下例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyLibrary:
"""这是一个带有一些文档的示例库。"""

def keyword_with_short_documentation(self, argument):
"""这个关键字只有简短的文档"""
pass

def keyword_with_longer_documentation(self):
"""文档的第一行在这里。

更长的文档在这里继续,它可以包含
多行或段落。
"""
pass

Python 有创建库文档的工具,如上所述。然而,这些工具的输出对于一些用户来说可能稍微有些技术性。另一种选择是使用 Robot Framework 自己的文档工具 Libdoc。此工具可以从使用静态库 API 的库(如上所述)创建库文档,但它也处理使用动态库 API 和混合库 API 的库。

关键字文档的第一行逻辑行,直到第一个空行,用于特殊目的,应包含关键字的简短总体描述。它被 Libdoc 用作简短文档(例如,作为工具提示)并且也显示在测试日志中。

默认情况下,文档被认为遵循 Robot Framework 的文档格式规则。这种简单的格式允许经常使用的样式,如 粗体斜体,表格,列表,链接等。也可以使用 HTML,纯文本和 reStructuredText 格式。有关如何在库源代码中设置格式的信息,请参见文档格式部分,有关格式的更多信息,请参见 Libdoc 章节。

注意:在 Robot Framework 3.1 之前,简短文档只包含关键字文档的第一行物理行。

测试库

任何非琐碎的测试库都需要进行彻底的测试,以防止其中出现错误。当然,这种测试应该是自动化的,以便在库发生变化时重新运行测试。

Python 有出色的单元测试工具,它们非常适合测试库。在使用它们进行此目的的测试与使用它们进行其他测试之间没有主要的区别。熟悉这些工具的开发人员不需要学习任何新东西,而不熟悉它们的开发人员应该 anyway 学习它们。

也很容易使用 Robot Framework 本身来测试库,从而为它们进行实际的端到端验收测试。在 BuiltIn 库中有很多有用的关键字可以用于此目的。特别值得一提的是 Run Keyword And Expect Error,它对于测试关键字是否正确报告错误非常有用。

是否使用单元级别或验收级别的测试方法取决于上下文。如果需要模拟实际的被测试系统,通常在单元级别上更容易。另一方面,验收测试确保关键字通过 Robot Framework 工作。如果不能决定,当然可以使用这两种方法。

打包库

在库被实现、文档化和测试后,仍然需要将其分发给用户。对于由单个文件组成的简单库,通常只需要求用户将该文件复制到某个地方并相应地设置模块搜索路径。更复杂的库应该被打包以使安装更容易。

由于库是正常的编程代码,它们可以使用正常的打包工具进行打包。有关打包和分发 Python 代码的信息,请参见 https://packaging.python.org/。当使用 pip 或其他工具安装此类包时,它会自动在模块搜索路径中。

弃用关键字

有时候,可能需要用新的关键字替换现有的关键字,或者完全删除它们。仅仅通知用户这种变化可能并不总是足够的,运行时获取警告更有效。为了支持这一点,Robot Framework 具有标记关键字已弃用的能力。这使得更容易从测试数据中找到旧的关键字并删除或替换它们。

关键字可以通过在其文档开始处添加文本 DEPRECATED(区分大小写)并在文档的第一行也有一个关闭 * 来标记为已弃用。例如,DEPRECATEDDEPRECATED.* 和 DEPRECATED in version 1.5. 都是有效的标记。

当执行一个已弃用的关键字时,会记录一个弃用警告,并且警告也会显示在控制台和日志文件中的测试执行错误部分。弃用警告以文本 “Keyword ‘’ is deprecated.” 开始,并在弃用标记之后有剩余的简短文档(如果有的话)。例如,如果执行以下关键字,日志文件中将会有如下所示的警告。

1
2
3
4
5
6
def example_keyword(argument):
"""*DEPRECATED!!* Use keyword `Other Keyword` instead.

This keyword does something to given ``argument`` and returns results.
"""
return do_something(argument)
1
20080911 16:00:22.650	WARN	Keyword ‘SomeLibrary.Example Keyword’ is deprecated. Use keyword `Other Keyword` instead.

这个弃用系统适用于大多数测试库,也适用于用户关键字。

动态库 API

动态 API 在很多方面与静态 API 类似。例如,报告关键字状态、记录日志和返回值的工作方式完全相同。最重要的是,导入动态库和使用它们的关键字与其他库没有区别。换句话说,用户不需要知道他们的库使用的是什么 API。

静态库和动态库之间的唯一区别是 Robot Framework 如何发现库实现了哪些关键字,这些关键字有哪些参数和文档,以及如何实际执行这些关键字。在静态 API 中,所有这些都是通过反射完成的,但是动态库有一些特殊的方法用于这些目的。

动态 API 的一个好处是在组织库时有更多的灵活性。使用静态 API,必须在一个类或模块中有所有的关键字,而使用动态 API,可以例如将每个关键字实现为一个单独的类。然而,这种用例对于 Python 来说并不那么重要,因为它的动态能力和多继承已经提供了足够的灵活性,而且还有可能使用混合库 API。

动态 API 的另一个主要用例是实现一个库,使其可以作为可能在其他进程甚至其他机器上运行的实际库的代理。这种代理库可以非常薄,因为关键字名称和所有其他信息都是动态获取的,所以当在实际库中添加新的关键字时,无需更新代理。

本节解释了动态 API 如何在 Robot Framework 和动态库之间工作。对于 Robot Framework 来说,这些库是如何实际实现的(例如,如何将 run_keyword 方法的调用映射到正确的关键字实现)并不重要,有许多不同的方法是可能的。Python 用户也可能会发现 PythonLibCore 项目很有用。

获取关键字名称

动态库通过 get_keyword_names 方法告诉它们实现了哪些关键字。此方法不能接受任何参数,并且必须返回一个包含库实现的关键字名称的字符串列表或数组。

如果返回的关键字名称包含多个单词,它们可以用空格或下划线分隔,或者以驼峰格式返回。例如,[‘first keyword’, ‘second keyword’],[‘first_keyword’, ‘second_keyword’] 和 [‘firstKeyword’, ‘secondKeyword’] 都将被映射到关键字 First Keyword 和 Second Keyword。

动态库必须始终具有此方法。如果它缺失,或者由于某种原因调用它失败,库将被视为静态库。

标记要作为关键字公开的方法

如果一个动态库应该包含既是关键字又是私有辅助方法的方法,那么标记关键字方法可能是明智的,这样在实现 get_keyword_names 时就更容易。robot.api.deco.keyword 装饰器允许轻松做到这一点,因为它在装饰的方法上创建了一个自定义的 ‘robot_name’ 属性。这允许在 get_keyword_names 期间只通过检查库中每个方法的 robot_name 属性来生成关键字列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from robot.api.deco import keyword

class DynamicExample:

def get_keyword_names(self):
# 从库中获取所有属性及其值。
attributes = [(name, getattr(self, name)) for name in dir(self)]
# 过滤出没有 'robot_name' 设置的属性。
keywords = [(name, value) for name, value in attributes
if hasattr(value, 'robot_name')]
# 返回 'robot_name' 的值(如果给定),否则返回原始的 'name'。
return [value.robot_name or name for name, value in keywords]

def helper_method(self):
# ...

@keyword
def keyword_method(self):
# ...
执行关键字

动态库有一个特殊的 run_keyword(别名 runKeyword)方法用于执行它们的关键字。当在测试数据中使用来自动态库的关键字时,Robot Framework 使用 run_keyword 方法来执行它。此方法接受两个或三个参数。第一个参数是一个字符串,包含要执行的关键字的名称,格式与 get_keyword_names 返回的格式相同。第二个参数是一个列表,包含在测试数据中给关键字的位置参数,可选的第三个参数是一个包含命名参数的字典。如果缺少第三个参数,不支持自由命名参数和仅命名参数,其他命名参数映射到位置参数。

注意

在 Robot Framework 3.1 之前,无论 run_keyword 是否接受两个或三个参数,普通命名参数都映射到位置参数。第三个参数只获取可能的自由命名参数。

获取关键字名称和参数后,库可以自由地执行关键字,但必须使用与静态库相同的机制与框架进行通信。这意味着使用异常来报告关键字状态,通过写入标准输出或使用提供的日志 API 进行记录,以及在 run_keyword 中使用 return 语句返回某些内容。

每个动态库必须都有 get_keyword_namesrun_keyword 方法,但动态 API 中的其余方法是可选的。下面的示例显示了一个工作的,尽管微不足道的,动态库。

1
2
3
4
5
6
7
8
class DynamicExample:

def get_keyword_names(self):
return ['first keyword', 'second keyword']

def run_keyword(self, name, args, kwargs):
print("Running keyword '%s' with positional arguments %s and named arguments %s."
% (name, args, kwargs))
获取关键字参数

如果动态库只实现了 get_keyword_namesrun_keyword 方法,Robot Framework 就没有关于实现的关键字接受哪些参数的任何信息。例如,在上面的示例中,First Keyword 和 Second Keyword 都可以使用任何参数。这是有问题的,因为大多数真实的关键字都期望有一定数量的关键字,在这种情况下,它们需要自己检查参数计数。

动态库可以通过使用 get_keyword_arguments(别名 getKeywordArguments)方法来通信它们的关键字期望什么参数。此方法获取关键字的名称作为参数,并且必须返回一个字符串列表,包含该关键字接受的参数。

与其他关键字一样,动态关键字可以要求任意数量的位置参数,有默认值,接受可变数量的参数,接受自由命名参数和具有仅命名参数。如何表示所有这些不同变量的语法是从 Python 中它们是如何指定的派生出来的,并在下表中进行了解释。

参数类型及其表示方式
参数类型 如何表示 示例
无参数 空列表 []
一个或多个位置参数 包含参数名称的字符串列表 ['argument']
['arg1', 'arg2', 'arg3']
默认值 表示参数名称和默认值的两种方式:
1. 作为一个字符串,其中名称和默认值用 = 分隔。
2. 作为一个元组,其中名称和默认值作为单独的项。在 Robot Framework 3.2 中新增。
字符串与 = 分隔符:
['name=default']
['a', 'b=1', 'c=2']
元组:
[('name', 'default')]
['a', ('b', 1), ('c', 2)]
仅位置参数 / 标记之前的参数。在 Robot Framework 6.1 中新增。 ['posonly', '/']
['p', 'q', '/', 'normal']
可变数量的参数(varargs) 可能的位置参数之后的参数有一个 * 前缀 ['*varargs']
['argument', '*rest']
['a', 'b=42', '*c']
仅命名参数 varargs 之后的参数,或者如果没有 varargs,则是一个孤立的 *。有或没有默认值。需要 run_keyword 支持仅命名参数。在 Robot Framework 3.1 中新增。 ['*varargs', 'named']
['*', 'named']
['*', 'x', 'y=default']
['a', '*b', ('c', 42)]
自由命名参数(kwargs) 最后的参数有 ** 前缀。需要 run_keyword 支持自由命名参数。 ['**named']
['a', ('b', 42), '**c']
['*varargs', '**kwargs']
['*', 'kwo', '**kws']

执行关键字

当使用 get_keyword_arguments 时,Robot Framework 会自动计算关键字需要多少个位置参数,以及是否支持自由命名参数。如果一个关键字使用了无效的参数,将会发生错误,甚至不会调用 run_keyword

返回的实际参数名称和默认值也很重要。它们需要用于支持命名参数,Libdoc 工具需要它们来创建有意义的库文档。

如上表所述,可以通过参数名称以字符串的形式指定默认值,如 ‘name=default’,或者以元组的形式,如 (‘name’, ‘default’)。前一种语法的主要问题是所有的默认值都被视为字符串,而后一种语法允许使用所有对象,如 (‘integer’, 1) 或 (‘boolean’, True)。当使用字符串以外的其他对象时,Robot Framework 可以根据它们进行自动参数转换。

出于一致性的原因,也可以将不接受默认值的参数指定为一个元素的元组。例如,[‘a’, ‘b=c’, ‘d’] 和 [(‘a’,), (‘b’, ‘c’), (‘d’,)] 是等价的。

如果 get_keyword_arguments 丢失或对某个关键字返回 Python None,那么该关键字将获得一个接受所有参数的参数规范。这个自动参数规范是 [*varargs, **kwargs] 或 [*varargs],取决于 run_keyword 是否支持自由命名参数。

注意

在 Robot Framework 3.2 中新增了将参数指定为元组,如 (‘name’, ‘default’) 的支持。在 Robot Framework 6.1 中新增了动态库 API 对仅位置参数的支持。

获取关键字参数类型

Robot Framework 3.1 引入了对自动参数转换的支持,动态库 API 也支持这一点。转换逻辑与静态库完全相同,但指定类型信息的方式自然是不同的。

对于动态库,可以使用可选的 get_keyword_types 方法(别名 getKeywordTypes)返回类型。它可以使用列表或字典返回类型,就像使用 @keyword 装饰器时可以指定类型一样。类型信息可以使用实际类型(如 int)来指定,但特别是如果动态库从外部系统获取此信息,使用字符串(如 ‘int’ 或 ‘integer’)可能更容易。有关支持的类型以及如何指定它们的更多信息,请参见 “支持的转换” 部分。

Robot Framework 也会根据参数默认值进行自动参数转换。此前,这在动态 API 中无法工作,因为只能将参数指定为字符串。如前一节所述,这在 Robot Framework 3.2 中发生了变化,现在像 (‘example’, True) 这样返回的默认值会自动用于此目的。

从 Robot Framework 7.0 开始,动态库还可以通过在返回的类型字典中使用键 ‘return’ 和适当的类型来指定关键字返回类型。这个信息在执行期间不用于任何事情,但是它会被 Libdoc 显示出来,用于文档目的。

获取关键字标签

动态库可以通过使用 get_keyword_tags 方法(别名 getKeywordTags)报告关键字标签。它获取一个关键字名称作为参数,并应返回相应的标签作为字符串列表。

另外,也可以在由下面讨论的 get_keyword_documentation 方法返回的文档的最后一行上指定标签。这需要在最后一行开始时使用 Tags: 并在其后列出标签,如 Tags: first tag, second, third。

信息

get_keyword_tags 方法保证在 get_keyword_documentation 方法之前被调用。这使得只有在 get_keyword_tags 方法未被调用时,才能轻松地将标签嵌入到文档中。

获取关键字文档

如果动态库想要提供关键字文档,它们可以实现 get_keyword_documentation 方法(别名 getKeywordDocumentation)。它接受一个关键字名称作为参数,并且,如方法名称所示,返回其文档作为字符串。

返回的文档与静态库中的关键字文档字符串的使用方式相同。主要的用例是将关键字的文档获取到由 Libdoc 生成的库文档中。此外,文档的第一行(直到第一个 \n)显示在测试日志中。

获取通用库文档

get_keyword_documentation 方法也可以用于指定整体库文档。这个文档在测试执行时不使用,但它可以使由 Libdoc 生成的文档更好。

动态库可以提供通用库文档和与使用库相关的文档。前者是通过调用 get_keyword_documentation 与特殊值 __intro__ 获得的,后者是使用值 __init__ 获得的。如何呈现文档最好在实践中使用 Libdoc 进行测试。

动态库还可以直接在代码中指定通用库文档,作为库类和其 __init__ 方法的 docstring。如果直接从代码和 get_keyword_documentation 方法中获取了非空文档,后者优先。

获取关键字源信息

动态 API 遮蔽了 Robot Framework 的关键字的实际实现,因此无法看到关键字是在哪里实现的。这意味着编辑器和其他使用 Robot Framework API 的工具无法实现如 go-to-definition 等功能。这个问题可以通过实现另一个可选的动态方法 get_keyword_source(别名 getKeywordSource)来解决,该方法返回源信息。

get_keyword_source 方法的返回值必须是一个字符串,或者如果没有源信息可用,则为 None。在简单的情况下,只需简单地返回实现关键字的文件的绝对路径即可。如果知道关键字实现开始的行号,可以将其嵌入到返回值中,如 path:lineno。只返回行号也是可能的,如 :lineno

库本身的源信息是从导入的库类中自动获取的,与其他库 API 的方式相同。库源路径用于所有没有定义自己的源路径的关键字。

注意

为关键字返回源信息是 Robot Framework 3.2 中的新功能。

动态库中的命名参数语法

动态库 API 也支持命名参数语法。使用语法是基于从库使用 get_keyword_arguments 方法获取的参数名称和默认值。

如果 run_keyword 方法接受三个参数,第二个参数获取所有位置参数作为列表,最后一个参数获取所有命名参数作为映射。如果它只接受两个参数,命名参数被映射到位置参数。在后一种情况下,如果一个关键字有多个带有默认值的参数,并且只给出了后面的一些参数,那么框架会根据 get_keyword_arguments 方法返回的默认值填充跳过的可选参数。

以下示例说明了如何使用动态库中的命名参数语法。所有示例都使用一个关键字 Dynamic,它有一个参数规范 [a, b=d1, c=d2]。每行的注释显示了在这些情况下如何调用 run_keyword,如果它有两个参数(即签名是 name, args)以及如果它有三个参数(即 name, args, kwargs)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** 测试用例 ***                  # args          # args, kwargs
仅位置参数
Dynamic x # [x] # [x], {}
Dynamic x y # [x, y] # [x, y], {}
Dynamic x y z # [x, y, z] # [x, y, z], {}

仅命名参数
Dynamic a=x # [x] # [], {a: x}
Dynamic c=z a=x b=y # [x, y, z] # [], {a: x, b: y, c: z}

位置和命名参数
Dynamic x b=y # [x, y] # [x], {b: y}
Dynamic x y c=z # [x, y, z] # [x, y], {c: z}
Dynamic x b=y c=z # [x, y, z] # [x], {y: b, c: z}

中间缺失
Dynamic x c=z # [x, d1, z] # [x], {c: z}

注意

在 Robot Framework 3.1 之前,所有正常的命名参数都被映射到位置参数,可选的 kwargs 只用于自由命名参数。在上述示例中,run_keyword 始终像现在如果不支持 kwargs 那样被调用。

动态库中的自由命名参数

动态库也可以支持自由命名参数(**named)。这种支持的强制前提条件是 run_keyword 方法接受三个参数:第三个参数将获取自由命名参数以及可能的其他命名参数。这些参数作为映射传递给关键字。

关键字接受哪些参数取决于 get_keyword_arguments 为它返回什么。如果最后一个参数以 ** 开头,那么该关键字被认为接受自由命名参数。

以下示例说明了如何使用动态库中的自由命名参数语法。所有示例都使用一个关键字 Dynamic,它的参数规范是 [a=d1, b=d2, **named]。注释显示了实际调用 run_keyword 方法的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*** 测试用例 ***                  # args, kwargs
无参数
Dynamic # [], {}

仅位置参数
Dynamic x # [x], {}
Dynamic x y # [x, y], {}

仅自由命名参数
Dynamic x=1 # [], {x: 1}
Dynamic x=1 y=2 z=3 # [], {x: 1, y: 2, z: 3}

位置和自由命名参数
Dynamic x y=2 # [x], {y: 2}
Dynamic x y=2 z=3 # [x], {y: 2, z: 3}

位置作为命名和自由命名参数
Dynamic a=1 x=1 # [], {a: 1, x: 1}
Dynamic b=2 x=1 a=1 # [], {a: 1, b: 2, x: 1}

注意

在 Robot Framework 3.1 之前,所有正常的命名参数都被映射到位置参数,但现在它们是 kwargs 的一部分,与自由命名参数一起。

动态库中的仅命名参数

从 Robot Framework 3.1 开始,动态库可以有仅命名参数。这需要 run_keyword 方法接受三个参数:第三个参数获取仅命名参数以及其他命名参数。

get_keyword_arguments 方法返回的参数规范中,仅命名参数在可能的可变数量的参数(varargs)之后指定,或者如果关键字不接受 varargs,则在一个孤立的星号()之后指定。仅命名参数可以有默认值,有默认值和没有默认值的参数的顺序无关紧要。

以下示例说明了如何使用动态库中的仅命名参数语法。所有示例都使用一个关键字 Dynamic,它被指定为具有参数规范 [positional=default, *varargs, named, named2=default, **free]。注释显示了实际调用 run_keyword 方法的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*** 测试用例 ***                                  # args, kwargs
仅命名参数
Dynamic named=value # [], {named: value}
Dynamic named=value named2=2 # [], {named: value, named2: 2}

命名参数与位置和 varargs
Dynamic argument named=xxx # [argument], {named: xxx}
Dynamic a1 a2 named=3 # [a1, a2], {named: 3}

位置作为命名的命名参数
Dynamic named=foo positional=bar # [], {positional: bar, named: foo}

命名参数与自由命名参数
Dynamic named=value foo=bar # [], {named: value, foo: bar}
Dynamic named2=2 third=3 named=1 # [], {named: 1, named2: 2, third: 3}
总结

下表列出了动态 API 中的所有特殊方法。方法名称以下划线格式列出,但它们的驼峰别名的工作方式完全相同。

动态 API 中的所有特殊方法

名称 参数 目的
get_keyword_names 返回实现的关键字的名称。
run_keyword name, arguments, kwargs 使用给定的参数执行指定的关键字。kwargs 是可选的。
get_keyword_arguments name 返回关键字的参数规范。可选方法。
get_keyword_types name 返回关键字的参数类型信息。可选方法。在 RF 3.1 中新增。
get_keyword_tags name 返回关键字的标签。可选方法。
get_keyword_documentation name 返回关键字和库的文档。可选方法。
get_keyword_source name 返回关键字的源。可选方法。在 RF 3.2 中新增。

使用动态 API 的一个好例子是 Robot Framework 自己的 Remote 库。

注意

从 Robot Framework 7.0 开始,动态库可以有它们的特殊方法的异步实现。

混合库 API

混合库 API,顾名思义,是静态 API 和动态 API 的混合。就像动态 API 一样,只使用混合 API 也可以实现一个库。

获取关键字名称

获取关键字名称的方式与动态 API 完全相同。实际上,库需要有 get_keyword_namesgetKeywordNames 方法,返回库实现的关键字名称的列表。

运行关键字

在混合 API 中,没有 run_keyword 方法用于执行关键字。相反,Robot Framework 使用反射来查找实现关键字的方法,这与静态 API 类似。使用混合 API 的库可以直接实现这些方法,更重要的是,它可以动态地处理它们。

在 Python 中,使用 __getattr__ 方法动态处理缺失的方法很容易。这个特殊方法可能对大多数 Python 程序员来说都很熟悉,他们可以立即理解以下示例。其他人可能会发现先查阅 Python 参考手册更容易。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from somewhere import external_keyword

class HybridExample:

def get_keyword_names(self):
return ['my_keyword', 'external_keyword']

def my_keyword(self, arg):
print("My Keyword called with '%s'" % arg)

def __getattr__(self, name):
if name == 'external_keyword':
return external_keyword
raise AttributeError("Non-existing attribute '%s'" % name)

注意,__getattr__ 不像动态 API 中的 run_keyword 那样执行实际的关键字。相反,它只返回一个可调用的对象,然后由 Robot Framework 执行。

另一个需要注意的点是,Robot Framework 使用从 get_keyword_names 返回的相同名称来查找实现它们的方法。因此,类本身实现的方法的名称必须以与它们定义的相同的格式返回。例如,如果 get_keyword_names 返回 My Keyword 而不是 my_keyword,那么上面的库将无法正确工作。

获取关键字参数和文档

当使用此 API 时,Robot Framework 使用反射来查找实现关键字的方法,这与静态 API 类似。获取到方法的引用后,它会以与使用静态 API 相同的方式从中搜索参数和文档。因此,没有必要像动态 API 那样有获取参数和文档的特殊方法。

总结

在实现测试库时,混合 API 具有与实际动态 API 相同的动态能力。它的一个很大的好处是,不需要有获取关键字参数和文档的特殊方法。通常,只有真正的动态关键字需要在 __getattr__ 中处理,其他的可以直接在主库类中实现。

由于明显的优势和相等的能力,混合 API 在大多数情况下都是比动态 API 更好的选择。一个值得注意的例外是将库实现为代理实际库实现的地方,因为那时实际的关键字必须在其他地方执行,代理只能向前传递关键字名称和参数。

使用混合 API 的一个好例子是 Robot Framework 自己的 Telnet 库。

使用 Robot Framework 的内部模块

测试库可以使用 Robot Framework 的内部模块,例如,获取有关执行的测试和使用的设置的信息。然而,应谨慎使用这种与框架通信的强大机制,因为并非所有的 Robot Framework 的 API 都是为外部使用而设计的,它们可能会在不同的框架版本之间发生根本性的变化。

可用的 API

API 文档在优秀的 Read the Docs 服务上单独托管。如果不确定如何使用某个 API 或者它是否具有向前兼容性,请向邮件列表发送问题。

使用 BuiltIn 库

最安全的 API 是在 BuiltIn 库中实现关键字的方法。关键字的更改很少,它们总是首先弃用旧的用法。最有用的方法之一是 replace_variables,它允许访问当前可用的变量。下面的示例演示了如何获取 ${OUTPUT_DIR},这是许多方便的自动变量之一。也可以使用 set_test_variableset_suite_variableset_global_variable 从库中设置新的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
import os.path
from robot.libraries.BuiltIn import BuiltIn

def do_something(argument):
builtin = BuiltIn()
output = do_something_that_creates_a_lot_of_output(argument)
if builtin.robot_running:
output_dir = builtin.replace_variables('${OUTPUT_DIR}')
else:
output_dir = '.'
with open(os.path.join(output_dir, 'output.txt'), 'w') as file:
file.write(output)
print('*HTML* Output written to <a href="output.txt">output.txt</a>')

如上例所示,BuiltIn 还有一个方便的 robot_running 属性,用于检测 Robot Framework 是否正在运行。

使用 BuiltIn 的方法的唯一问题是,所有 run_keyword 方法的变体都必须特别处理。使用 run_keyword 方法的方法必须使用 BuiltIn 模块中的 register_run_keyword 方法将自己注册为运行关键字。这个方法的文档解释了为什么需要这样做,显然也解释了如何做。

扩展现有的测试库

本节解释了如何向现有测试库添加新功能以及如何在自己的库中使用它们的不同方法。

修改原始源代码

如果可以访问想要扩展的库的源代码,可以直接修改源代码。这种方法的最大问题是,可能很难更新原始库而不影响更改。对于用户来说,使用具有与原始库不同功能的库可能也会令人困惑。重新打包库也可能是一个大的额外任务。

如果增强功能是通用的,并且计划将它们提交给原始开发人员,那么这种方法将非常有效。如果的更改被应用到原始库,它们将被包含在未来的版本中,并且上述所有问题都将得到缓解。如果更改是非通用的,或者由于某种原因无法提交它们,那么可能在接下来的部分中解释的方法会更好。

使用继承

扩展现有库的另一种直接方法是使用继承。下面的示例说明了如何向 SeleniumLibrary 添加新的 Title Should Start With 关键字。

1
2
3
4
5
6
7
8
9
10
11
from SeleniumLibrary import SeleniumLibrary
from SeleniumLibrary.base import keyword

class ExtendedSeleniumLibrary(SeleniumLibrary):

@keyword
def title_should_start_with(self, expected):
title = self.get_title()
if not title.startswith(expected):
raise AssertionError("Title '%s' did not start with '%s'"
% (title, expected))

与修改原始库相比,这种方法的一个大的区别是新库的名称与原始库不同。一个好处是可以轻松地告诉正在使用一个自定义库,但一个大问题是不能轻易地使用原始库和新库。首先,新库将具有与原始库相同的关键字,这意味着总是存在冲突。另一个问题是库不共享它们的状态。

当开始使用一个新库并希望从一开始就向它添加自定义增强时,这种方法效果很好。否则,本节中解释的其他机制可能会更好。

直接使用其他库

因为测试库在技术上只是类或模块,所以使用另一个库的简单方法是导入它并使用它的方法。当方法是静态的并且不依赖于库状态时,这种方法非常有效。这由早期使用 Robot Framework 的 BuiltIn 库的示例所说明。

然而,如果库有状态,事情可能不会按照希望的方式工作。在库中使用的库实例与框架使用的不是同一个,因此执行的关键字所做的更改对库不可见。下一节将解释如何获取对框架使用的同一库实例的访问权限。

从 Robot Framework 获取活动库实例

可以使用 BuiltIn 关键字 Get Library Instance 从框架本身获取当前活动的库实例。此关键字返回的库实例与框架本身使用的相同,因此看到正确的库状态没有问题。尽管这个功能作为关键字可用,但通常在测试库中直接通过导入 BuiltIn 库类来使用。下面的示例说明了如何实现与早期关于使用继承的示例中相同的 Title Should Start With 关键字。

1
2
3
4
5
6
7
8
from robot.libraries.BuiltIn import BuiltIn

def title_should_start_with(expected):
seleniumlib = BuiltIn().get_library_instance('SeleniumLibrary')
title = seleniumlib.get_title()
if not title.startswith(expected):
raise AssertionError("Title '%s' did not start with '%s'"
% (title, expected))

当库有状态时,这种方法显然比直接导入库并使用它更好。相对于继承,最大的好处是可以正常使用原始库,并在需要时使用新库。这在下面的示例中得到了证明,其中预期在新库 SeLibExtensions 中可以使用前面示例中的代码。

1
2
3
4
5
6
7
8
*** Settings ***
Library SeleniumLibrary
Library SeLibExtensions

*** Test Cases ***
Example
Open Browser http://example # SeleniumLibrary
Title Should Start With Example # SeLibExtensions

使用动态或混合 API 的库

使用动态或混合库 API 的测试库通常有自己的系统来扩展它们。对于这些库,需要向库开发人员询问指导,或者查阅库文档或源代码。

远程库接口

远程库接口提供了在 Robot Framework 运行的机器上有测试库的方法,也为使用原生支持的 Python 以外的其他语言实现库提供了方法。对于测试库,用户远程库看起来几乎与任何其他测试库一样,使用远程库接口开发测试库也非常接近创建普通测试库。

介绍

使用远程库 API 的两个主要原因:

  • 可以在 Robot Framework 运行的机器上有实际的库。这为分布式测试提供了有趣的可能性。
  • 测试库可以使用支持 XML-RPC 协议的任何语言来实现。存在为各种语言(如 Python、Java、Ruby、.NET 等)准备的通用远程服务器。

远程库接口由 Remote 库提供,这是标准库之一。这个库本身没有任何关键字,但它作为核心框架和其他地方实现的关键字之间的代理。Remote 库通过远程服务器与实际库实现进行交互,Remote 库和服务器使用 XML-RPC 通道上的简单远程协议进行通信。所有这些的高级架构在下面的图片中进行了说明:

注意

远程客户端使用 Python 的标准 XML-RPC 模块。它不支持一些 XML-RPC 服务器实现的自定义 XML-RPC 扩展。

使用 Remote 库

导入 Remote 库

Remote 库需要知道远程服务器的地址,但是导入它和使用它提供的关键字与使用其他库没有什么不同。如果需要在一个套件中多次使用 Remote 库,或者只是想给它一个更具描述性的名称,可以在导入时给它一个别名。

1
2
3
4
*** 设置 ***
库 Remote http://127.0.0.1:8270 AS Example1
库 Remote http://example.com:8080/ AS Example2
库 Remote http://10.0.0.2/example 1 minute AS Example3

上面的第一个示例使用的 URL 也是 Remote 库在没有给定地址时使用的默认地址。

上面的最后一个示例显示了如何给 Remote 库提供一个自定义超时作为可选的第二个参数。当最初连接到服务器时以及如果连接意外关闭时,将使用超时。超时可以以 Robot Framework 时间格式给出,如 60s 或 2 分钟 10 秒。默认超时通常是几分钟,但它取决于操作系统及其配置。注意,如果设置的超时比关键字执行时间短,将中断关键字。

注意

  • 端口 8270 是远程服务器预期使用的默认端口,已由 IANA 为此目的注册。选择这个端口号是因为 82 和 70 分别是字母 R 和 F 的 ASCII 码。
  • 当连接到本地机器时,建议使用 IP 地址 127.0.0.1 而不是机器名 localhost。这样可以避免地址解析,至少在 Windows 上可能非常慢。
  • 如果 URI 在服务器地址后没有路径,Remote 库使用的 XML-RPC 模块将默认使用 /RPC2 路径。实际上,使用 http://127.0.0.1:8270 与使用 http://127.0.0.1:8270/RPC2 是相同的。这可能会或可能不会成为问题,具体取决于远程服务器。如果地址有路径,即使路径只是 /,也不会添加额外的路径。例如,http://127.0.0.1:8270/ http://127.0.0.1:8270/my/path 都不会被修改。
启动和停止远程服务器

在可以导入 Remote 库之前,必须启动提供实际关键字的远程服务器。如果在启动测试执行之前启动了服务器,那么可以像上面的示例那样使用正常的库设置。或者,其他关键字,例如来自 Process 或 SSH 库的关键字,可以启动服务器,但是可能需要使用 Import Library 关键字,因为当测试执行开始时,库不可用。

如何停止远程服务器取决于它是如何实现的。通常,服务器支持以下方法:

  • 无论使用哪个库,远程服务器都应提供 Stop Remote Server 关键字,以便执行的测试可以轻松使用。
  • 远程服务器应在其 XML-RPC 接口中有 stop_remote_server 方法。
  • 在运行服务器的控制台上按 Ctrl-C 应该可以停止服务器。
  • 可以使用操作系统提供的工具终止服务器进程(例如 kill)。

注意

服务器可能被配置为用户不能使用 Stop Remote Server 关键字或 stop_remote_server 方法来停止它。

支持的参数和返回值类型

由于 XML-RPC 协议不支持所有可能的对象类型,因此必须将 Remote 库和远程服务器之间传输的值转换为兼容的类型。这适用于 Remote 库传递给远程服务器的关键字参数以及服务器返回给 Remote 库的返回值。

Remote 库和 Python 远程服务器都按照以下规则处理 Python 值。其他远程服务器应该有类似的行为。

  • 字符串、数字和布尔值在传递时不做修改。
  • Python 的 None 被转换为一个空字符串。
  • 所有列表、元组和其他可迭代对象(除字符串和字典外)都作为列表传递,以便递归地转换它们的内容。
  • 字典和其他映射作为字典传递,以便将它们的键转换为字符串,递归地将值转换为支持的类型。
  • 返回的字典被转换为所谓的点可访问字典,允许使用扩展变量语法像 {result.key} 这样访问键作为属性。这也适用于嵌套字典,如result.key这样访问键作为属性。这也适用于嵌套字典,如{root.child.leaf}。
  • 包含不能在 XML 中表示的 ASCII 范围内的字节的字符串被发送为 Binary 对象,该对象在内部使用 XML-RPC base64 数据类型。接收到的 Binary 对象自动转换为字节字符串。
  • 其他类型被转换为字符串。

远程协议

本节解释了 Remote 库和远程服务器之间使用的协议。这些信息主要针对希望创建新的远程服务器的人。

远程协议是在 XML-RPC 之上实现的,XML-RPC 是一种使用 XML 在 HTTP 上的简单远程过程调用协议。大多数主流语言(Python、Java、C、Ruby、Perl、Javascript、PHP等)都内置或作为扩展支持 XML-RPC。

Python 远程服务器可以作为参考实现。

必需的方法

远程服务器提供关于它们包含的关键字的信息有两种可能性。下面简要解释了这些方法,并在后续部分中进行了更详细的文档记录。

  • 远程服务器可以实现动态库 API 具有的相同方法。这意味着 get_keyword_names 方法和可选的 get_keyword_argumentsget_keyword_typesget_keyword_tagsget_keyword_documentation 方法。注意,像 getKeywordNames 这样使用 “驼峰命名” 是不可能的,就像在正常的动态 API 中一样。
  • 从 Robot Framework 4.0 开始,远程服务器可以有一个 get_library_information 方法,该方法返回一个字典,其中包含所有库和关键字信息。如果远程服务器有这个方法,那么像 get_keyword_names 这样的其他 getter 方法将根本不会被使用。这种方法的好处是只需要一个 XML-RPC 调用就可以获取信息,而上面解释的方法需要每个关键字进行几次调用。对于更大的库,差异可能很大。
  • 无论远程服务器如何提供关于它们的关键字的信息,它们都必须有 run_keyword 方法,当执行关键字时使用。实际关键字如何实现对 Remote 库来说并不重要。远程服务器可以像可用的通用远程服务器那样作为真实测试库的包装器,也可以自己实现关键字。

远程服务器还应在其公共接口中有 stop_remote_server 方法以方便停止它们。他们还应该自动将此方法作为 Stop Remote Server 关键字暴露出来,以允许在测试数据中使用它,无论测试库是什么。允许用户停止服务器并不总是可取的,服务器可能支持以某种方式禁用此功能。该方法,以及暴露的关键字,应该根据是否允许停止返回 True 或 False。这使得外部工具可以知道是否成功停止了服务器。

使用 get_keyword_names 和特定于关键字的 getter

本节解释了当服务器实现 get_keyword_names 时,Remote 库如何获取关键字名称和其他信息。下一节将介绍使用较新的 get_library_info 方法。

get_keyword_names 方法必须返回服务器包含的关键字的名称,作为字符串列表。远程服务器也可以(并且应该)实现 get_keyword_argumentsget_keyword_typesget_keyword_tagsget_keyword_documentation 方法,以提供有关关键字的更多信息。所有这些方法都以关键字的名称作为参数,它们必须返回的内容在下表中进行了解释。

关键字特定的 getter 方法

方法 返回值
get_keyword_arguments 以与动态库相同的格式返回字符串列表形式的参数。
get_keyword_types 类型信息,以字符串列表或字典形式返回。详情请参见下文。
get_keyword_documentation 文档,以字符串形式返回。
get_keyword_tags 标签,以字符串列表形式返回。

用于参数转换的类型信息可以以列表形式返回,基于位置将类型名称映射到参数,也可以直接以字典形式返回,将参数名称映射到类型名称。实际上,这与使用普通库中的 @keyword 装饰器指定类型的方式相同。区别在于,因为 XML-RPC 协议不支持任意值,所以需要使用类型名称或别名(如 ‘int’ 或 ‘integer’)来指定类型信息,而不是使用实际类型(如 int)。此外,XML-RPC 服务器可能不允许 None 或 null 值,但可以使用空字符串来表示某个参数没有类型信息。

也支持基于默认值的参数转换,使用与普通库相同的逻辑。为了使这个工作,具有默认值的参数必须以元组的形式返回,而不是字符串,就像动态库一样。例如,如果参数信息返回的是 [(‘count’, 1), (‘caseless’, True)],那么参数转换就会起作用,但如果是 [‘count=1’, ‘caseless=True’],则不会起作用。

远程服务器还可以提供通用库文档,用于使用 Libdoc 工具生成文档。通过调用 get_keyword_documentation 并使用特殊值 __intro____init__ 来获取这些信息。

注意

get_keyword_types 是在 Robot Framework 3.1 中新增的,基于默认值的参数转换支持是在 Robot Framework 4.0 中新增的。

使用 get_library_information

get_library_information 方法允许在一个 XML-RPC 调用中返回整个库的信息。信息必须以字典的形式返回,其中键是关键字名称,值是包含关键字信息的嵌套字典。字典也可以包含用于通用库信息的单独条目。

关键字信息字典可以包含关键字参数、文档、标签和类型,相应的键是 argsdoctagstypes。信息必须使用与前一节中讨论的 get_keyword_argumentsget_keyword_documentationget_keyword_tagsget_keyword_types 相同的语义提供。如果某些信息不可用,可以从 info 字典中完全省略。

get_library_information 还支持返回用于 Libdoc 的通用库文档。这是通过在返回的库信息字典中包含特殊的 __intro____init__ 条目来完成的。

例如,一个 Python 库如下:

1
2
3
4
5
6
7
8
9
10
11
"""库文档。"""

from robot.api.deco import keyword

@keyword(tags=['x', 'y'])
def example(a: int, b=True):
"""关键字文档。"""
pass

def another():
pass

可以映射到这种类型的库信息字典:

1
2
3
4
5
6
7
8
{
'__intro__': {'doc': '库文档'}
'example': {'args': ['a', 'b=True'],
'types': ['int'],
'doc': '关键字文档。',
'tags': ['x', 'y']}
'another: {'args': []}
}

注意

get_library_information 是在 Robot Framework 4.0 中新增的。

执行远程关键字

当 Remote 库希望服务器执行某个关键字时,它会调用远程服务器的 run_keyword 方法,并传递关键字名称、参数列表,以及可能的自由命名参数字典。基本类型可以直接作为参数使用,但更复杂的类型需要转换为支持的类型。

服务器必须在结果字典(或映射,取决于术语)中返回执行的结果,该字典包含以下表格中解释的项目。注意,只有状态条目是必需的,如果其他条目不适用,可以省略。

远程结果字典中的条目

名称 解释
status 必需的执行状态。可以是 PASS 或 FAIL。
output 可能写入日志文件的输出。必须以单个字符串给出,但可以包含多个消息和不同的日志级别,格式为 INFO First message\nHTML 2nd\nWARN Another message。也可以将时间戳嵌入到日志消息中,如 INFO:1308435758660 Message with timestamp。
return 可能的返回值。必须是支持的类型之一。
error 可能的错误消息。仅在执行失败时使用。
traceback 可能的堆栈跟踪,当执行失败时,使用 DEBUG 级别写入日志文件。
continuable 当设置为 True,或者在 Python 中被认为是 True 的任何值,发生的失败被认为是可继续的。
fatal 像 continuable,但表示发生的失败是致命的。
不同的参数语法

Remote 库是一个动态库,通常,它根据与任何其他动态库相同的规则处理不同的参数语法。这包括必需的参数、默认值、varargs,以及命名参数语法。

自由命名参数(**kwargs)也大多数情况下与其他动态库的工作方式相同。首先,get_keyword_arguments 必须返回一个包含 **kwargs 的参数规范,就像任何其他动态库一样。主要的区别在于,远程服务器的 run_keyword 方法必须有一个可选的第三个参数,用于获取用户指定的 kwargs。第三个参数必须是可选的,因为出于向后兼容性的原因,Remote 库只有在测试数据中使用了 kwargs 时,才会将 kwargs 传递给 run_keyword 方法。

实际上,run_keyword 应该看起来像下面的 Python 和 Java 示例,具体取决于语言如何处理可选参数。

1
2
3
4
5
6
7
8
9
def run_keyword(name, args, kwargs=None):
# ...
public Map run_keyword(String name, List args) {
// ...
}

public Map run_keyword(String name, List args, Map kwargs) {
// ...
}

监听器接口

Robot Framework 的监听器接口提供了一个强大的机制,用于在执行过程中获取通知以及检查和修改数据和结果。例如,当套件、测试和关键字开始和结束时,当输出文件准备好时,以及最后整个执行结束时,都会调用监听器。示例用途包括与外部测试管理系统通信,当测试失败时发送消息,以及在执行过程中修改测试。

监听器以具有某些特殊方法的类或模块的形式实现。它们可以从命令行中使用,并由库注册。前者的监听器在整个执行过程中都是活动的,而后者只在执行注册它们的库的套件时活动。

有两种支持的监听器接口版本,监听器版本 2 和监听器版本 3。它们的方法大致相同,但这些方法被调用时的参数不同。新的监听器版本 3 更强大,通常推荐使用。

监听器结构

监听器以与库类似的方式实现为模块或类。它们可以实现某些命名的钩子方法,具体取决于它们对哪些事件感兴趣。例如,如果监听器希望在测试开始时收到通知,它可以实现 start_test 方法。如后续部分所述,不同的监听器版本有稍微不同的可用方法集,它们也被调用时的参数不同。

1
2
3
4
5
6
7
# 使用监听器 API 版本 3 实现的模块监听器。

def start_suite(data, result):
print(f"Suite '{data.name}' starting.")

def end_test(data, result):
print(f"Test '{result.name}' ended with status {result.status}.")

监听器不需要实现任何显式接口,只需简单地实现所需的方法,它们将被自动识别。然而,有基类 robot.api.interfaces.ListenerV2robot.api.interfaces.ListenerV3 可以用来在编辑器中获取方法名称完成、类型提示等。

1
2
3
4
5
6
7
8
9
10
11
12
# 与上面的示例相同,但使用了可选的基类和类型提示。

from robot import result, running
from robot.api.interfaces import ListenerV3

class Example(ListenerV3):

def start_suite(self, data: running.TestSuite, result: result.TestSuite):
print(f"Suite '{data.name}' starting.")

def end_test(self, data: running.TestCase, result: result.TestCase):
print(f"Test '{result.name}' ended with status {result.status}.")

注意

  • 可选的监听器基类是在 Robot Framework 6.1 中新增的。
  • 除了使用 “蛇形命名法”(如 start_test)与监听器方法名称外,还可以使用 “驼峰命名法”(如 startTest)。当可以在 Jython 上运行 Robot Framework 并使用 Java 实现监听器时,添加了此支持。出于向后兼容性的原因,它被保留下来,但不推荐在新的监听器中使用。

监听器接口版本

有两个支持的监听器接口版本,版本号分别为 2 和 3。监听器可以通过具有值 2 或 3 的 ROBOT_LISTENER_API_VERSION 属性来指定要使用的版本。从 Robot Framework 7.0 开始,如果未指定版本,则默认使用监听器版本 3。

监听器版本 2 和监听器版本 3 的方法大致相同,但传递给这些方法的参数不同。给监听器 2 方法的参数是字符串和包含执行信息的字典。可以检查和进一步发送此信息,但无法直接修改它。监听器 3 方法获取 Robot Framework 本身使用的相同模型对象,这些模型对象可以被检查和修改。

监听器版本 3 比较旧的监听器版本 2 更强大,通常推荐使用。

监听器版本 2

使用监听器 API 版本 2 的监听器在执行过程中会收到关于各种事件的通知,但它们无法访问实际执行的测试,因此无法直接影响执行或创建的结果。

API 版本 2 中的监听器方法列在下面的表格和可选的 ListenerV2 基类的 API 文档中。所有与测试执行进度相关的方法都有相同的签名 method(name, attributes),其中 attributes 是一个包含事件详细信息的字典。监听器方法可以自由地处理它们接收到的信息,但不能直接更改它。如果需要,可以使用监听器版本 3。

方法 参数 文档说明描述
start_suite name, attributes 当测试套件开始时调用。包含以下属性字典内容:
id: 套件id,如顶级套件为s1,第一个子套件为s1-s1,以此类推。
longname: 包含父套件的套件名称。
doc: 套件文档。
metadata: 自由套件元数据,以字典形式。
source: 套件创建来源的文件/目录的绝对路径。
suites: 此套件具有的直接子套件的名称列表。
totaltests: 此套件及其所有子套件中测试的总数,以整数形式。
starttime: 套件执行开始时间。
end_suite name, attributes 当测试套件结束时调用。包含以下属性字典内容:
id: 同start_suite。
longname: 同start_suite。
doc: 同start_suite。
metadata: 同start_suite。
source: 同start_suite。
starttime: 同start_suite。
endtime: 套件执行结束时间。
elapsedtime: 总执行时间,以毫秒为单位的整数。
status: 套件状态,为字符串PASS、FAIL或SKIP。
statistics: 套件的统计信息,包括通过和未通过测试的数量,以字符串形式。
message: 如果套件设置或拆卸失败的错误信息,否则为空。
start_test name, attributes 当测试用例开始时调用。包含以下属性字典内容:
id: 测试id,格式如s1-s2-t2,其中开头是父套件id,最后一部分显示该套件中的测试索引。
longname: 包括父套件的测试名称。
originalname: 可能未解析变量的测试名称。RF 3.2新功能。
doc: 测试文档。
tags: 测试标签,为字符串列表。
template: 用于测试的模板名称。如果测试未模板化,则为空字符串。
source: 测试用例源文件的绝对路径。RF 4.0新功能。
lineno: 测试在源文件中开始的行号。RF 3.2新功能。
starttime: 测试执行开始时间。
end_test name, attributes 当测试用例结束时调用。包含以下属性字典内容:
id: 与start_test相同。
longname: 与start_test相同。
originalname: 与start_test相同。
doc: 与start_test相同。
tags: 与start_test相同。
template: 与start_test相同。
source: 与start_test相同。
lineno: 与start_test相同。
starttime: 与start_test相同。
endtime: 测试执行结束时间。
elapsedtime: 总执行时间,以毫秒为单位的整数。
status: 测试状态,为字符串PASS、FAIL或SKIP。
message: 状态消息。通常是错误消息或空字符串。
start_keyword name, attributes 当一个关键字或一个控制结构(如 IF/ELSE 或 TRY/EXCEPT)开始时调用:
对于关键字,name 是完整的关键字名称,包含可能的库或资源名称作为前缀,如 MyLibrary.Example Keyword。对于控制结构,name 包含参数的字符串表示。
关键字和控制结构共享大部分属性,但控制结构可以根据其类型具有额外的属性。
共享属性:
type: 指定开始项类型的字符串。可能的值有:KEYWORD、SETUP、TEARDOWN、FOR、WHILE、ITERATION、IF、ELSE IF、ELSE、TRY、EXCEPT、FINALLY、VAR、RETURN、BREAK、CONTINUE 和 ERROR。所有类型值在 RF 4.0 中改变,在 RF 5.0 中 FOR ITERATION 被改为 ITERATION。
kwname: 关键字名称,不包含库或资源前缀。控制结构的参数的字符串表示。
libname: 关键字所属的库或资源文件的名称。在测试用例文件中的用户关键字和控制结构中为空字符串。
doc: 关键字文档。
args: 关键字的参数,以字符串列表形式。
assign: 关键字返回值被赋值的变量名称列表。
tags: 关键字标签,以字符串列表形式。
source: 使用关键字的文件的绝对路径。在 RF 4.0 中新增。
lineno: 使用关键字的行号。在 RF 4.0 中新增。
status: 初始关键字状态。如果关键字未执行(例如,由于之前的失败),则为 NOT RUN,否则为 NOT SET。在 RF 4.0 中新增。
starttime: 关键字执行开始时间。
FOR 类型的额外属性:
variables: 每个循环迭代的分配变量,以字符串列表形式。
flavor: 循环类型(例如 IN RANGE)。
values: 循环遍历的值列表,以字符串列表形式。
start: 开始配置。仅在 IN ENUMERATE 循环中使用。在 RF 6.1 中新增。
mode: 模式配置。仅在 IN ZIP 循环中使用。在 RF 6.1 中新增。
fill: 填充值配置。仅在 IN ZIP 循环中使用。在 RF 6.1 中新增。
FOR 循环的 ITERATION 类型的额外属性:
variables: 一个 FOR 循环迭代的变量及其内容的字符串表示,以字典形式。
WHILE 类型的额外属性:
condition: 循环条件。
limit: 最大迭代限制。
on_limit: 如果超过限制,该怎么做。有效值是 pass 和 fail。在 RF 7.0 中新增。
on_limit_message: 达到 WHILE 循环的限制时引发的自定义错误。在 RF 6.1 中新增。
IF 和 ELSE IF 类型的额外属性:
condition: 正在评估的条件表达式。在 RF 6.1 中新增的 ELSE IF。
EXCEPT 类型的额外属性:
patterns: 匹配的异常模式,以字符串列表形式。
pattern_type: 模式匹配类型(例如 GLOB)。
variable: 包含捕获的异常的变量。
RETURN 类型的额外属性:
values: 关键字的返回值,以字符串列表形式。
VAR 类型的额外属性:
name: 变量名称。
value: 变量值。标量变量为字符串,否则为列表。
scope: 变量范围(例如 GLOBAL),以字符串形式。
控制结构的额外属性通常在 RF 6.0 中新增。VAR 在 RF 7.0 中新增。
end_keyword name, attributes 当一个关键字或一个控制结构结束时会被调用。
name 是完整的关键字名称,包含可能的库或资源名称作为前缀。例如,MyLibrary.Example Keyword
控制结构具有额外的属性,这些属性根据 type 属性变化。有关所有可能属性的描述,请参见 start_keyword 部分。
属性字典的内容:
type: 与 start_keyword 相同。
kwname: 与 start_keyword 相同。
libname: 与 start_keyword 相同。
doc: 与 start_keyword 相同。
args: 与 start_keyword 相同。
assign: 与 start_keyword 相同。
tags: 与 start_keyword 相同。
source: 与 start_keyword 相同。
lineno: 与 start_keyword 相同。
starttime: 与 start_keyword 相同。
endtime: 关键字执行结束时间。
elapsedtime: 总执行时间,以毫秒为单位的整数。
status: 关键字状态,字符串 PASS、FAIL、SKIP 或 NOT RUN。SKIP 和 NOT RUN 在 RF 4.0 中新增。
log_message message 当执行的关键字写入日志消息时会被调用。
message 是一个字典,包含以下内容:
message: 消息的内容。
level: 用于记录消息的日志级别。
timestamp: 消息创建时间,格式为 YYYY-MM-DD hh:mm:ss.mil。
html: 字符串 yes 或 no,表示是否应将消息解释为 HTML。
如果消息级别低于当前阈值级别,则不会调用此方法。
message message 当框架本身写入系统日志消息时会被调用。
message 是一个字典,其内容与 log_message 方法相同。
library_import name, attributes 当导入一个库时会被调用。
name 是导入的库的名称。如果在使用 AS 导入库时给库指定了自定义名称,name 就是指定的别名。
属性字典的内容:
args: 传递给库的参数,以列表形式。
originalname: 如果使用 AS 给库指定了别名,则为原始库名称,否则与 name 相同。
source: 库源的绝对路径。如果由于某种原因获取库的源失败,则为 None。
importer: 导入库的文件的绝对路径。当导入 BuiltIn 以及使用 Import Library 关键字时为 None。
resource_import name, attributes 当导入资源文件时会被调用。
name 是导入的资源文件的名称,不包含文件扩展名。
属性字典的内容:
source: 导入的资源文件的绝对路径。
importer: 导入资源文件的文件的绝对路径。使用 Import Resource 关键字时为 None。
variables_import name, attributes 当导入变量文件时会被调用。
name 是导入的变量文件的名称,包含文件扩展名。
属性字典的内容:
args: 传递给变量文件的参数,以列表形式。
source: 导入的变量文件的绝对路径。
importer: 导入资源文件的文件的绝对路径。使用 Import Variables 关键字时为 None。
output_file path 当准备好写入输出文件时会被调用。
path 是文件的绝对路径,以字符串形式表示。
log_file path 当准备好写入日志文件时会被调用。
path 是文件的绝对路径,以字符串形式表示。
report_file path 当准备好写入报告文件时会被调用。
path 是文件的绝对路径,以字符串形式表示。
xunit_file path 当准备好写入 xunit 文件时会被调用。
path 是文件的绝对路径,以字符串形式表示。
debug_file path 当准备好写入调试文件时会被调用。
path 是文件的绝对路径,以字符串形式表示。
close 当整个测试执行结束时会被调用。
对于库监听器,当库超出范围时会被调用。
监听器版本 3

监听器版本 3 的方法大多与监听器版本 2 相同,但与测试执行相关的方法的参数不同。这些方法获取 Robot Framework 本身使用的实际运行和结果模型对象,监听器既可以查询它们需要的信息,也可以即时更改模型对象。

在 Robot Framework 7.0 中,当它获得了与关键字和控制结构相关的方法时,对监听器版本 3 进行了大量增强。它仍然没有与库、资源文件和变量文件导入相关的方法,但计划在 Robot Framework 7.1 中添加它们。

API 版本 3 中的监听器方法列在下面的表格和可选的 ListenerV3 基类的 API 文档中。

方法 参数 文档说明描述
start_suite data, result 当测试套件开始时调用。dataresult 是表示执行的测试套件及其执行结果的模型对象。
end_suite data, result 当测试套件结束时调用。参数与 start_suite 相同。
start_test data, result 当测试用例开始时调用。dataresult 是表示执行的测试用例及其执行结果的模型对象。
end_test data, result 当测试用例结束时调用。参数与 start_test 相同。
start_keyword data, result 当关键字开始时调用。dataresult 是表示执行的关键字调用及其执行结果的模型对象。默认情况下,当用户关键字、库关键字开始时,以及当关键字调用无效时,会调用此方法。如果实现了更具体的 start_user_keywordstart_library_keywordstart_invalid_keyword 方法,则不会调用此方法。
end_keyword data, result 当关键字开始时调用。参数和其他语义与 start_keyword 相同。
start_user_keyword data, implementation, result 当用户关键字开始时调用。dataresultstart_keyword 相同,implementation 是实际执行的用户关键字。如果实现了此方法,start_keyword 不会在用户关键字中被调用。
end_user_keyword data, implementation, result 当用户关键字结束时调用。参数和其他语义与 start_user_keyword 相同。
start_library_keyword data, implementation, result 当库关键字开始时调用。dataresultstart_keyword 相同,implementation 表示执行的库关键字。如果实现了此方法,start_keyword 不会在库关键字中被调用。
end_library_keyword data, implementation, result 当库关键字结束时调用。参数和其他语义与 start_library_keyword 相同。
start_invalid_keyword data, implementation, result 当无效的关键字调用开始时调用。dataresultstart_keyword 相同,implementation 表示无效的关键字调用。关键字可能没有被找到,可能有多个匹配,或者关键字调用本身可能无效。如果实现了此方法,start_keyword 不会在无效的关键字调用中被调用。
end_invalid_keyword data, implementation, result 当无效的关键字调用结束时调用。参数和其他语义与 start_invalid_keyword 相同。
start_for, start_for_iteration, start_while, start_while_iteration, start_if, start_if_branch, start_try, start_try_branch, start_var, start_continue, start_break, start_return data, result 当控制结构开始时调用。有关更多信息,请参阅可选的 ListenerV3 基类的文档和类型提示。
end_for, end_for_iteration, end_while, end_while_iteration, end_if, end_if_branch, end_try, end_try_branch, end_var, end_continue, end_break, end_return data, result 当控制结构结束时调用。有关更多信息,请参阅可选的 ListenerV3 基类的文档和类型提示。
start_error data, result 当无效语法开始时调用。
end_error data, result 当无效语法结束时调用。
log_message message 当执行的关键字写入日志消息时调用。message 是表示记录的消息的模型对象。如果消息的级别低于当前阈值级别,此方法不会被调用。
message message 当框架本身写入系统日志消息时调用。messagelog_message 中的对象相同。
library_import N/A 目前未实现。
resource_import N/A 目前未实现。
variables_import N/A 目前未实现。
output_file path 当准备好写入输出文件时调用。path 是文件的绝对路径,作为 pathlib.Path 对象。
log_file path 当准备好写入日志文件时调用。path 是文件的绝对路径,作为 pathlib.Path 对象。
report_file path 当准备好写入报告文件时调用。path 是文件的绝对路径,作为 pathlib.Path 对象。
xunit_file path 当准备好写入 xunit 文件时调用。path 是文件的绝对路径,作为 pathlib.Path 对象。
debug_file path 当准备好写入调试文件时调用。path 是文件的绝对路径,作为 pathlib.Path 对象。
close 当整个测试执行结束时调用。对于库监听器,当库超出范围时会被调用。

注意

在 Robot Framework 7.0 之前,传递给结果文件相关的监听器版本 3 方法的路径是字符串。

使用监听器

从命令行注册监听器

需要在整个执行过程中保持活动的监听器必须从命令行中使用。这是通过使用 --listener 选项完成的,将监听器的名称作为参数给出。监听器的名称来自实现监听器的类或模块的名称,类似于库名称来自实现库的类或模块的名称。指定的监听器必须在导入它们时搜索测试库的同一模块搜索路径中。除了使用名称注册监听器外,还可以给出监听器文件的绝对路径或相对路径,类似于测试库。可以通过多次使用此选项来使用多个监听器:

1
2
3
robot --listener MyListener tests.robot
robot --listener path/to/MyListener.py tests.robot
robot --listener module.Listener --listener AnotherListener tests.robot

还可以从命令行给监听器类提供参数。参数在监听器名称(或路径)后使用冒号(:)作为分隔符指定。如果监听器被给出为绝对 Windows 路径,则驱动器字母后的冒号不被视为分隔符。此外,还可以使用分号(;)作为替代参数分隔符。这在监听器参数本身包含冒号时很有用,但在 UNIX-like 操作系统上需要用引号包围整个值:

1
2
3
robot --listener listener.py:arg1:arg2 tests.robot
robot --listener "listener.py;arg:with:colons" tests.robot
robot --listener c:\path\listener.py;d:\first\arg;e:\second\arg tests.robot

除了逐个传递参数作为位置参数外,还可以使用命名参数语法传递它们,类似于使用关键字时:

1
2
robot --listener listener.py:name=value tests.robot
robot --listener "listener.py;name=value:with:colons;second=argument" tests.robot

监听器参数会根据类型提示和默认值自动转换,使用与关键字相同的规则。例如,这个监听器

1
2
3
4
5
class Listener:

def __init__(self, port: int, log=True):
self.port = post
self.log = log

可以这样使用

1
robot --listener Listener:8270:false

并且第一个参数将根据类型提示转换为整数,第二个参数将根据默认值转换为布尔值。

注意

命名参数语法和参数转换在 Robot Framework 4.0 中是新的。

作为监听器的库

有时,对于测试库来说,获取关于测试执行的通知也是有用的。例如,这允许它们在测试套件或整个测试执行结束时自动执行某些清理活动。

注册监听器

测试库可以通过使用 ROBOT_LIBRARY_LISTENER 属性来注册监听器。此属性的值应为要使用的监听器的实例。它可能是一个完全独立的监听器,或者库本身可以充当监听器。为了避免监听器方法被暴露为关键字,在后一种情况下,可以使用下划线作为前缀。例如,可以使用 _end_suite 而不是 end_suite

以下示例说明了使用外部监听器以及库本身充当监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from listener import Listener

class LibraryWithExternalListener:
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
ROBOT_LIBRARY_LISTENER = Listener()

def example_keyword(self):
...

class LibraryItselfAsListener:
ROBOT_LIBRARY_SCOPE = 'SUITE'
ROBOT_LISTENER_API_VERSION = 2

def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self

# 使用 '_' 前缀以避免监听器方法成为关键字。
def _end_suite(self, name, attrs):
print(f"Suite '{name}' ending with status {attrs['id']}.")

def example_keyword(self):
...

如上面的第二个示例已经演示,库监听器可以使用 ROBOT_LISTENER_API_VERSION 属性指定监听器接口版本,就像任何其他监听器一样。

从 Robot Framework 7.0 开始,监听器也可以通过使用字符串 SELF(不区分大小写)作为监听器来注册自己。这在使用 @library 装饰器时特别方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from robot.api.deco import keyword, library

@library(scope='SUITE', listener='SELF')
class LibraryItselfAsListener:

# 监听器版本未指定,因此默认使用监听器版本 3。
# 当使用 @library 装饰器时,关键字必须使用 @keyword 装饰器,
# 因此这里无需使用 '_' 前缀。
def end_suite(self, data, result):
print(f"Suite '{data.name}' ending with status {result.status}.")

@keyword
def example_keyword(self):
...

也可以通过给 ROBOT_LIBRARY_LISTENER 一个列表值来为单个库指定多个监听器:

1
2
3
4
5
6
7
from listeners import Listener1, Listener2, Listener3

class LibraryWithMultipleListeners:
ROBOT_LIBRARY_LISTENER = [Listener1(), Listener2(), Listener3()]

def example_keyword(self):
...
调用监听器方法

库监听器会收到在导入使用它们的库的套件中的所有事件的通知。实际上,这意味着会调用套件、测试、关键字、控制结构和日志消息相关的方法。除此之外,当库超出范围时,还会调用 close 方法。

如果库每次实例化时都创建一个新的监听器实例,那么要使用的实际监听器实例将根据库的范围而变化。

监听器示例

本节包含使用监听器接口的示例。前面的示例说明了在执行过程中获取通知,后面的示例修改了执行的测试和创建的结果。

获取信息

第一个示例是作为 Python 模块实现的。它使用的是监听器版本 2,但同样可以使用监听器版本 3 来实现。

1
2
3
4
5
6
7
8
"""如果测试失败,停止执行的监听器。"""

ROBOT_LISTENER_API_VERSION = 2

def end_test(name, attrs):
if attrs['status'] == 'FAIL':
print(f"Test '{name}' failed: {attrs['message']}")
input("Press enter to continue.")

如果将上述示例保存为,例如,PauseExecution.py 文件,那么可以像这样从命令行中使用它:

1
robot --listener path/to/PauseExecution.py tests.robot

下一个示例(仍然使用监听器版本 2)稍微复杂一些。它将获取的所有信息写入临时目录中的文本文件,没有太多格式化。文件名可以从命令行给出,但也有默认值。请注意,在实际使用中,通过命令行选项 --debugfile 可用的调试文件功能可能比这个示例更有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os.path
import tempfile

class Example:
ROBOT_LISTENER_API_VERSION = 2

def __init__(self, file_name='listen.txt'):
path = os.path.join(tempfile.gettempdir(), file_name)
self.file = open(path, 'w')

def start_suite(self, name, attrs):
self.file.write("%s '%s'\n" % (name, attrs['doc']))

def start_test(self, name, attrs):
tags = ' '.join(attrs['tags'])
self.file.write("- %s '%s' [ %s ] :: " % (name, attrs['doc'], tags))

def end_test(self, name, attrs):
if attrs['status'] == 'PASS':
self.file.write('PASS\n')
else:
self.file.write('FAIL: %s\n' % attrs['message'])

def end_suite(self, name, attrs):
self.file.write('%s\n%s\n' % (attrs['status'], attrs['message']))

def close(self):
self.file.close()

修改数据和结果

以下示例说明了如何修改执行的测试和套件以及执行结果。所有这些示例都需要使用监听器版本 3。

修改执行的套件和测试

更改执行内容就像修改传递给监听器方法的表示执行数据的模型对象一样简单。下面的示例说明了如何向每个执行的套件添加一个新的测试,并向每个测试添加一个新的关键字调用。

1
2
3
4
5
def start_suite(data, result):
data.tests.create(name='New test')

def start_test(data, result):
data.body.create_keyword(name='Log', args=['Keyword added by listener!'])

此 API 与可以用于在整个测试执行开始之前修改套件和测试的预运行修改器 API 非常相似。使用监听器 API 的主要好处是可以根据执行结果或其他方式动态地进行修改。例如,这为基于模型的测试提供了有趣的可能性。

尽管监听器接口并未建立在 Robot Framework 的内部访问者接口之上,就像预运行修改器 API 一样,但监听器仍然可以自己使用访问者接口。例如,可以像这样使用预运行修改器示例中使用的 SelectEveryXthTest 访问者:

1
2
3
4
5
from SelectEveryXthTest import SelectEveryXthTest

def start_suite(suite, result):
selector = SelectEveryXthTest(x=2)
suite.visit(selector)
访问库或资源文件

可以获取更多关于实际执行的关键字及其所属的库或资源文件的信息:

1
2
3
4
5
6
7
8
9
10
11
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult

def start_library_keyword(data: KeywordData,
implementation: LibraryKeyword,
result: KeywordResult):
library = implementation.owner
print(f"Keyword '{implementation.name}' is implemented in library "
f"'{library.name}' at '{implementation.source}' on line "
f"{implementation.lineno}. The library has {library.scope.name} "
f"scope and the current instance is {library.instance}.")

如上例所示,可以获取到实际库实例的访问权限。这意味着监听器可以检查库状态并修改它。对于用户关键字,甚至可以修改关键字本身,或者通过所有者资源文件,修改资源文件中的任何其他关键字。

修改结果

可以通过修改传递给监听器方法的结果对象来改变测试执行结果。以下是一个作为类实现并使用类型提示的监听器的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from robot import result, running

class ResultModifier:

def __init__(self, max_seconds: float = 10.0):
self.max_seconds = max_seconds

def start_suite(self, data: running.TestSuite, result: result.TestSuite):
result.doc = '由监听器设置的文档。'
# 关于测试的信息只能通过数据获得。
smoke_tests = [test for test in data.tests if 'smoke' in test.tags]
result.metadata['Smoke tests'] = len(smoke_tests)

def end_test(self, data: running.TestCase, result: result.TestCase):
elapsed_seconds = result.elapsed_time.total_seconds()
if result.status == 'PASS' and elapsed_seconds > self.max_milliseconds:
result.status = 'FAIL'
result.message = '测试执行时间过长。'

def log_message(self, msg: result.Message):
if msg.level == 'WARN' and not msg.html:
msg.message = f'<b style="font-size: 1.5em">{msg.message}</b>'
msg.html = True

一个限制是,修改当前测试套件或测试用例的名称是不可能的,因为在调用监听器时,它已经被写入到 output.xml 文件中。由于同样的原因,在 end_suite 方法中修改已经完成的测试也没有效果。

请注意,尽管监听器可以改变任何执行的关键字或控制结构的状态,但这并不直接影响执行的测试的状态。一般来说,监听器不能直接使关键字失败,以便停止执行,也不能处理失败,以便继续执行。如果有需要,将来可能会添加这种功能。

此 API 与 pre-Rebot 修改器 API 非常相似,可以用于在生成报告和日志之前修改结果。主要的区别是,监听器也修改了创建的 output.xml 文件。

更多示例

关键字和控制结构相关的监听器版本 3 方法如此多样,以至于在用户指南中无法完全覆盖它们。有关更多示例,可以查看接受测试,这些测试以各种方式使用这些方法。

解析器接口

Robot Framework 支持可以处理自定义数据格式甚至覆盖 Robot Framework 自己的解析器的外部解析器。

注意

自定义解析器在 Robot Framework 6.1 中是新的。

使用解析器

解析器是从命令行使用 --parser 选项启用的,使用的语义与监听器完全相同。这包括指定解析器为名称或路径,给解析器类提供参数等:

1
2
3
robot --parser MyParser tests.custom
robot --parser path/to/MyParser.py tests.custom
robot --parser Parser1:arg --parser Parser2:a1:a2 path/to/tests

解析器 API

解析器可以作为模块和类来实现。本节解释了它们必须包含哪些属性和方法。

EXTENSION 或 extension 属性

此属性指定解析器支持的文件扩展名或扩展名。接受 EXTENSIONextension 名称,如果两者都存在,前者优先。该属性可以是字符串或字符串序列。扩展名不区分大小写,可以带或不带前导点。如果解析器作为类实现,可以将此属性设置为类属性或实例属性。

也支持包含多个部分的扩展名,如 .example.ext.robot.zip

注意

如果解析器支持 .robot 扩展名,它将用于解析这些文件,而不是标准解析器。

parse 方法

强制性的 parse 方法负责解析套件文件。它被调用时,每个解析的文件都有一个解析器支持的扩展名。该方法必须返回一个 TestSuite 对象。

在简单的情况下,parse 可以被实现为只接受一个参数,即指向要解析的文件的 pathlib.Path 对象。如果解析器对在更高级别的套件初始化文件中设置的 Test SetupTest TeardownTest TagsTest Timeout 的默认值感兴趣,parse 方法必须接受两个参数。在这种情况下,第二个参数是一个 TestDefaults 对象。

parse_init 方法

可选的 parse_init 方法负责解析套件初始化文件,即格式为 __init__.ext 的文件,其中 .ext 是解析器支持的扩展名。该方法必须返回一个表示整个目录的 TestSuite 对象。从子套件文件和目录创建的套件将被添加到其子套件中。

parse_init 也可以被实现为接受一个或两个参数,取决于它是否对测试相关的默认值感兴趣。如果它接受默认值,它可以操作传递的 TestDefaults 对象,并在解析子套件文件时看到更改。

只有当解析器需要支持套件初始化文件时,才需要此方法。

可选的基类

解析器不需要实现任何显式的接口,但扩展可选的 Parser 基类可能会有所帮助。主要的好处是基类有文档和类型提示。它也作为更正式的 API 规范。

示例

作为模块实现的解析器

第一个示例演示了一个作为模块实现的简单解析器,支持一个硬编码的扩展名。它只创建一个虚拟套件,并不实际解析任何内容。

1
2
3
4
5
6
7
8
9
from robot.api import TestSuite

EXTENSION = '.example'

def parse(source):
suite = TestSuite(name='Example', source=source)
test = suite.tests.create(name='Test')
test.body.create_keyword(name='Log', args=['Hello!'])
return suite
作为类实现的解析器

第二个解析器作为一个类实现,接受要使用的扩展名作为参数。解析器读取给定的源文件,并从其中的每一行创建虚拟测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pathlib import Path
from robot.api import TestSuite

class ExampleParser:

def __init__(self, extension: str):
self.extension = extension

def parse(self, source: Path) -> TestSuite:
name = TestSuite.name_from_source(source, self.extension)
suite = TestSuite(name, source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line)
test.body.create_keyword(name='Log', args=['Hello!'])
return suite
扩展可选基类的解析器

这个解析器扩展了可选的 Parser 基类。它支持解析套件初始化文件,使用 TestDefaults 并注册多个扩展名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pathlib import Path
from robot.api import TestSuite
from robot.api.interfaces import Parser, TestDefaults

class ExampleParser(Parser):
extension = ('example', 'another')

def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""创建一个套件并将可能的默认值从 init 文件设置到测试中。"""
suite = TestSuite(TestSuite.name_from_source(source), source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line, doc='Example')
test.body.create_keyword(name='Log', args=['Hello!'])
defaults.set_to(test)
return suite

def parse_init(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""创建一个虚拟套件并设置一些默认值。

只有在有一个支持的扩展名的初始化文件时,才会调用此方法。
"""
defaults.tags = ('tags', 'from init')
defaults.setup = {'name': 'Log', 'args': ['Hello from init!']}
return TestSuite(TestSuite.name_from_source(source.parent), doc='Example',
source=source, metadata={'Example': 'Value'})
作为预处理器的解析器

最后一个示例解析器充当 Robot Framework 数据文件的预处理器,支持格式为 === Test Cases === 的标题,除了 *** Test Cases ***。在这种使用情况下,使用 TestSuite.from_stringTestSuite.from_modelTestSuite.from_file_system 工厂方法构造返回的套件是方便的。

1
2
3
4
5
6
7
8
9
10
11
12
from pathlib import Path
from robot.running import TestDefaults, TestSuite

class RobotPreprocessor:
extension = '.robot'

def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
data = source.read_text()
for header in 'Settings', 'Variables', 'Test Cases', 'Keywords':
data = data.replace(f'=== {header} ===', f'*** {header} ***')
suite = TestSuite.from_string(data, defaults=defaults)
return suite.config(name=TestSuite.name_from_source(source), source=source)

辅助工具

库文档工具(Libdoc)

Libdoc 是 Robot Framework 的内置工具,可以为 Robot Framework 库和资源文件生成文档。它可以为人类生成 HTML 文档,也可以生成 XML 和 JSON 格式的机器可读规范文件。Libdoc 还有一些特殊命令可以在控制台上显示库或资源信息。

可以为以下内容创建文档:

  • 使用普通静态库 API 实现的库,
  • 使用动态 API 的库,包括远程库,
  • 资源文件,
  • 套件文件,以及
  • 套件初始化文件。

此外,还可以使用 Libdoc 之前创建的 XML 和 JSON 规范文件作为输入。

注意

在 Robot Framework 6.0 中,新增了为套件文件和套件初始化文件生成文档的支持。

在 Robot Framework 4.0 中,新增了对 JSON 规范文件的支持。

一般使用

概要
1
2
libdoc [options] library_or_resource output_file
libdoc [options] library_or_resource list|show|version [names]
选项
  • -f, --format <html|xml|json|libspec>:指定是否为人类生成 HTML 输出,或者以 XML 或 JSON 格式生成机器可读的规范文件。libspec 格式意味着将文档转换为 HTML 的 XML 规范。默认格式从输出文件扩展名中获取。
  • -s, --specdocformat <raw|html>:指定用于 XML 和 JSON 规范文件的文档格式。raw 表示保留原始文档格式,html 表示将文档转换为 HTML。默认值是 XML 规范文件的 raw 和 JSON 规范以及使用特殊 libspec 格式时的 html。这是 Robot Framework 4.0 中的新功能。
  • -F, --docformat <robot|html|text|rest>:指定源文档格式。可能的值是 Robot Framework 的文档格式、HTML、纯文本和 reStructuredText。默认值可以在测试库源代码中指定,初始默认值是 robot
  • --theme <dark|light|none>:使用深色或浅色 HTML 主题。如果未使用此选项,或者值为 none,则根据浏览器颜色方案选择主题。仅适用于 HTML 输出。这是 Robot Framework 6.0 中的新功能。
  • -N, --name <newname>:设置文档化库或资源的名称。
  • -V, --version <newversion>:设置文档化库或资源的版本。测试库的默认值在源代码中定义。
  • -P, --pythonpath <path>:搜索库和资源的额外位置,类似于运行测试时的情况。
  • --quiet:不将生成的输出文件的路径打印到控制台。这是 Robot Framework 4.0 中的新功能。
  • -h, --help:打印此帮助。

执行 Libdoc

运行 Libdoc 的最简单方法是使用作为正常安装的一部分创建的 libdoc 命令:

1
libdoc ExampleLibrary ExampleLibrary.html

另一种可能性是直接执行 robot.libdoc 模块。如果使用多个 Python 版本安装了 Robot Framework,并希望使用 Libdoc 的特定版本,这种方法特别有用:

1
2
python -m robot.libdoc ExampleLibrary ExampleLibrary.html
python3.9 -m robot.libdoc ExampleLibrary ExampleLibrary.html

还有另一种选择是作为脚本运行 robot.libdoc 模块:

1
python path/to/robot/libdoc.py ExampleLibrary ExampleLibrary.html

注意

单独的 libdoc 命令是 Robot Framework 4.0 中的新功能。

指定库或资源文件

Python 库和动态库的名称或路径

在为使用 Python 或使用动态库 API 实现的库编写文档时,可以通过仅使用库名称或库源代码的路径来指定库:

1
2
libdoc ExampleLibrary ExampleLibrary.html
libdoc src/ExampleLibrary.py docs/ExampleLibrary.html

在前一种情况下,将使用模块搜索路径搜索库,其名称必须与在 Robot Framework 测试数据中导入库时的格式相同。

如果这些库在导入时需要参数,则必须使用两个冒号将参数与库名称或路径连接,如 MyLibrary::arg1::arg2。如果参数更改了库提供的关键字或以其他方式更改了其文档,那么使用 --name 选项也更改库名称可能是个好主意。

资源文件的路径

资源文件必须始终使用路径指定:

1
libdoc example.resource example.html

如果路径不存在,资源文件也会从执行测试用例时的所有目录的模块搜索路径中搜索。

Libdoc 规范文件

也可以使用先前生成的 Libdoc XML 或 JSON 规范文件作为输入。如果规范文件使用 *.xml*.libspec*.json 扩展名,这将起作用:

1
2
3
libdoc Example.xml Example.html
libdoc Example.libspec Example.html
libdoc Example.json Example.html

注意

*.libspec 扩展名的支持是 Robot Framework 3.2 中的新功能。

*.json 扩展名的支持是 Robot Framework 4.0 中的新功能。

生成文档

Libdoc 可以生成 HTML(供人类阅读)以及 XML 或 JSON(供工具使用)格式的文档。写入文档的文件是在库/资源名称或路径之后作为第二个参数指定的,输出格式默认从输出文件扩展名中获取。

Libdoc HTML 文档

大多数 Robot Framework 库使用 Libdoc 生成 HTML 格式的库文档。因此,这种格式对于使用过 Robot Framework 的大多数人来说都很熟悉。下面可以看到一个简单的示例,它是基于本节稍后找到的示例生成的。

HTML 文档从库的一般介绍开始,然后是关于导入库时的配置(如果适用),最后是所有关键字的快捷方式和关键字本身。右下角的放大镜图标打开关键字搜索对话框,也可以通过简单地按 s 键打开。

如果输出文件扩展名为 *.html,Libdoc 会自动创建 HTML 文档。如果需要使用其他扩展名,可以使用 --format 选项显式指定格式。

1
2
3
libdoc OperatingSystem OperatingSystem.html
libdoc --name MyLibrary Remote::http://10.0.0.42:8270 MyLibrary.html
libdoc --format HTML test/resource.robot doc/resource.htm
Libdoc XML 规范文件

Libdoc 也可以生成适合外部工具(如编辑器)使用的 XML 格式的文档。它包含与 HTML 格式相同的所有信息,但以机器可读的格式提供。

XML 规范文件还包含库和关键字源信息,因此库和每个关键字可以有源路径(source 属性)和行号(lineno 属性)。源路径相对于生成规范文件的目录,因此如果规范被移动,它不会引用正确的文件。如果关键字的源路径与库的源路径相同,则省略关键字的源路径,如果由于任何原因从库获取源路径和行号失败,则省略源路径和行号。

如果输出文件扩展名为 *.xml*.libspec,Libdoc 会自动使用 XML 格式。当使用特殊的 *.libspec 扩展名时,Libdoc 会自动启用选项 -f XML -s HTML,这意味着创建一个 XML 输出文件,其中关键字文档被转换为 HTML。如果需要,可以使用 --format 选项显式设置格式。

1
2
3
4
libdoc OperatingSystem OperatingSystem.xml
libdoc test/resource.robot doc/resource.libspec
libdoc --format xml MyLibrary MyLibrary.spec
libdoc --format xml -s html MyLibrary MyLibrary.xml

准确的 Libdoc 规范文件格式在 https://github.com/robotframework/robotframework/tree/master/doc/schema 上有一个 XML 架构(XSD)进行了文档化。规范文件格式可能会在 Robot Framework 的主要版本之间发生变化。

为了让外部工具更容易知道如何解析某个规范文件,规范文件的根元素有一个专用的 specversion 属性。它在 Robot Framework 3.2 中添加,值为 2,早期的规范文件可以认为版本为 1。如果有变化,将来会增加规范版本。Robot Framework 4.0 引入了新的规范版本 3,与早期版本不兼容。

注意

在 Robot Framework 3.2 中引入的 XML:HTML 格式已被 LIBSPEC 格式或选项组合 --format XML --specdocformat HTML 替代。

在 Robot Framework 3.2 中新增了包含源信息和规范版本。

Libdoc JSON 规范文件

自 Robot Framework 4.0 起,Libdoc 也可以生成适合外部工具(如编辑器或网页)使用的 JSON 格式的文档。它包含与 HTML 格式相同的所有信息,但以机器可读的格式提供。

与 XML 规范文件类似,JSON 规范文件包含所有信息,也可以作为 Libdoc 的输入。从该格式可以创建任何其他输出格式。默认情况下,库文档字符串在 JSON 输出文件中被转换为 HTML 格式。

JSON 规范文件的确切格式在 https://github.com/robotframework/robotframework/tree/master/doc/schema 上有一个 JSON 架构进行了文档化。规范文件格式可能会在 Robot Framework 的主要版本之间发生变化。

在控制台查看信息

Libdoc 有三个特殊的命令可以在控制台上显示信息。这些命令用于替代输出文件的名称,它们也可以接受额外的参数。

list

列出库/资源包含的关键字的名称。可以通过传递可选的模式作为参数来限制只显示某些关键字。如果关键字的名称包含给定的模式,则列出关键字。

show

显示库/资源的文档。可以通过传递名称作为参数来限制只显示某些关键字。如果关键字的名称与任何给定的名称匹配,则显示关键字。特殊参数 intro 将只显示库的介绍和导入部分。

version

显示库版本

listshow 提供的可选模式不区分大小写和空格。两者都接受 *? 作为通配符。

示例:

1
2
3
4
5
6
libdoc Dialogs list
libdoc SeleniumLibrary list browser
libdoc Remote::10.0.0.42:8270 show
libdoc Dialogs show PauseExecution execute*
libdoc SeleniumLibrary show intro
libdoc SeleniumLibrary version

编写文档

本节讨论为使用静态库 API 的基于 Python 的测试库以及动态库和资源文件编写文档。在用户指南的其他地方详细描述了创建测试库和资源文件。

Python 库

使用静态库 API 的 Python 库的文档简单地写为库类或模块以及实现关键字的方法的文档字符串。方法文档的第一行被视为关键字的简短文档(例如,用作生成的 HTML 文档中链接的工具提示),因此它应尽可能描述,但不应过长。

下面的简单示例说明了如何一般地编写文档。可以看到基于此示例生成的 HTML 文档是什么样子,本章末尾还有一个稍长的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ExampleLibrary:
"""用于演示目的的库。

这个库只用于一个示例,它没有做任何有用的事情。
"""

def my_keyword(self):
"""什么都不做。"""
pass

def your_keyword(self, arg):
"""接受一个参数并且*不对其做任何事情*。

示例:
| Your Keyword | xxx |
| Your Keyword | yyy |
"""
pass

信息

如果库做了一些初始化工作,那么在使用 Libdoc 时不应该做,可以轻松地检测 Robot Framework 是否正在运行

有关 Python 文档字符串的更多信息,请参阅 PEP-257。

动态库

为了能够为动态库生成有意义的文档,库必须使用 get_keyword_argumentsget_keyword_documentation 方法(或使用它们的驼峰命名变体 getKeywordArgumentsgetKeywordDocumentation)返回关键字参数名称和文档。库还可以通过特殊的 __intro____init__ 值支持一般的库文档。

有关如何创建这些方法的更多信息,请参阅动态库 API 部分。

导入部分

根据其初始化方法,创建了关于如何导入库的单独部分。如果库有一个 __init__ 方法,除了 self 之外还需要参数,那么将显示其文档和参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
class TestLibrary:

def __init__(self, mode='default')
"""创建新的 TestLibrary。`mode` 参数用于确定模式。"""
self.mode = mode

def some_keyword(self, arg):
"""根据给定的 `arg` 做一些事情。

所做的事情取决于在 `importing` 库时指定的 `mode`。
"""
if self.mode == 'secret':
# ...
资源文件文档

资源文件中的关键字可以使用 [Documentation] 设置进行文档化,Libdoc 也使用此文档。文档的第一行(直到第一个隐式换行符或显式 \n)被视为与测试库类似的简短文档。

资源文件本身也可以在设置部分有文档,用于记录整个资源文件。

资源文件中的可能变量不能被文档化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** Settings ***
Documentation 用于演示目的的资源文件。
... 这个资源只用于一个示例,它没有做任何有用的事情。

*** Keywords ***
My Keyword
[Documentation] 什么都不做
No Operation

Your Keyword
[Arguments] ${arg}
[Documentation] 接受一个参数并且*不对其做任何事情*。
...
... 示例:
... | Your Keyword | xxx |
... | Your Keyword | yyy |
No Operation

文档语法

Libdoc 支持 Robot Framework 自己的文档语法、HTML、纯文本和 reStructuredText。可以在库源代码中使用 ROBOT_LIBRARY_DOC_FORMAT 属性或使用 --docformat (-F) 选项从命令行给出要使用的格式。在这两种情况下,可能的不区分大小写的值是 ROBOT(默认)、HTMLTEXTreST

Robot Framework 自己的文档格式是默认的,通常推荐的格式。其他格式在使用现有代码和现有文档的测试库中特别有用。

Robot Framework 文档语法

Robot Framework 的文档语法中最重要的特性是使用 *bold*_italic_ 进行格式化,自定义链接和自动将 URL 转换为链接,以及使用管道字符简单地创建表格和预格式化文本块(对于示例很有用)。如果文档变得更长,支持章节标题也可能很方便。

下面的示例说明了一些最重要的格式化特性。注意,由于这是默认格式,因此无需使用 ROBOT_LIBRARY_DOC_FORMAT 属性,也无需从命令行给出格式。

1
2
3
4
5
6
7
8
9
10
"""Robot Framework 格式的示例库。

- 使用 *bold* 和 _italic_ 进行格式化。
- URLs 如 http://example.com 会被转换为链接。
- 支持自定义链接,如 [http://robotframework.org|Robot Framework]。
- 可以链接到 `My Keyword`。
"""

def my_keyword():
"""这里没有更多内容。"""
自动创建目录

对于较大的库,通常有必要在库介绍中添加目录。当使用 Robot Framework 文档格式时,可以通过在自己的一行中添加一个特殊的 %TOC% 标记来自动完成此操作。目录是根据介绍中使用的顶级章节标题(例如 = Section =)创建的。除此之外,TOC 还获取到自动创建的快捷方式和关键字以及适用时的导入和标签部分的链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""演示 TOC 生成的示例库。

%TOC% 标记只创建实际的目录,可能的
标题或其他解释需要像下面这样单独添加。

== 目录 ==

%TOC%

= 章节标题 =

顶级章节标题会自动添加到 TOC。

= 第二章节 =

== 子章节 ==

子章节标题不会添加到 TOC。
"""

def my_keyword():
"""这里没有更多内容。"""

注意

在 Robot Framework 3.2 中,自动 TOC 生成是新功能。

HTML 文档语法

当使用 HTML 格式时,可以使用任何语法自由地创建文档。主要的缺点是 HTML 标记不是那么人性化,这可能使源代码中的文档难以维护和阅读。HTML 格式的文档由 Libdoc 直接使用,无需任何转换或转义。然而,支持使用像 My Keyword 这样的语法链接到关键字的特殊语法。

下面的示例包含与前一个示例相同的格式化示例。现在必须使用 ROBOT_LIBRARY_DOC_FORMAT 属性或在命令行上像 --docformat HTML 这样给出格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
"""HTML 格式的示例库。

<ul>
<li>使用 <b>bold</b> 和 <i>italic</i> 进行格式化。
<li>URL 不会自动转换为链接。
<li>支持自定义链接,如 <a href="http://www.w3.org/html">HTML</a>。
<li>可以链接到 `My Keyword`。
</ul>
"""
ROBOT_LIBRARY_DOC_FORMAT = 'HTML'

def my_keyword():
"""这里没有更多内容。"""

内部链接

Libdoc 支持在文档中对关键字和不同章节进行内部链接。链接是通过将目标名称用反引号字符(如 target)包围来完成的。目标名称不区分大小写,可能的目标在后续章节中有解释。

如果找不到链接目标,不会有错误或警告,而是 Libdoc 只将其中的文本格式化为斜体。早期,当引用关键字参数时,推荐使用这种格式,但这可能会意外地创建内部链接。现在,推荐使用双反引号(如 argument)代替内联代码样式。未来可能会删除旧的单反引号格式,以便在找不到链接目标时给出错误。

除了以下章节中的示例外,还在本章末尾的较长示例中显示了内部链接和参数格式化。

链接到关键字

库中的所有关键字都会自动创建链接目标,可以使用 Keyword Name 语法进行链接。下面的示例说明了两个关键字都有链接到对方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def keyword(log_level="INFO"):
"""做一些事情并使用给定的级别记录输出。

`log level` 的有效值是 "INFO"(默认)"DEBUG" 和 "TRACE"。

另请参见 `Another Keyword`。
"""
# ...

def another_keyword(argument, log_level="INFO"):
"""对给定的参数做一些事情并记录输出。

有关有效日志级别的信息,请参见 `Keyword`。
"""
# ...

注意

当使用 reStructuredText 文档语法时,必须像 Keyword Name 这样转义反引号。

链接到自动章节

Libdoc 生成的文档总是包含库的整体介绍和关键字的章节。如果库本身需要参数,还有一个单独的导入章节。如果任何关键字有标签,概览中也会显示它们的单独选择器。

所有章节都充当可以链接的目标,可能的目标名称列在下表中。在下一节的示例中显示了如何使用这些目标。

章节 目标
介绍 introductionlibrary introduction
导入 importinglibrary importing
关键字 keywords

注意

在 Robot Framework 4.0 之前,还有标签和快捷方式的章节。在 Robot Framework 4.0 中,这些已被概览菜单取代。这意味着之前链接到快捷方式或标签章节的链接不再工作。

链接到自定义章节

Robot Framework 的文档语法支持自定义章节标题,库或资源文件介绍中使用的标题会自动创建链接目标。下面的示例说明了如何链接到自动和自定义章节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""用于 Libdoc 演示目的的库。

这个库没有做任何有用的事情。

= 我的章节 =

我们确实在文档中有一个自定义章节。
"""

def keyword():
"""什么都不做。

有关更多信息,请参见 `introduction`,并参见 `My section` 以测试如何
链接到自定义章节。
"""
pass

注意

只有在使用 Robot Framework 文档语法时,才能链接到自定义章节。

表示参数

Libdoc 会自动显示关键字参数的信息。

包含的信息 以下信息将显示所有关键字,无论它们是在库中实现还是在资源文件中实现:

  • 参数名称。用户关键字参数显示时不带 ${} 装饰,使得无论关键字来自何处,参数看起来都是一样的。
  • 标记告诉参数是仅位置、仅命名、自由位置、自由命名,还是可以通过位置或名称给出的普通参数。
  • 可能的默认值。显示为 = 42
  • 可能的类型。显示为 <int>。可以是链接到下一节解释的类型文档。

在关键字文档中引用参数时,建议使用内联代码样式,如 argument

自动列出类型文档

如上所述,Libdoc 在列出参数时会自动显示可能的类型信息。如果类型是基于 Enum 或 TypedDict 的自定义类型,类型会被自动转换,或者类型有自定义转换器,那么类型本身也会单独列出以显示更多关于它的信息。当这些类型用于参数时,类型名称也会变成链接到类型信息的链接。

所有列出的数据类型都显示可能的类型文档以及支持的参数类型。此外,基于 Enum 的类型列出可用的成员,基于 TypedDict 的类型显示字典结构。

注意

在 Robot Framework 4.0 中,基于 Enum 和 TypedDict 的类型的自动列出是新功能。在 Robot Framework 5.0 中,列出其他类型是新功能。

Libdoc 示例

以下示例说明了如何使用最重要的文档格式化可能性、内部链接等。点击这里查看生成的文档是什么样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class LoggingLibrary:
"""用于记录消息的库。

= 目录 =

%TOC%

= 使用 =

这个库有几个关键字,例如 `Log Message`,用于记录消息。实际上,这个库只用于 _Libdoc_ 演示目的。

= 有效的日志级别 =

有效的日志级别是 ``INFO``,``DEBUG`` 和 ``TRACE``。默认的日志级别可以在 `importing` 时设置。

= 示例 =

注意示例中的关键字是如何链接的。

| `Log Message` | My message | | |
| `Log Two Messages` | My message | Second message | level=DEBUG |
| `Log Messages` | First message | Second message | Third message |
"""
ROBOT_LIBRARY_VERSION = '0.1'

def __init__(self, default_level='INFO'):
"""可以在库导入时给出默认的日志级别。

有关可用日志级别的信息,请参见 `Valid log levels` 章节。

示例:

| =Setting= | =Value= | =Value= | =Comment= |
| Library | LoggingLibrary | | # 使用默认级别 (INFO) |
| Library | LoggingLibrary | DEBUG | # 使用给定的级别 |
"""
self.default_level = self._verify_level(default_level)

def _verify_level(self, level):
level = level.upper()
if level not in ['INFO', 'DEBUG', 'TRACE']:
raise RuntimeError("无效的日志级别'%s'。有效的级别是 "
"'INFO', 'DEBUG', 和 'TRACE'")
return level

def log_message(self, message, level=None):
"""使用指定的日志级别将给定的消息写入日志文件。

要记录的消息和要使用的日志级别分别由
``message`` 和 ``level`` 参数定义。

如果没有给出日志级别,则使用在 `library
importing` 时给出的默认级别。
"""
level = self._verify_level(level) if level else self.default_level
print("*%s* %s" % (level, message))

def log_two_messages(self, message1, message2, level=None):
"""使用指定的日志级别将给定的消息写入日志文件。

有关更多信息,请参见 `Log Message` 关键字。
"""
self.log_message(message1, level)
self.log_message(message2, level)

def log_messages(self, *messages):
"""使用在 `importing` 时设置的日志级别记录给定的消息。

另请参见 `Log Message` 和 `Log Two Messages`。
"""
for msg in messages:
self.log_message(msg)

所有标准库都有由 Libdoc 生成的文档,它们的文档(和源代码)作为更现实的示例。

测试数据文档工具(Testdoc)

Testdoc 是 Robot Framework 的内置工具,用于基于测试用例生成高级文档。创建的文档是 HTML 格式,包括每个测试套件和测试用例的名称、文档和其他元数据,以及顶级关键字及其参数。

一般使用

概要
1
python -m robot.testdoc [options] data_sources output_file
选项
  • -T, --title <title> 设置生成的文档的标题。标题中的下划线会被转换为空格。默认标题是顶级套件的名称。
  • -N, --name <name> 覆盖顶级测试套件的名称。
  • -D, --doc <doc> 覆盖顶级测试套件的文档。
  • -M, --metadata <name:value> 设置/覆盖顶级测试套件的自由元数据。
  • -G, --settag <tag> 将给定的标签设置到所有测试用例。
  • -t, --test <name> 通过名称包含测试。
  • -s, --suite <name> 通过名称包含套件。
  • -i, --include <tag> 通过标签包含测试。
  • -e, --exclude <tag> 通过标签排除测试。
  • -A, --argumentfile <path> 文本文件,从中读取更多参数。与运行测试时的参数文件工作方式完全相同。
  • -h, --help 在控制台打印此帮助。

除了 --title 之外,所有选项的语义与执行测试用例时的相同选项完全相同。

生成文档

数据可以作为单个文件、目录或多个文件和目录给出。在所有这些情况下,最后一个参数必须是要写入输出的文件。

Testdoc 可以作为已安装的模块执行,如 python -m robot.testdoc,或作为脚本执行,如 python path/robot/testdoc.py

示例:

1
2
python -m robot.testdoc my_test.robot testdoc.html
python path/to/robot/testdoc.py --name "Smoke tests" --include smoke path/to/tests smoke.html

测试数据清理工具(Tidy)

内置的 Tidy 工具在 Robot Framework 4.1 中被弃用,取而代之的是新的和增强的外部 Robotidy 工具。它在 Robot Framework 5.0 中被完全移除。

外部工具

有许多外部工具可以与 Robot Framework 一起使用。这些工具包括各种 IDE 和文本编辑器的插件,用于并行执行和数据驱动测试的工具,用于持续集成系统的插件等。

这些工具作为与 Robot Framework 本身独立的单独项目进行开发。有关可用工具的列表,请参见 http://robotframework.org/。

附录

可用设置

此附录列出了可以在不同部分使用的所有设置。

注意

设置可以本地化。请参阅支持的翻译附录以获取更多信息。

设置部分

设置部分用于导入库、资源文件和变量文件,并为测试套件和测试用例定义元数据。它可以包含在测试用例文件和资源文件中。请注意,在资源文件中,设置部分只能包含导入库、资源和变量的设置。

名称 描述
Library 用于导入库
Resource 用于使用资源文件
Variables 用于使用变量文件
Name 用于设置自定义套件名称
Documentation 用于指定套件或资源文件文档
Metadata 用于设置自由套件元数据
Suite Setup 用于指定套件设置
Suite Teardown 用于指定套件拆卸
Test Tags 用于为套件中的所有测试指定测试用例标签
Force Tags, Default Tags 已弃用的设置,用于指定测试用例标签
Keyword Tags 用于为某个文件中的所有用户关键字指定用户关键字标签
Test Setup 用于指定默认测试设置
Test Teardown 用于指定默认测试拆卸
Test Template 用于指定测试用例的默认模板关键字
Test Timeout 用于指定默认测试用例超时
Task Setup, Task Teardown, Task Template, Task Timeout 分别是 Test Setup, Test Teardown, Test Template 和 Test Timeout 的别名,可以在创建任务时使用

测试用例部分

测试用例部分的设置始终特定于为其定义的测试用例。这些设置中的一些会覆盖在设置部分定义的默认值。

在任务部分创建任务时,可以使用完全相同的设置。

名称 描述
[Documentation] 用于指定测试用例文档
[Tags] 用于标记测试用例
[Setup] 用于指定测试设置
[Teardown] 用于指定测试拆解
[Template] 用于指定模板关键字
[Timeout] 用于指定测试用例超时

关键字部分

关键字部分中的设置特定于为其定义的用户关键字。

原文 描述
[Documentation] 用于指定用户关键字文档
[Tags] 用于指定用户关键字标签
[Arguments] 用于指定用户关键字参数
[Setup] 用于指定用户关键字设置,Robot Framework 7.0中的新功能
[Teardown] 用于指定用户关键字拆解
[Timeout] 用于指定用户关键字超时
[Return] 用于指定用户关键字返回值,已在Robot Framework 7.0中弃用,建议使用RETURN语句代替

命令行选项

此附录列出了执行测试用例和后处理输出时可用的所有命令行选项。还列出了影响执行和后处理的环境变量。

测试执行的命令行选项
  • --rpa:开启通用自动化模式。
  • --language <lang>:激活本地化。lang 可以是内置语言的名称或代码,或者是自定义语言文件的路径或模块名称。
  • -F, --extension <value>:执行目录时,只解析这些文件。
  • -I, --parseinclude <pattern>:执行目录时,只解析匹配的文件。
  • -N, --name <name>:设置顶级测试套件的名称。
  • -D, --doc <document>:设置顶级测试套件的文档。
  • -M, --metadata <name:value>:为顶级测试套件设置自由元数据。
  • -G, --settag <tag>:为所有执行的测试用例设置标签。
  • -t, --test <name>:通过名称选择测试用例。
  • --task <name>:执行任务时可以使用的 --test 的别名。
  • -s, --suite <name>:通过名称选择测试套件。
  • -R, --rerunfailed <file>:选择从早期输出文件中重新执行的失败测试。
  • -S, --rerunfailedsuites <file>:选择从早期输出文件中重新执行的失败测试套件。
  • -i, --include <tag>:通过标签选择测试用例。
  • -e, --exclude <tag>:通过标签选择测试用例。
  • --skip <tag>:具有给定标签的测试将被跳过。标签可以是模式。
  • --skiponfailure <tag>:如果失败,具有给定标签的测试将被跳过。
  • -v, --variable <name:value>:设置单个变量。
  • -V, --variablefile <path:args>:使用变量文件设置变量。
  • -d, --outputdir <dir>:定义创建输出文件的位置。
  • -o, --output <file>:设置生成的输出文件的路径。
  • --legacyoutput:以 Robot Framework 6.x 兼容格式创建输出文件。
  • -l, --log <file>:设置生成的日志文件的路径。
  • -r, --report <file>:设置生成的报告文件的路径。
  • -x, --xunit <file>:设置生成的 xUnit 兼容结果文件的路径。
  • -b, --debugfile <file>:在执行期间编写的调试文件。
  • -T, --timestampoutputs:在上述输出文件中添加时间戳。
  • --splitlog:将日志文件分割成较小的部分,可以在浏览器中透明地打开。
  • --logtitle <title>:为生成的测试日志设置标题。
  • --reporttitle <title>:为生成的测试报告设置标题。
  • --reportbackground <colors>:设置生成的报告的背景颜色。
  • --maxerrorlines <lines>:设置测试失败时报告中显示的错误行数。
  • --maxassignlength <characters>:设置在日志中显示的变量分配的字符数。
  • -L, --loglevel <level>:设置日志的阈值级别。可以选择性地给出默认可见日志级别,用冒号(:)分隔。
  • --suitestatlevel <level>:定义在输出中的套件统计表中显示的级别数。
  • --tagstatinclude <tag>:只在标签统计表中包含这些标签。
  • --tagstatexclude <tag>:从标签统计表中排除这些标签。
  • --tagstatcombine <tags:title>:基于标签创建组合统计。
  • --tagdoc <pattern:doc>:为指定的标签添加文档。
  • --tagstatlink <pattern:link:title>:在标签统计表中添加外部链接。
  • --expandkeywords <name:pattern|tag:pattern>:在生成的日志文件中自动展开关键字。
  • --removekeywords <all|passed|name:pattern|tag:pattern|for|while|wuks>:从生成的日志文件中删除关键字数据。
  • --flattenkeywords <for|while|iteration|name:pattern|tag:pattern>:在生成的日志文件中展平关键字。
  • --listener <name:args>:设置一个监听器以监视测试执行。
  • --nostatusrc:无论测试用例是否失败,都将返回代码设置为零。错误代码正常返回。
  • --runemptysuite:即使选择的测试套件为空,也执行测试。
  • --dryrun:在干运行模式下,测试将在不执行来自测试库的关键字的情况下运行。对于验证测试数据语法很有用。
  • -X, --exitonfailure:如果有任何测试失败,停止测试执行。
  • --exitonerror:如果在解析测试数据、导入库等过程中发生任何错误,停止测试执行。
  • --skipteardownonexit:如果测试执行提前停止,跳过拆卸。
  • --prerunmodifier <name:args>:激活测试数据的程序化修改。
  • --prerebotmodifier <name:args>:激活结果的程序化修改。
  • --randomize <all|suites|tests|none>:随机化测试执行顺序。
  • --console <verbose|dotted|quiet|none>:控制台输出类型。
  • --dotted--console dotted的快捷方式。
  • --quiet--console quiet的快捷方式。
  • -W, --consolewidth <width>:设置控制台输出的宽度。
  • -C, --consolecolors <auto|on|ansi|off>:指定控制台上使用的颜色。
  • -K, --consolemarkers <auto|on|off>:当测试用例中的顶级关键字结束时,在控制台上显示标记。
  • -P, --pythonpath <path>:添加到模块搜索路径的额外位置。
  • -A, --argumentfile <path>:从中读取更多参数的文本文件。
  • -h, --help:打印使用说明。
  • --version:打印版本信息。
后处理输出的命令行选项
  • --rpa:开启通用自动化模式。
  • -R, --merge:更改结果组合行为为合并。
  • -N, --name <name>:设置顶级测试套件的名称。
  • -D, --doc <document>:设置顶级测试套件的文档。
  • -M, --metadata <name:value>:为顶级测试套件设置自由元数据。
  • -G, --settag <tag>:为所有处理的测试用例设置标签。
  • -t, --test <name>:通过名称选择测试用例。
  • --task <name>:执行任务时可以使用的 --test 的别名。
  • -s, --suite <name>:通过名称选择测试套件。
  • -i, --include <tag>:通过标签选择测试用例。
  • -e, --exclude <tag>:通过标签选择测试用例。
  • -d, --outputdir <dir>:定义创建输出文件的位置。
  • -o, --output <file>:设置生成的输出文件的路径。
  • --legacyoutput:以 Robot Framework 6.x 兼容格式创建输出文件。
  • -l, --log <file>:设置生成的日志文件的路径。
  • -r, --report <file>:设置生成的报告文件的路径。
  • -x, --xunit <file>:设置生成的 xUnit 兼容结果文件的路径。
  • -T, --timestampoutputs:在上述输出文件中添加时间戳。
  • --splitlog:将日志文件分割成较小的部分,可以在浏览器中透明地打开。
  • --logtitle <title>:为生成的测试日志设置标题。
  • --reporttitle <title>:为生成的测试报告设置标题。
  • --reportbackground <colors>:设置生成的报告的背景颜色。
  • -L, --loglevel <level>:设置选择日志消息的阈值级别。可以选择性地给出默认可见日志级别,用冒号(:)分隔。
  • --suitestatlevel <level>:定义在输出中的套件统计表中显示的级别数。
  • --tagstatinclude <tag>:只在标签统计表中包含这些标签。
  • --tagstatexclude <tag>:从标签统计表中排除这些标签。
  • --tagstatcombine <tags:title>:基于标签创建组合统计。
  • --tagdoc <pattern:doc>:为指定的标签添加文档。
  • --tagstatlink <pattern:link:title>:在标签统计表中添加外部链接。
  • --expandkeywords <name:pattern|tag:pattern>:在生成的日志文件中自动展开关键字。
  • --removekeywords <all|passed|name:pattern|tag:pattern|for|wuks>:从生成的输出中删除关键字数据。
  • --flattenkeywords <for|foritem|name:pattern|tag:pattern>:在生成的输出中展平关键字。
  • --starttime <timestamp>:创建报告时设置测试执行的开始时间。
  • --endtime <timestamp>:创建报告时设置测试执行的结束时间。
  • --nostatusrc:无论测试用例是否失败,都将返回代码设置为零。错误代码正常返回。
  • --processemptysuite:即使文件包含空测试套件,也处理输出文件。
  • --prerebotmodifier <name:args>:激活结果的程序化修改。
  • -C, --consolecolors <auto|on|ansi|off>:指定控制台上使用的颜色。
  • -P, --pythonpath <path>:添加到模块搜索路径的额外位置。
  • -A, --argumentfile <path>:从中读取更多参数的文本文件。
  • -h, --help:打印使用说明。
  • --version:打印版本信息。

执行和后处理的环境变量

  • ROBOT_OPTIONSREBOT_OPTIONS:默认选项的空格分隔列表,这些选项将放在命令行的任何显式选项前面。
  • ROBOT_SYSLOG_FILE:Robot Framework 将解析测试用例文件和运行测试的内部信息写入的系统日志文件的路径。
  • ROBOT_SYSLOG_LEVEL:写入系统日志文件时使用的日志级别。
  • ROBOT_INTERNAL_TRACES:当设置为任何非空值时,Robot Framework 的内部方法将包含在错误跟踪中。

翻译

Robot Framework 支持翻译部分标题、设置、在行为驱动开发(BDD)中使用的 Given/When/Then 前缀,以及在自动布尔参数转换中使用的 true 和 false 字符串。本附录列出了 Robot Framework 开箱即用支持的所有语言(不包括英语)的所有翻译。

如何实际激活翻译在本地化部分有解释。该部分还解释了如何创建自定义语言定义以及如何贡献新的翻译。

简体中文 (zh-CN)

部分标题
标题 翻译
Settings 设置
Variables 变量
Test Cases 用例
Tasks 任务
Keywords 关键字
Comments 备注
设置
设置 翻译
Library 程序库
Resource 资源文件
Variables 变量文件
Name
Documentation 说明
Metadata 元数据
Suite Setup 用例集启程
Suite Teardown 用例集终程
Test Setup 用例启程
Task Setup 任务启程
Test Teardown 用例终程
Task Teardown 任务终程
Test Template 用例模板
Task Template 任务模板
Test Timeout 用例超时
Task Timeout 任务超时
Test Tags 用例标签
Task Tags 任务标签
Keyword Tags 关键字标签
Tags 标签
Setup 启程
Teardown 终程
Template 模板
Timeout 超时
Arguments 参数
BDD 前缀
前缀 翻译
Given 假定
When
Then 那么
And 并且
But 但是
布尔字符串
True/False Values
True 真, 是, 开
False 假, 否, 关, 空

文档格式化

在测试数据中,可以使用简单的 HTML 格式化对测试套件、测试用例和用户关键字文档以及自由套件元数据进行文档化,以及在文档化测试库时。这种格式化类似于大多数 wiki 中使用的样式,旨在使其既可以作为纯文本理解,也可以在 HTML 转换后理解。

处理测试数据中的空白
换行符

在文档化测试套件、测试用例和用户关键字或向测试套件添加元数据时,可以使用 \n 转义序列手动添加换行符。

1
2
3
*** Settings ***
Documentation First line.\n\nSecond paragraph. This time\nwith multiple lines.
Metadata Example list - first item\n- second item\n- third

注意

如下面的段落部分所解释的,Second paragraph, this time\nwith multiple lines. 中的单个换行符实际上并不影响该段落的渲染。然而,创建列表或其他此类结构时需要换行符。

手动向长文档添加换行符需要一些努力,额外的字符也使文档更难阅读。不过,可以避免这种情况,因为在连续的文档和元数据行之间会自动插入换行符。实际上,这意味着上面的示例也可以按以下方式编写。

1
2
3
4
5
6
7
8
9
10
11
*** Settings ***
Documentation
... First line.
...
... Second paragraph. This time
... with multiple lines.
Metadata
... Example list
... - first item
... - second item
... - third

如果一行已经以字面换行符结束,或者以转义反斜杠结束,则不会添加自动换行符:

1
2
3
4
5
6
7
8
*** Test Cases ***
Ends with newline
[Documentation] Ends with a newline and\n
... automatic newline is not added.

Ends with backslash
[Documentation] Ends with a backslash and \
... no newline is added.
空格

与 Robot Framework 数据的其他地方不同,文档和元数据中保留了前导空格和连续的内部空格。例如,这使得可以将列表项分割到多行,并具有带空格的预格式化文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Test Cases ***
Long list item
[Documentation]
... List:
... - Short item.
... - Second item is pretty long and it is split to
... multiple rows. Leading spaces are preserved.
... - Another short item.

Preformatted text
[Documentation]
... Example with consecutive internal spaces:
...
... | *** Test Cases ***
... | Example
... | Keyword

注意

在 Robot Framework 6.1 中,文档和元数据中保留空格是新的。在早期版本中,需要使用反斜杠转义空格。

段落

在格式化的 HTML 文档中,所有常规文本都表示为段落。实际上,无论换行符是手动添加的还是自动添加的,单个换行符分隔的行将被组合成一个段落。可以使用空行(即两个换行符)分隔多个段落,而且在后续部分讨论的表格、列表和其他特殊格式化块也会结束一个段落。

例如,以下测试套件或资源文件文档:

1
2
3
4
5
6
*** Settings ***
Documentation
... First paragraph has only one line.
...
... Second paragraph, this time created
... with multiple lines.

将被格式化为 HTML:

第一段只有一行。

第二段,这次创建了多行。

内联样式

文档语法支持粗体、斜体和代码的内联样式。可以通过在选定的单词或单词前后添加一个星号来创建粗体文本,例如 这是粗体。斜体样式类似,但要使用的特殊字符是下划线,例如,斜体。也可以使用 粗斜体 的语法来创建粗斜体。

代码样式是使用双反引号创建的,如 code。结果是带有浅灰色背景的等宽文本。

单独的星号、下划线或双反引号,或者在单词中间,不会开始格式化,但在它们前后的标点字符是允许的。当多行形成一个段落时,所有内联样式都可以跨多行。

内联样式示例:

未格式化 格式化
bold bold
italic italic
bold italic bold italic
code code
bold, then italic and finally some code bold, then italic and finally some code
This is bold\non multiple\nlines. This is bold on multiple lines.

URLs

所有看起来像 URL 的字符串都会自动转换为可点击的链接。此外,以 .jpg、.jpeg、.png、.gif、.bmp 或 .svg(不区分大小写)结尾的 URL 将自动创建图像。例如,像 http://example.com 这样的 URL 会变成链接,而 http:///host/image.jpg 和 file:///path/chart.png 会变成图像。

URL 自动转换为链接的功能适用于日志和报告中的所有数据,但只有在测试套件、测试用例和关键字文档以及测试套件元数据中才会创建图像。

注意

.svg 图像支持是 Robot Framework 3.2 中的新功能。

自定义链接和图像

可以使用特殊语法 [link|content] 创建自定义链接和嵌入图像。这将创建一个链接或图像,取决于链接和内容图像。如果它们具有与 URL 特殊的相同的图像扩展名,或者以 data:image/ 开头,那么它们就被认为是图像。在所有情况下,部分之间的周围方括号和管道字符都是必需的。

注意

对 data:image/ 前缀的支持是 Robot Framework 3.2 中的新功能。

带有文本内容的链接

如果链接和内容都不是图像,最后的结果是一个普通的链接,其中链接是链接目标,内容是可见的文本:

1
2
[file.html|this file] -> <a href="file.html">this file</a>
[http://host|that host] -> <a href="http://host">that host</a>
带有图像内容的链接

如果内容是图像,会得到一个链接,其中链接内容是图像。链接目标由链接创建,它可以是文本或图像:

1
2
3
[robot.html|robot.png] -> <a href="robot.html"><img src="robot.png"></a>
[robot.html|data:image/png;base64,oooxxx=] -> <a href="robot.html"><img src="data:image/png;base64,oooxxx="></a>
[image.jpg|thumb.jpg] -> <a href="image.jpg"><img src="thumb.jpg"></a>
带有标题文本的图像

如果链接是图像但内容不是,语法创建一个图像,其中内容是鼠标悬停在图像上时显示的标题文本:

1
2
[robot.jpeg|Robot rocks!] -> <img src="robot.jpeg" title="Robot rocks!">
[data:image/png;base64,oooxxx=|Robot rocks!] -> <img src="data:image/png;base64,oooxxx=" title="Robot rocks!">

节标题

如果文档变得较长,通常最好将其分割成几个部分。可以使用 = 我的标题 = 的语法来用标题分隔各个部分,其中等号的数量表示标题的级别:

1
2
3
4
5
6
7
8
9
10
11
12
13
= First section =

== Subsection ==

Some text.

== Second subsection ==

More text.

= Second section =

You probably got the idea.

注意,只支持三个标题级别,并且等号与标题文本之间的空格是必需的。

表格

表格是使用管道字符(带有周围空格)作为列分隔符和换行符作为行分隔符创建的。可以通过用等号(和可选的空格)包围单元格内容来创建标题单元格,如 = 标题 ==标题=。表格单元格还可以包含链接和格式化内容,如粗体和斜体:

1
2
3
| =A= |  =B=  | = C =  |
| _1_ | Hello | world! |
| _2_ | Hi |

创建的表格总是有一个细边框,普通文本是左对齐的。标题单元格中的文本是粗体和居中的。空单元格会自动添加,使行长度相等。例如,上面的示例在 HTML 中的格式化如下:

=A= =B= = C =
_1_ Hello world!
_2_ Hi

列表

列表是通过以连字符和空格(’- ‘)开始一行来创建的。可以通过用一个或多个空格缩进连续的行来将列表项分割成多行。不以 ‘- ’ 开头且未缩进的行结束了列表:

1
2
3
4
5
6
Example:
- a list item
- second list item
is continued

This is outside the list.

上述文档在 HTML 中的格式化如下:

Example:

a list item second list item is continued This is outside the list.

预格式化文本

在文档中,可以嵌入预格式化文本块。预格式化块是通过以 ‘| ’ 开始行来创建的,除非在否则为空的行上,否则在管道字符后面的一个空格是必需的。开始的 ‘| ’ 序列将从结果的 HTML 中删除,但所有其他空白都将保留。

在以下文档中,当转换为 HTML 时,两个中间行形成一个预格式化块:

1
2
3
4
Doc before block:
| inside block
| some additional whitespace
After block.

上述文档格式化如下:

1
2
3
4
5
Doc before block:

inside block
some additional whitespace
After block.

水平尺

水平尺(<hr> 标签)使得可以将较大的部分彼此分开,可以通过在一行中单独放置三个或更多的连字符来创建它们:

1
2
3
4
5
Some text here.

---

More text...

上述文档格式化如下:

1
2
3
4
5
Some text here.

---

More text...

时间格式

Robot Framework 有自己的时间格式,既灵活易用,又易于理解。它被几个关键字(例如,BuiltIn 关键字 Sleep 和 Wait Until Keyword Succeeds)、DateTime 库和超时使用。

作为数字的时间

时间总是可以作为一个纯数字给出,在这种情况下,它被解释为秒。整数和浮点数都可以工作,可以使用实数或包含数值的字符串。

注意

注意

在某些上下文中,纯数字可以被解释为其他时间。例如,对于 WHILE 循环限制,整数表示最大迭代次数。

作为时间字符串的时间

将时间表示为时间字符串意味着使用诸如 2 分钟 42 秒这样的格式,这通常比仅将值作为秒更容易理解。例如,不太容易理解 4200 秒是多长时间,但 1 小时 10 分钟立即就清楚了。

这种格式的基本思想是首先有一个数字,然后有一个文本指定该数字代表的时间。数字可以是整数或浮点数,整个格式对大小写和空格不敏感,可以添加 - 前缀来指定负时间。可用的时间说明符有:

  • days, day, d
  • hours, hour, h
  • minutes, minute, mins, min, m
  • seconds, second, secs, sec, s
  • milliseconds, millisecond, millis, ms
  • microseconds, microsecond, us, μs
  • nanoseconds, nanosecond, ns

示例:

1
2
3
4
5
6
1 min 30 secs 
1.5 minutes
90 s
1 day 2 hours 3 minutes 4 seconds 5 milliseconds 6 microseconds 7 nanoseconds
1d 2h 3m 4s 5ms 6μs 7 ns
- 10 seconds

注意

在 Robot Framework 6.0 中,对微秒和纳秒的支持是新的。

作为 “计时器” 字符串的时间

时间也可以以计时器格式 hh:mm:ss.mil 给出。在这种格式中,小时和毫秒部分都是可选的,当它们不重要时,可以省略前导和尾随的零,负时间可以通过添加 - 前缀来表示。例如,以下计时器和时间字符串值是相同的:

计时器和时间字符串示例

计时器 时间字符串
00:00:01 1 秒
01:02:03 1 小时 2 分钟 3 秒
1:00:00 1 小时
100:00:00 100 小时
00:02 2 秒
42:00 42 分钟
00:01:02.003 1 分钟 2 秒 3 毫秒
00:01.5 1.5 秒
-01:02.345 - 1 分钟 2 秒 345 毫秒

布尔参数

Robot Framework 标准库中的许多关键字接受作为布尔值 true 或 false 处理的参数。如果这样的参数作为字符串给出,那么如果它是一个空字符串或等于 FALSE、NONE、NO、OFF 或 0(不区分大小写),则被认为是 false。除非关键字文档明确说明否则,其他字符串被认为是 true,其他参数类型使用与 Python 相同的规则进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Keywords ***
True examples
Should Be Equal ${x} ${y} Custom error values=True # 字符串通常为 true。
Should Be Equal ${x} ${y} Custom error values=yes # 与上面相同。
Should Be Equal ${x} ${y} Custom error values=${TRUE} # Python 的 `True` 是 true。
Should Be Equal ${x} ${y} Custom error values=${42} # 除 0 之外的数字为 true。

False examples
Should Be Equal ${x} ${y} Custom error values=False # 字符串 `false` 是 false。
Should Be Equal ${x} ${y} Custom error values=no # 字符串 `no` 也是 false。
Should Be Equal ${x} ${y} Custom error values=${EMPTY} # 空字符串是 false。
Should Be Equal ${x} ${y} Custom error values=${FALSE} # Python 的 `False` 是 false。
Should Be Equal ${x} ${y} Custom error values=no values # 这个关键字的特殊 false 字符串。

注意

在 Robot Framework 3.1 中,将 OFF 和 0 视为 false 是新的。

表达式求值

本附录解释了在不同上下文中如何使用 Python 求值表达式,以及如何处理表达式中的变量。

简介

诸如 IF/ELSE 结构、WHILE 循环和内联 Python 求值以及几个 BuiltIn 关键字都接受一个在 Python 中求值的表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*** Test Cases ***
IF/ELSE
IF ${x} > 0
Log to console ${x} is positive
ELSE
Log to console ${x} is negative
END

Inline Python evaluation
Log to console ${x} is ${{'positive' if ${x} > 0 else 'negative'}}

Evaluate keyword
${type} = Evaluate 'positive' if ${x} > 0 else 'negative'
Log to console ${x} is ${type}

Should Be True keyword
Should Be True ${x} > 0

注意,与其创建复杂的表达式,通常最好将逻辑移动到测试库中。这通常可以简化维护,并提高执行速度。

求值命名空间

表达式使用 Python 的 eval 函数进行求值,因此可以使用正常的 Python 构造,如 ‘{x}’ == ‘expected’、x′==′expected′、{x} > 0 和 ‘${x}’.upper() not in (‘FAIL’, ‘BAD’),并且所有内置函数,如 len() 和 int() 都可用。此外,所有未识别的 Python 变量都被认为是自动导入的模块。可以使用所有可用的 Python 模块,包括标准模块和已安装的第三方模块。

以下示例演示了使用 Python 内置函数以及使用内联 Python 求值语法的模块,但相同的表达式也可以与 IF/ELSE 结构和 BuiltIn 关键字一起工作,无需在表达式周围使用 ${{}} 装饰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*** Variables ***
${VAR} 123

*** Test Cases ***
Python syntax
Should Be True ${{'${VAR}' == '123'}}
Should Be True ${{'${VAR}'.startswith('x') or '${VAR}' in '012345'}}

Python builtins
Should Be Equal ${{len('${VAR}')}} ${3}
Should Be Equal ${{int('${VAR}')}} ${123}

Access modules
Should Be Equal ${{os.sep}} ${/}
Should Be Equal ${{round(math.pi, 2)}} ${3.14}
Should Start With ${{robot.__version__}} 4.

使用模块的一个限制是,只有当根模块自动导入子模块时,才能使用像 rootmod.submod 这样的嵌套模块。这并不总是这样,使用这样的模块是不可能的。一个相关的具体示例是 selenium 模块,至少在撰写本文时,仅导入 selenium 并不导入 selenium.webdriver 子模块。另一个限制是,当使用 Python 3 时,模块不能在列表推导的表达式部分使用。这两个问题的解决方法是使用接受模块作为参数进行导入并添加到求值命名空间的 BuiltIn 关键字 Evaluate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*** Test Cases ***
Does not work due to nested module structure
Log ${{selenium.webdriver.ChromeOptions()}}

Evaluate keyword with nested module
${options} = Evaluate selenium.webdriver.ChromeOptions() modules=selenium.webdriver
Log ${options}

Does not work due to list comprehension
Log ${{[json.loads(item) for item in ('1', '"b"')]}}

Evaluate keyword with list comprehension
${items} = Evaluate [json.loads(item) for item in ('1', '"b"')] modules=json
Log ${items}

如果需要进一步的定制,Evaluate 关键字还支持自定义求值命名空间。有关更多详细信息,请参阅 BuiltIn 库中的文档。

使用变量

正常的 ${variable} 语法

当在表达式中使用正常的 {variable} 语法使用变量时,其值会在表达式求值之前被替换。这意味着在表达式中使用的值将是变量值的字符串表示,而不是变量值本身。对于数字和其他可以直接求值的对象的字符串表示,这不是问题。例如,如果有一个作为整数的返回码在变量variable语法使用变量时,其值会在表达式求值之前被替换。这意味着在表达式中使用的值将是变量值的字符串表示,而不是变量值本身。对于数字和其他可以直接求值的对象的字符串表示,这不是问题。例如,如果有一个作为整数的返回码在变量{rc} 中,使用像 ${rc} > 0 这样的东西是可以的。

对于其他对象,行为取决于字符串表示。最重要的是,字符串必须始终用单引号或双引号引起来,如 ‘{x}’,如果它们可以包含换行符,它们必须用三引号引起来,如 ‘’’x′,如果它们可以包含换行符,它们必须用三引号引起来,如′′′{x}’‘’。包含引号本身的字符串会导致额外的问题,但通常可以通过三引号来处理。另外,反斜杠字符 \ 是有问题的,但可以通过使用 Python 的原始字符串表示法,如 r’${path}’ 来处理。

1
2
3
4
5
6
7
8
9
*** Test Cases ***
Using normal variable syntax
Should Be True ${rc} > 0
IF '${status}'.upper() == 'PASS'
Log Passed
END
IF 'FAIL' in r'''${output}'''
Log Output contains FAIL
END
特殊的 $variable 语法

引用字符串并不那么方便,但有些情况下,将变量替换为其字符串表示会导致更大的问题。例如,如果变量值可以是字符串或 Python None,需要像 ‘${var}’ 这样引用,否则字符串就不起作用,但那么 None 就被解释为字符串了。幸运的是,这个部分讨论的这些问题有一个简单的解决方案。

实际的变量值在求值命名空间中可用,并可以使用没有花括号的特殊变量语法,如 $variable 来访问。这样的变量永远不应该被引用,即使它们包含字符串。

将这些示例与上一节的示例进行比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
*** Test Cases ***
Using special variable syntax
Should Be True $rc > 0
IF $status.upper() == 'PASS'
Log Passed
END
IF 'FAIL' in $output
Log Output contains FAIL
END

Only possible using special variable syntax
Should Be True $example is not None
Should Be True len($result) > 1 and $result[1] == 'OK'

使用 $variable 语法会稍微减慢表达式求值的速度。这通常不应该有问题,但如果经常求值复杂的表达式,并且有严格的时间限制,应该考虑到这一点。无论如何,将这样的逻辑移动到测试库通常是一个好主意。

注意

由于技术原因,这些特殊变量在求值期间作为局部变量可用。这使得它们在非局部范围内不可用,例如在列表推导的表达式部分和在 lambda 内部。

注册

本附录列出了与 Robot Framework 关联的文件扩展名、媒体类型等。

套件文件扩展名

以下扩展名的套件文件会被自动解析:

.robot 使用纯文本格式的套件文件。 .robot.rst 使用 reStructuredText 格式的套件文件。 .rbt 使用 JSON 格式的套件文件。 使用其他扩展名是可能的,但需要单独配置。

资源文件扩展名

资源文件可以使用以下扩展名:

.resource 推荐在使用纯文本格式时使用。 .robot, .txt 和 .tsv 出于向后兼容性原因,支持在纯文本格式中使用。推荐使用 .resource,将来可能会强制使用。 .rst 和 .rest 使用 reStructuredText 格式的资源文件。 .rsrc 和 .json 使用 JSON 格式的资源文件。

媒体类型

使用 Robot Framework 数据的媒体类型是 text/robotframework。

远程服务器端口

默认的远程服务器端口是 8270。该端口已由 IANA 注册。

  • 标题: Robot Framework教程
  • 作者: Yiuhang Chan
  • 创建于 : 2024-04-19 11:17:08
  • 更新于 : 2024-04-25 16:44:46
  • 链接: https://www.yiuhangblog.com/2024/04/19/20240419RobotFramework教程/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
此页目录
Robot Framework教程