Robot Framework 新版教程 - 资源文件和变量文件
(Robot Framework 7.x 教程, Part 9)
套件文件和套件初始化文件中的用户关键字和变量只能在创建它们的文件中使用,而资源文件(resource files)提供了一种共享它们的机制。创建资源文件的高层语法与创建套件文件完全相同,支持的文件格式也一样。主要区别在于资源文件不能包含测试。
变量文件(variable files)提供了一种强大的创建和共享变量的机制。例如,它们允许使用字符串以外的值类型,并支持动态创建变量。由于变量文件使用 Python 或 YAML 创建,它们的灵活性也使其比 Variables section 稍微复杂一些。
资源文件
资源文件通常使用纯文本格式创建,但也支持 reStructuredText 格式和 JSON 格式。
引入资源文件
资源文件通过 Settings section 中的 Resource 设置导入,资源文件的路径作为该设置的参数。推荐的资源文件扩展名是 .resource。出于向后兼容的原因,.robot、.txt 和 .tsv 也可以使用,但未来可能会强制要求使用 .resource。
如果资源文件路径是绝对路径,则直接使用。否则,首先相对于导入文件所在目录搜索资源文件。如果未找到,则从 Python 模块搜索路径中的目录搜索。从模块搜索路径搜索资源文件使得可以将它们作为包数据打包到 Python 包中,并像 package/example.resource 这样导入。
资源文件路径可以包含变量,建议使用变量使路径与系统无关(例如 ${RESOURCES}/login.resource 或 ${RESOURCE_PATH})。此外,路径中的正斜杠(/)在 Windows 上会自动转换为反斜杠。
*** Settings ***
Resource example.resource
Resource ../resources/login.resource
Resource package/example.resource
Resource ${RESOURCES}/common.resource
资源文件中定义的用户关键字和变量在引入该资源文件的文件中可用。同样可用的还有该资源文件所导入的库、资源文件和变量文件中的所有关键字和变量。
.resource扩展名是 Robot Framework 3.1 的新功能。
资源文件结构
资源文件的高层结构与套件文件相同,但不能包含测试或任务。此外,资源文件的 Settings section 只能包含导入项(Library、Resource、Variables)、Documentation 和 Keyword Tags。Variables section 和 Keywords section 的使用方式与套件文件完全相同。
如果多个资源文件中存在同名的用户关键字,使用时必须在关键字名前加上不含扩展名的资源文件名作为前缀(例如 myresources.Some Keyword 和 common.Some Keyword)。此外,如果多个资源文件包含同名变量,最先导入的文件中的变量会被使用。
为资源文件添加文档
资源文件中创建的关键字可以使用 [Documentation] 设置添加文档。资源文件本身也可以在 Settings section 中添加 Documentation,与套件类似。
Libdoc 和各种编辑器会使用这些文档,打开资源文件的人也自然可以查看。关键字文档的第一个逻辑行(直到第一个空行)会在关键字运行时被记录到日志中,但资源文件的文档在测试执行期间会被忽略。
资源文件示例
*** 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}
使用 reStructuredText 格式的资源文件
可用于套件文件的 reStructuredText 格式同样适用于资源文件。此类资源文件可以使用 .rst 或 .rest 扩展名,导入方式与普通资源文件完全相同:
*** Settings ***
Resource example.rst
解析 reStructuredText 格式的资源文件时,Robot Framework 会忽略代码块之外的所有数据,与解析 reStructuredText 套件文件的方式完全相同。例如,以下资源文件导入了 OperatingSystem 库,定义了 ${MESSAGE} 变量,并创建了 My Keyword 关键字:
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 扩展名,导入方式与普通资源文件完全相同:
*** Settings ***
Resource example.rsrc
资源文件可以通过 ResourceFile.to_json 转换为 JSON 格式,也可以通过 ResourceFile.from_json 重建:
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')
变量文件
变量文件包含可在测试数据中使用的变量。变量也可以通过 Variables section 创建或从命令行设置,但变量文件允许动态创建变量,也便于创建字符串以外的变量值。
变量文件通常实现为模块,有两种创建变量的方式:
直接从模块获取变量:变量指定为模块属性。在简单场景中,语法非常简单,几乎不需要编程。例如,MY_VAR = 'my value' 会以指定文本为值创建变量 ${MY_VAR}。此方式的一个限制是不允许使用参数。
从特殊函数获取变量:变量文件可以有一个特殊的 get_variables(或 getVariables)方法,以映射形式返回变量。由于该方法可以接受参数,这种方式非常灵活。
变量文件还可以实现为类,框架会实例化该类。在这种情况下也可以将变量定义为属性或从 get_variables 方法动态获取。变量文件还可以创建为 YAML 和 JSON 格式。
引入变量文件
Settings section
所有测试数据文件都可以在 Settings section 中使用 Variables 设置导入变量文件。变量文件通常使用文件路径导入,方式与通过 Resource 设置导入资源文件类似。变量文件的路径被视为相对于导入文件所在目录的路径,如果未找到,则从模块搜索路径中搜索。路径也可以包含变量,斜杠在 Windows 上会转换为反斜杠。
*** Settings ***
Variables myvariables.py
Variables ../data/variables.py
Variables ${RESOURCES}/common.yaml
从 Robot Framework 5.0 开始,使用 Python 实现的变量文件也可以使用模块名导入,与库的导入方式类似。使用此方式时,模块需要在模块搜索路径中。
*** Settings ***
Variables myvariables
Variables rootmodule.Variables
如果变量文件接受参数,参数在变量文件的路径或名称之后指定:
*** Settings ***
Variables arguments.py arg1 ${ARG2}
Variables arguments argument
变量文件中的所有变量在导入它的测试数据文件中可用。如果导入了多个变量文件且包含同名变量,最先导入的文件中的变量会被使用。此外,Variables section 中创建的变量和从命令行设置的变量会覆盖变量文件中的变量。
命令行
引入变量文件的另一种方式是使用命令行选项 --variablefile。变量文件通过路径或模块名引用,与在 Settings section 中导入的方式类似。可能的参数用冒号(:)与路径连接:
--variablefile myvariables.py
--variablefile path/variables.py
--variablefile /absolute/path/common.py
--variablefile variablemodule
--variablefile arguments.py:arg1:arg2
--variablefile rootmodule.Variables:arg1:arg2
从命令行引入的变量文件同样会从模块搜索路径中搜索。相对路径被视为相对于执行开始的目录。
如果变量文件使用 Windows 绝对路径,驱动器号后面的冒号不被视为分隔符:
--variablefile C:\path\variables.py
也可以使用分号(;)作为参数分隔符。当变量文件参数本身包含冒号时这很有用,但在类 UNIX 操作系统上需要用引号包围整个值:
--variablefile C:\path\variables.py;D:\data.xls
--variablefile "myvariables.py;argument:with:colons"
从命令行引入的变量文件中的变量在所有测试数据文件中全局可用,与通过 --variable 选项设置的单个变量类似。如果同时使用了 --variablefile 和 --variable 选项且存在同名变量,通过 --variable 选项单独设置的变量优先。
直接从模块获取变量
基本语法
引入变量文件时,它们作为 Python 模块被导入,所有模块级别的、不以下划线(_)开头的属性默认被视为变量。由于变量名不区分大小写,大小写名称都可以使用,但通常推荐全局变量和属性使用大写字母。
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__ 前缀:
from collections import OrderedDict
LIST__ANIMALS = ["cat", "dog"]
DICT__FINNISH = OrderedDict([("cat", "kissa"), ("dog", "koira")])
这些前缀不会成为最终变量名的一部分,但它们会使 Robot Framework 验证值是否确实是列表型或字典型。对于字典,实际存储的值也会被转换为 Variables section 中创建字典时使用的特殊字典。这些字典的值可以通过属性访问,如 ${FINNISH.cat}。这些字典也是有序的,但保留源顺序要求原始字典也是有序的。
上面两个示例中的变量也可以使用以下 Variables section 创建:
*** 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}"会创建一个值为字面字符串an ${example}的变量${VAR},无论变量${example}是否存在。
使用对象作为值
变量文件中的变量不像 Variables section 那样仅限于字符串或其他基本类型。它们的变量可以包含任何对象。下面的示例中,变量 ${MAPPING} 包含一个 Python 字典,还有两个从同一文件中实现的自定义对象创建的变量。
MAPPING = {'one': 1, 'two': 2}
class MyObject:
def __init__(self, name):
self.name = name
OBJ1 = MyObject('John')
OBJ2 = MyObject('Jane')
动态创建变量
由于变量文件使用真正的编程语言创建,它们可以包含设置变量的动态逻辑。
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 库设置不同的变量,但你也可以使用自己的代码来构造值。下面的示例展示了这个概念,类似地,你的代码可以从数据库、外部文件读取数据,甚至向用户询问。
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} 变量。
通常额外的变量不会造成问题,但它们可能覆盖其他变量并导致难以调试的错误。一种忽略其他属性的方式是为它们添加下划线前缀:
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__,给它一个要作为变量处理的属性名列表:
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__ 前缀使列表和字典变量的创建更明确。下面的示例在功能上与直接从模块获取变量的第一个示例完全相同。
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 函数的参数相同。引入变量文件时,参数在变量文件路径之后指定;在命令行中,参数与路径之间用冒号或分号分隔。
下面的简单示例展示了如何在变量文件中使用参数。在更实际的场景中,参数可以是外部文本文件或数据库的路径,用于从中读取变量。
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 = '') 的变量文件可以这样导入:
*** Settings ***
Variables example.py 42 # 转换为整数
Variables example.py second=value # 命名参数语法
将变量文件实现为类
变量文件也可以实现为类。
实现
由于变量文件始终使用文件系统路径导入,类名必须与其所在模块名相同。
框架会使用无参数创建类的实例,变量从实例中获取。与模块类似,变量可以直接定义为实例的属性,也可以从特殊的 get_variables 方法获取。
当变量直接定义在实例中时,所有包含可调用值的属性会被忽略,以避免从实例可能拥有的方法中创建变量。如果确实需要可调用的变量,需要使用其他方式创建变量文件。
示例
第一个示例从属性创建变量。它从类属性创建 ${VARIABLE} 和 @{LIST},从实例属性创建 ${ANOTHER VARIABLE}:
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}:
class DynamicExample:
def get_variables(self, *args):
return {'dynamic variable': ' '.join(args)}
YAML 变量文件
变量文件也可以实现为 YAML 文件。YAML 是一种数据序列化语言,语法简单且对人类友好,同时也易于机器解析。以下示例展示了一个简单的 YAML 文件:
string: Hello, world!
integer: 42
list:
- one
- two
dict:
one: yksi
two: kaksi
with spaces: kolme
YAML 变量文件可以与普通变量文件完全相同地使用:通过命令行的 --variablefile 选项、Settings section 中的 Variables 设置,以及 Import Variables 关键字动态导入。它们通过扩展名自动识别,扩展名必须是 .yaml 或 .yml。如果导入上面的 YAML 文件,它将创建与以下 Variables section 完全相同的变量:
*** Variables ***
${STRING} Hello, world!
${INTEGER} ${42}
@{LIST} one two
&{DICT} one=yksi two=kaksi with spaces=kolme
用作变量文件的 YAML 文件在顶层必须始终是映射。如上例所示,映射中的键和值分别成为变量名和值。变量值可以是 YAML 语法支持的任何数据类型。如果名称或值包含非 ASCII 字符,YAML 变量文件必须使用 UTF-8 编码。
用作值的映射会自动转换为 Variables section 中创建字典时使用的特殊字典。这些字典的值可以通过属性访问,如 ${DICT.one}(前提是名称作为 Python 属性名是合法的)。如果名称包含空格或不是合法的属性名,始终可以使用 ${DICT}[with spaces] 语法访问字典值。
在 Robot Framework 中使用 YAML 文件需要安装 PyYAML 模块。通常可以通过 pip 安装:
pip install pyyaml。
JSON 变量文件
变量文件也可以实现为 JSON 文件。与上一节讨论的 YAML 类似,JSON 也是一种面向人类和机器的数据序列化格式。它基于 JavaScript 语法,虽然不如 YAML 对人类友好,但仍然相对容易理解和修改。以下示例包含与前面 YAML 示例完全相同的数据:
{
"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 的新功能。