Robot Framework 新版教程 - 变量
(Robot Framework 7.x 教程, Part 7)
变量
介绍
变量是 Robot Framework 的核心特性,可以在测试数据的大多数地方使用。最常见的用法是在 Test Case 和 Keywords 节中作为关键字的参数,同时所有设置项的值也允许使用变量。普通的关键字名称不能通过变量来指定,但可以使用 BuiltIn 库的 Run Keyword 关键字来达到相同的效果。
Robot Framework 拥有自己的变量体系,可以分别使用 ${SCALAR}、@{LIST} 和 &{DICT} 语法作为标量、列表或字典使用。除此之外,还可以直接使用 %{ENV_VAR} 语法访问环境变量。
变量在以下场景中很有用:
-
当多处使用的值经常变化时。使用变量后,只需要在变量定义的地方修改即可。
-
创建与系统无关、与操作系统无关的测试数据时。使用变量代替硬编码值可以大大简化这一过程(例如,用
${RESOURCES}代替c:\resources,用${HOST}代替10.0.0.1:8080)。因为变量可以在启动测试时通过命令行设置,修改系统相关的变量非常方便(例如,--variable RESOURCES:/opt/resources --variable HOST:10.0.0.2:1234)。这也有利于本地化测试,通常需要使用不同的本地化字符串运行相同的测试。 -
当关键字参数需要非字符串对象时。如果关键字本身不支持参数转换,那么没有变量就无法实现这一点。
-
当不同的关键字(甚至在不同的测试库中)需要相互通信时。你可以将一个关键字的返回值赋给变量,然后将其作为参数传递给另一个关键字。
-
当测试数据中的值很长或过于复杂时。例如,使用
${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})。更重要的是,大小写的使用应保持一致。
变量名(如 ${example})由变量标识符($、@、&、%)、花括号({、})和括号之间的基本名称组成。创建变量时,基本名称后面还可以跟一个变量类型定义,如 ${example: int}。
变量的基本名称可以包含任意字符。但强烈建议仅使用字母、数字、下划线和空格。这是使用扩展变量语法的当前要求,未来可能会成为所有变量的要求。
标量变量语法
在 Robot Framework 测试数据中使用变量最常见的方式是标量变量语法,如 ${var}。使用该语法时,变量名会被其值原样替换。大多数情况下变量值是字符串,但变量可以包含任何对象,包括数字、列表、字典,甚至自定义对象。
下面的示例演示了标量变量的用法。假设变量 ${GREET} 和 ${NAME} 已经可用,并分别被赋值为字符串 Hello 和 world,那么以下两个测试用例是等价的:
*** Test Cases ***
Constants
Log Hello
Log Hello, world!!
Variables
Log ${GREET}
Log ${GREET}, ${NAME}!!
当标量变量单独使用时(周围没有其他文本或变量),如上面的 ${GREET},变量会被其值原样替换,值可以是任何对象。如果变量不是单独使用的,如上面的 ${GREER}, ${NAME}!!,其值会先被转换为字符串,然后与其他数据拼接。
当使用命名参数语法(如
argname=${var})传递关键字参数时,变量值同样是原样使用而不进行字符串转换。
下面的示例演示了变量单独使用和与其他内容一起使用时的区别。首先,假设变量 ${STR} 被设置为字符串 Hello, world!,${OBJ} 被设置为以下 Python 对象的实例:
class MyObj:
def __str__(self):
return "Hi, terra!"
设置好这两个变量后,有如下测试数据:
*** 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!"
包含字节的标量变量
包含 bytes 或 bytearrays 的变量在处理方式上与其他包含非字符串值的变量略有不同:
-
如果单独使用,行为与其他对象完全相同,其值会原样传递给关键字。
-
如果仅与其他同样包含 bytes 或 bytearrays 的变量拼接,结果是 bytes 而非字符串。
-
如果与字符串或包含 bytes/bytearrays 以外类型的变量拼接,它们会像其他对象一样被转换为字符串,但字符串表示与 Python 中的正常表示不同。在 Python 中,字符串表示包含引号和
b前缀(如b'\x00'),但在 Robot Framework 中,引号和前缀被省略,每个字节被映射到具有相同序数的 Unicode 码点。实际上这等同于使用 Latin-1 编码将 bytes 转换为字符串。这种格式的一大好处是生成的字符串可以被转换回 bytes,例如使用 BuiltIn 库的Convert To Bytes关键字或通过自动参数转换。
以下示例演示了 bytes 的用法(bytearrays 的工作方式完全相同)。变量 ${a} 预期包含字节 \x00\x01,变量 ${b} 包含字节 a\xe4。
*** Test Cases ***
Bytes alone
[Documentation] Keyword gets bytes '\x00\x01'.
Keyword ${a}
Bytes concatenated with bytes
[Documentation] Keyword gets bytes '\x00\x01a\xe4'.
Keyword ${a}${b}
Bytes concatenated with others
[Documentation] Keyword gets string '=\x00\x01a\xe4='.
Keyword =${a}${b}=
拼接包含 bytes 的变量时得到 bytes 结果是 Robot Framework 7.2 中的新特性。在更早的版本中,结果是字符串。
所有字节在字符串表示中映射到对应的 Unicode 码点是 Robot Framework 7.2 中的新特性。在更早的版本中,只有 ASCII 范围内的字节直接映射到码点,其他字节使用转义格式表示。
列表变量语法
当变量以标量形式使用(如 ${EXAMPLE})时,其值会被原样使用。如果变量值是列表或类列表对象,也可以将其作为列表变量使用(如 @{EXAMPLE})。在这种情况下,列表会被展开,各个元素作为独立的参数分别传递。
用一个示例来解释最为直观。假设变量 ${USER} 包含一个有两个元素 robot 和 secret 的列表,那么以下前两个测试是等价的:
*** Test Cases ***
Constants
Login robot secret
List variable
Login @{USER}
List as scalar
Keyword ${USER}
上面的第三个测试说明,包含列表的变量也可以作为标量使用。在该测试中,关键字获得整个列表作为单个参数。
从 Robot Framework 4.0 开始,列表展开可以与列表元素访问结合使用:
*** Test Cases ***
Nested container
${nested} = Evaluate [['a', 'b', 'c'], {'key': ['x', 'y']}]
Log Many @{nested}[0] # 输出 'a'、'b' 和 'c'。
Log Many @{nested}[1][key] # 输出 'x' 和 'y'。
Slice
${items} = Create List first second third
Log Many @{items}[1:] # 输出 'second' 和 'third'。
列表变量与其他数据一起使用
列表变量可以与其他参数一起使用,包括其他列表变量。
*** Test Cases ***
Example
Keyword @{LIST} more args
Keyword ${SCALAR} @{LIST} constant
Keyword @{LIST} @{ANOTHER} @{ONE MORE}
列表变量与设置项一起使用
列表变量只能用于部分设置项。它们可以用于导入库和变量文件时的参数,但库和变量文件的名称本身不能是列表变量。同样,在 setup 和 teardown 中,列表变量不能用作关键字名称,但可以用于参数。在 tag 相关的设置项中可以自由使用。在不支持列表变量的地方,可以使用标量变量。
*** Settings ***
Library ExampleLibrary @{LIB ARGS} # 可以使用
Library ${LIBRARY} @{LIB ARGS} # 可以使用
Library @{LIBRARY AND ARGS} # 不可以使用
Suite Setup Some Keyword @{KW ARGS} # 可以使用
Suite Setup ${KEYWORD} @{KW ARGS} # 可以使用
Suite Setup @{KEYWORD AND ARGS} # 不可以使用
Test Tags @{TAGS} # 可以使用
字典变量语法
如前所述,包含列表的变量可以作为列表变量使用,将列表元素作为独立参数传递给关键字。类似地,包含 Python 字典或类字典对象的变量可以作为字典变量使用(如 &{EXAMPLE})。实际上,这意味着字典被展开,各个键值对作为命名参数传递给关键字。假设变量 &{USER} 的值为 {'name': 'robot', 'password': 'secret'},那么以下前两个测试用例是等价的:
*** Test Cases ***
Constants
Login name=robot password=secret
Dictionary variable
Login &{USER}
Dictionary as scalar
Keyword ${USER}
上面的第三个测试说明,包含字典的变量也可以作为标量使用。在该测试中,关键字获得整个字典作为单个参数。
从 Robot Framework 4.0 开始,字典展开可以与字典元素访问结合使用,使得 &{nested}[key] 这样的用法成为可能。
字典变量与其他数据一起使用
字典变量可以与其他参数一起使用,包括其他字典变量。因为命名参数语法要求位置参数在命名参数之前,字典后面只能跟命名参数或其他字典。
*** Test Cases ***
Example
Keyword &{DICT} named=arg
Keyword positional @{LIST} &{DICT}
Keyword &{DICT} &{ANOTHER} &{ONE MORE}
字典变量与设置项一起使用
字典变量通常不能用于设置项。唯一的例外是导入、setup 和 teardown,在这些场景中字典可以用作参数。
*** 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] 语法访问包含序列(如列表、字符串或 bytes)的变量的某个元素,其中 index 是所选值的索引。索引从零开始,可以使用负索引从末尾访问元素,使用过大的索引会导致错误。索引会自动转换为整数,也可以使用变量作为索引。
*** 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 一样,你可以指定起始索引、结束索引和步长:
*** 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.2 之前,元素和切片访问仅支持包含列表、元组或其他被视为类列表对象的变量。现在所有序列(包括字符串和 bytes)都支持。
访问字典单个元素
可以使用 ${NAME}[key] 语法访问字典变量的某个值,其中 key 是所选值的键名。键被视为字符串,但可以使用变量来表示非字符串键。以这种方式访问的字典值可以像标量变量一样使用。
如果字典是在 Robot Framework 数据中创建的,也可以使用属性访问语法(如 ${NAME.key})来访问值。更多关于该语法的详细信息请参见创建字典一节。
*** 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'}],以下测试会通过:
*** 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 关键字删除现有的环境变量。因为环境变量是全局的,在一个测试用例中设置的环境变量可以在其后执行的其他测试用例中使用。但是,对环境变量的修改在测试执行结束后不再有效。
*** Test Cases ***
Environment variables
Log Current user: %{USER}
Run %{JAVA_HOME}${/}javac
Environment variable with default
Set Port %{APPLICATION_PORT=8080}
支持指定默认值是 Robot Framework 3.2 中的新特性。
创建变量
变量可以通过本节讨论的不同方式创建:
- 在 Variable 节中
- 使用变量文件
- 通过命令行
- 基于关键字的返回值
- 使用
VAR语法 - 使用
Set Test/Suite/Global Variable关键字
除此之外,还有各种自动可用的内置变量,用户关键字参数和 FOR 循环也会创建变量。在大多数创建变量的地方,可以使用变量类型转换来方便地创建非字符串值的变量。类型转换的一个重要应用是创建秘密变量。
Variable 节
变量最常见的来源是套件文件和资源文件中的 Variables 节。Variables 节很方便,因为它允许在与其他测试数据相同的地方创建变量,而且所需的语法非常简单。其主要缺点是无法动态创建变量。如果这是个问题,可以使用变量文件代替。
创建标量值
最简单的变量赋值是将字符串设置到标量变量中。只需在 Variables 节的第一列给出变量名(包括 ${}),在第二列给出值。如果第二列为空,则设置为空字符串。已定义的变量也可以用在值中。
*** Variables ***
${NAME} Robot Framework
${VERSION} 2.0
${ROBOT} ${NAME} ${VERSION}
也可以(但不是必须)在变量名后面使用等号 =,使赋值更加明确。
*** Variables ***
${NAME} = Robot Framework
${VERSION} = 2.0
如果标量变量的值很长,可以使用 ... 语法将其拆分到多行。默认情况下各行使用空格连接,但可以使用 separator 配置选项来更改:
*** Variables ***
${EXAMPLE} This value is joined
... together with a space.
${MULTILINE} First line.
... Second line.
... Third line.
... separator=\n
separator 选项是 Robot Framework 7.0 中新增的,但更早的版本也支持配置分隔符。在旧版本中,第一个值可以包含特殊的 SEPARATOR 标记:
*** Variables ***
${MULTILINE} SEPARATOR=\n
... First line.
... Second line.
... Third line.
separator 选项和 SEPARATOR 标记都是大小写敏感的。推荐使用 separator 选项,除非需要同时支持旧版本。
创建列表
创建列表和创建标量值一样简单。变量名同样在 Variables 节的第一列,值在后续列中,但这次变量名必须以 @ 开头而不是 $。列表可以有任意数量的元素(包括零个),如果需要可以拆分到多行。
*** Variables ***
@{NAMES} Matti Teppo
@{NAMES2} @{NAMES} Seppo
@{NOTHING}
@{MANY} one two three four
... five six seven
如列表变量语法一节中所述,包含列表的变量可以作为标量使用(如
${NAMES}),也可以使用列表展开语法使用(如@{NAMES})。
创建字典
字典可以在 Variables 节中以类似列表的方式创建。区别是名称必须以 & 开头,并且元素需要使用 name=value 语法或基于已有的字典变量来创建。如果有多个同名元素,最后一个值优先。如果名称中包含字面等号,可以使用反斜杠转义(如 \=)。
*** 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
如字典变量语法一节中所述,包含字典的变量可以作为标量使用(如
${USER 1}),也可以使用字典展开语法使用(如&{USER 1})。
与普通的 Python 字典不同,使用这种语法创建的字典的值可以通过属性访问,这意味着可以使用扩展变量语法(如 ${VAR.key})。不过,这仅在键是有效的属性名称且不与 Python 字典的正常属性冲突时才有效。例如,单个值 ${USER}[name] 也可以通过 ${USER.name} 访问,但使用 ${MANY.3} 是不可行的。
对于嵌套字典,键可以通过
${DATA.nested.key}的方式访问。
字典也是有序的。这意味着迭代时,元素总是按照定义顺序排列。这在字典作为列表变量与 FOR 循环或其他场景配合使用时很有用。当字典作为列表变量使用时,实际值包含字典的键。例如,@{MANY} 变量的值为 ['first', 'second', 3]。
基于另一个变量创建变量名
从 Robot Framework 7.0 开始,可以基于另一个变量动态创建变量名:
*** Variables ***
${X} Y
${${X}} Z # 名称基于 '${X}' 创建。
*** Test Cases ***
Dynamically created name
Should Be Equal ${Y} Z
使用变量文件
变量文件是创建各种变量的最强大机制。它可以将变量赋值给任何对象,还支持动态创建变量。变量文件的语法和使用方法在资源文件和变量文件一节中介绍。
命令行变量
变量可以通过命令行设置,既可以使用 --variable (-v) 选项逐个设置,也可以使用 --variablefile (-V) 选项通过变量文件设置。从命令行设置的变量对所有执行的测试数据文件全局可用,并且会覆盖 Variables 节和 Settings 节中导入的变量文件中同名的变量。
逐个设置变量的语法是 --variable name:value,其中 name 是不含 ${} 修饰的变量名,value 是其值。可以多次使用此选项设置多个变量。
--variable EXAMPLE:value
--variable HOST:localhost:7272 --variable USER:robot
在上面的示例中,变量设置如下:
${EXAMPLE}获得值value${HOST}和${USER}分别获得值localhost:7272和robot
通过命令行使用变量文件的基本语法是 --variablefile path/to/variables.py,"使用变量文件"一节有更详细的说明。实际创建哪些变量取决于引用的变量文件中有哪些变量。
如果同时从命令行指定了变量文件和单独的变量,后者具有更高的优先级。
关键字的返回值
关键字的返回值也可以赋给变量。这允许不同关键字之间进行通信,即使它们来自不同的库——通过将创建的变量作为参数传递给其他关键字。
以这种方式设置的变量在其他方面与任何其他变量类似,但它们仅在创建它们的局部作用域中可用。因此,不可能在一个测试用例中这样设置变量并在另一个测试用例中使用。这是因为,一般来说,自动化测试用例不应该相互依赖,意外设置一个在其他地方使用的变量可能会导致难以调试的错误。如果确实需要在一个测试用例中设置变量并在另一个中使用,可以使用 VAR 语法或 Set Test/Suite/Global Variable 关键字,具体在后续章节中介绍。
赋值标量变量
关键字返回的任何值都可以赋给标量变量。如下面的示例所示,所需的语法非常简单:
*** Test Cases ***
Returning
${x} = Get X an argument
Log We got ${x}!
在上面的示例中,Get X 关键字返回的值先被设置到变量 ${x} 中,然后被 Log 关键字使用。在被赋值变量名后面加等号 = 不是必须的,但可以使赋值更加明确。像这样创建局部变量在测试用例和用户关键字层级都适用。
注意,虽然值被赋给了标量变量,但如果它具有类列表的值,可以作为列表变量使用;如果它具有类字典的值,可以作为字典变量使用。
*** Test Cases ***
List assigned to scalar variable
${list} = Create List first second third
Length Should Be ${list} 3
Log Many @{list}
赋值变量元素
从 Robot Framework 6.1 开始,对于支持元素赋值的变量(如列表或字典),可以使用 ${var}[item] 语法来设置它们的值,其中 item 部分本身可以包含变量:
*** Test Cases ***
List item assignment
${list} = Create List one two three four
${list}[0] = Set Variable first
${list}[${1}] = Set Variable second
${list}[2:3] = Create List third
${list}[-1] = Set Variable last
Log Many @{list} # 输出 'first'、'second'、'third' 和 'last'
Dictionary item assignment
${dict} = Create Dictionary first_name=unknown
${dict}[first_name] = Set Variable John
${dict}[last_name] = Set Variable Doe
Log ${dictionary} # 输出 {'first_name': 'John', 'last_name': 'Doe'}
基于另一个变量创建变量名
从 Robot Framework 7.0 开始,可以基于另一个变量动态创建被赋值变量的名称:
*** Test Cases ***
Dynamically created name
${x} = Set Variable y
${${x}} = Set Variable z # 名称基于 '${x}' 创建。
Should Be Equal ${y} z
赋值列表变量
如果关键字返回一个列表或任何类列表对象,可以将其赋给列表变量:
*** Test Cases ***
Assign to list variable
@{list} = Create List first second third
Length Should Be ${list} 3
Log Many @{list}
因为所有 Robot Framework 变量都存储在同一个命名空间中,将值赋给标量变量和列表变量之间没有太大区别。将上面的示例与前面的 List assigned to scalar variable 测试用例做比较即可看出。主要区别是,创建列表变量时,Robot Framework 会自动验证该值是列表或类列表对象,并且存储的变量值将是根据返回值创建的新列表。赋给标量变量时,不会验证返回值,存储的值就是返回的那个对象。
赋值字典变量
如果关键字返回一个字典或任何类字典对象,可以将其赋给字典变量:
*** Test Cases ***
Assign to dictionary variable
&{dict} = Create Dictionary first=1 second=${2} ${3}=third
Length Should Be ${dict} 3
Do Something &{dict}
Log ${dict.first}
因为所有 Robot Framework 变量都存储在同一个命名空间中,也可以将字典赋给标量变量,在需要时再作为字典使用。但是,显式创建字典变量有一些具体的好处。首先,Robot Framework 会验证返回值是字典或类字典对象,就像验证列表变量只能获得类列表值一样。
更大的好处是,值会被转换为一种特殊字典,这种字典在 Variables 节中创建字典时也会使用。这些字典中的值可以使用属性访问方式访问,如上面示例中的 ${dict.first}。
多变量赋值
如果关键字返回一个列表或类列表对象,可以将各个值赋给多个标量变量,或者赋给标量变量和一个列表变量。
*** 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 命令行选项来更改。如果值为零或负数,则整个被赋值的值会被隐藏。
--maxassignlength 1000
--maxassignlength 0
值不被完整记录的原因是它可能非常大。如果你总是想完整查看某个值,可以在赋值后使用 BuiltIn 库的 Log 关键字来记录它。
--maxassignlength选项是 Robot Framework 5.0 中新增的。
VAR 语法
从 Robot Framework 7.0 开始,可以在测试和用户关键字内部使用 VAR 语法创建变量。VAR 标记是大小写敏感的,后面必须跟变量名和值。除了必须的 VAR 之外,整体语法与在 Variables 节中创建变量基本相同。
这种新语法旨在使创建变量更简单、更统一。它特别用于替代 BuiltIn 库的 Set Variable、Set Local Variable、Set Test Variable、Set Suite Variable 和 Set Global Variable 关键字,但也可以用于替代 Catenate、Create List 和 Create Dictionary。
创建标量变量
在简单情况下,只需给出变量名及其值即可创建标量变量。值可以是硬编码的字符串,也可以包含变量。如果值很长,可以将其拆分到多列和多行。在这种情况下,各部分默认使用空格连接,但可以通过 separator 配置选项指定分隔符。变量名后面可以有可选的 =,与基于关键字返回值创建变量以及在 Variables 节中创建变量的方式相同。
*** 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 语法。
*** 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 语法创建的变量默认仅在创建它们的测试或用户关键字中可用。不过,可以通过 scope 配置选项来更改。支持的值如下:
LOCAL:使变量在当前局部作用域中可用。这是默认值。
TEST:使变量在当前测试中可用,包括测试调用的所有关键字。如果在套件级别使用,使变量在 suite setup 和 teardown 中可用,但不在测试或可能的子套件中可用。在 Robot Framework 7.2 之前,在套件级别使用此作用域会报错。
TASK:TEST 的别名,可在创建任务时使用。
SUITE:使变量在当前测试套件中可用,包括该套件中所有后续的测试,但不包括可能的子套件中的测试。
SUITES:使变量在当前测试套件及其子套件中可用。这是 Robot Framework 7.1 中新增的。
GLOBAL:使变量全局可用,包括所有后续的关键字和测试。
虽然 Robot Framework 变量不区分大小写,但建议非局部变量名使用大写字母。
*** 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 ${SUITES} nested suite value scope=SUITES
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 ${SUITES} nested 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 ${SUITES} new nested suite value
Should Be Equal ${GLOBAL} new global value
Scope example, part 2
Should Be Equal ${SUITE} new suite value
Should Be Equal ${SUITES} new nested 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 ${SUITES} nested suite value
Should Be Equal ${GLOBAL} global value
VAR ${TEST} new ${TEST} scope=TEST
VAR ${SUITE} new ${SUITE} scope=SUITE
VAR ${SUITES} new ${SUITES} scope=SUITES
VAR ${GLOBAL} new ${GLOBAL} scope=GLOBAL
Should Be Equal ${TEST} new test value
Should Be Equal ${SUITE} new suite value
Should Be Equal ${SUITES} new nested suite value
Should Be Equal ${GLOBAL} new global value
条件创建变量
VAR 语法与 IF/ELSE 结构配合使用,可以方便地条件性创建变量。在简单情况下,使用内联 IF 会很方便。
*** 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
基于另一个变量创建变量名
如有需要,也可以基于另一个变量动态创建变量名。
*** Test Cases ***
Dynamic name
VAR ${x} y # 普通赋值。
VAR ${${x}} z # 名称动态创建。
Should Be Equal ${y} z
Set Test/Suite/Global Variable 关键字
在 Robot Framework 7.0 或更新版本中,推荐使用
VAR语法代替这些关键字。
BuiltIn 库有 Set Test Variable、Set Suite Variable 和 Set Global Variable 关键字,可用于在测试执行期间动态设置变量。如果变量已经存在于新的作用域中,其值将被覆盖,否则会创建新变量。
使用 Set Test Variable 关键字设置的变量在当前执行的测试用例的整个作用域中都可用。例如,如果你在用户关键字中设置了一个变量,它在测试用例级别和当前测试中使用的所有其他用户关键字中都可用。其他测试用例看不到用此关键字设置的变量。在测试作用域之外调用 Set Test Variable 会报错(例如在 Suite Setup 或 Teardown 中)。
使用 Set Suite Variable 关键字设置的变量在当前执行的测试套件的整个作用域中都可用。用此关键字设置变量与在测试数据文件中使用 Variables 节创建变量或从变量文件中导入变量效果相同。其他测试套件(包括可能的子测试套件)看不到用此关键字设置的变量。
使用 Set Global Variable 关键字设置的变量在设置后执行的所有测试用例和测试套件中全局可用。用此关键字设置变量与通过命令行使用 --variable 和 --variablefile 选项创建变量效果相同。因为此关键字可以在任何地方改变变量,应谨慎使用。
Set Test/Suite/Global Variable关键字直接将命名变量设置到测试、套件或全局变量作用域中,不返回任何值。而另一个 BuiltIn 库关键字Set Variable使用返回值设置局部变量。
变量类型转换
变量值通常是字符串,但也经常需要非字符串值。前面已经讨论了创建非字符串值变量的各种方法:
- 变量文件允许创建任意类型的对象。
- 关键字的返回值可以包含任何对象。
- 变量可以基于包含非字符串值的已有变量来创建。
@{list}和&{dict}语法允许原生创建列表和字典。
除了以上方法,还可以在创建变量时指定变量类型(如 ${name: int}),值会自动转换为指定的类型。这称为变量类型转换,其工作方式将在本节讨论。
变量类型转换是 Robot Framework 7.3 中的新特性。
变量类型语法
通用的变量类型语法在数据中是 ${name: type},在命令行上是 name: type:value。冒号后面的空格在两种情况下都是必须的。虽然在某些上下文中变量名可以基于另一个变量动态创建,但类型和类型分隔符必须始终指定为字面值。
变量类型转换支持与库关键字的参数转换相同的基本类型。例如,${number: int} 表示变量 ${number} 的值会被转换为整数。
变量类型转换还支持使用联合语法指定多种可能的类型。例如,${number: int | float} 表示值先尝试转换为整数,如果失败则转换为浮点数。
参数化类型也受支持。例如,${numbers: list[int]} 表示值被转换为整数列表。
与库关键字的参数转换相比,最大的限制是不支持 Enum 和 TypedDict 转换,也不能使用自定义转换器。这些限制可能会在未来版本中解除。
变量转换仅在创建变量时支持,在使用变量时不支持。
数据中的变量转换
在数据中,变量转换在以下场景中工作:在 Variables 节创建变量时、使用 VAR 语法时,以及基于关键字返回值创建变量时:
*** Variables ***
${VERSION: float} 7.3
${CRITICAL: list[int]} [3278, 5368, 5417]
*** Test Cases ***
Variables section
Should Be Equal ${VERSION} ${7.3}
Should Be Equal ${CRITICAL} ${{[3278, 5368, 5417]}}
VAR syntax
VAR ${number: int} 42
Should Be Equal ${number} ${42}
Assignment
# 在简单情况下,VAR 语法更方便。
${number: int} = Set Variable 42
Should Be Equal ${number} ${42}
# 在这种情况下,类型转换更有用。
${match} ${version: float} = Should Match Regexp RF 7.3 ^RF (\\d+\\.\\d+)$
Should Be Equal ${match} RF 7.3
Should Be Equal ${version} ${7.3}
除上述之外,变量类型转换还可用于用户关键字参数和
FOR循环。更多详情请参阅它们的文档。
变量类型转换不适用于
Set Test/Suite/Global Variable关键字。需要使用VAR语法替代。
@{list} 和 &{dict} 变量的类型转换
类型转换在使用 @{list} 和 &{dict} 语法创建列表和字典时也适用。对于列表,类型指定为 @{name: type},类型表示列表元素的类型。对于字典,字典值的类型可以指定为 &{name: type}。如果还需要指定键的类型,可以使用 &{name: ktype=vtype} 语法。
*** Variables ***
@{NUMBERS: int} 1 2 3 4 5
&{DATES: date} rc1=2025-05-08 final=2025-05-30
&{PRIORITIES: int=str} 3278=Critical 4173=High 5334=High
创建列表和字典的另一种方式是创建 ${scalar} 变量,使用 list 和 dict 类型(可以参数化),并以 Python 列表和字典字面量形式给出值:
*** Variables ***
${NUMBERS: list[int]} [1, 2, 3, 4, 5]
${DATES: list[date]} {'rc1': '2025-05-08', 'final': '2025-05-30'}
${PRIORITIES: dict[int, str]} {3278: 'Critical', 4173: 'High', 5334: 'High'}
使用 Python 列表和字典字面量可能有些复杂,尤其对于非程序员来说。这种方式的主要好处是它支持嵌套结构而无需使用临时值。以下示例使用不同方法创建相同的 ${PAYLOAD} 变量:
*** Variables ***
@{CHILDREN: int} 2 13 15
&{PAYLOAD: dict} id=${1} name=Robot children=${CHILDREN}
*** Variables ***
${PAYLOAD: dict} {'id': 1, 'name': 'Robot', 'children': [2, 13, 15]}
命令行上的变量转换
变量转换也适用于使用 --variable 选项创建的命令行变量。语法是 name: type:value,由于空格是必须的,整个选项值通常需要用引号括起来。以下示例演示了此功能的一些可能用法:
--variable "ITERATIONS: int:99"
--variable "PAYLOAD: dict:{'id': 1, 'name': 'Robot', 'children': [2, 13, 15]}"
--variable "START_TIME: datetime:now"
转换失败
如果类型转换失败,会产生错误且变量不会被创建。当值无法转换为指定类型或类型本身不受支持时,转换会失败:
*** Test Cases ***
Invalid value
VAR ${example: int} invalid
Invalid type
VAR ${example: invalid} 123
秘密变量
变量类型转换的一个重要用途是创建所谓的秘密变量。这些变量封装了它们的值,使得在变量作为参数和返回值在关键字之间传递时,即使在 trace 级别也不会记录真实值。
秘密变量的实际值可以通过 value 属性访问。它主要供接受秘密值的库关键字使用,但也可以在数据中使用扩展变量语法(如 ${secret.value})来访问。在数据中访问值会使其像普通变量一样出现在日志文件中,因此应仅用于调试或测试目的。
秘密变量不会隐藏或加密它们的值。因此,所有能直接或间接通过 Robot Framework API 访问这些变量的代码都可以获取真实值。
秘密变量是 Robot Framework 7.4 中的新特性。
在数据中创建秘密变量
在数据中,秘密变量可以在 Variables 节中创建,也可以使用 VAR 语法创建。为避免秘密值对所有能访问数据的人可见,不能使用字面值创建秘密变量。相反,值必须使用已有的秘密变量或环境变量(如 %{NAME})来创建。在两种情况下,将秘密值与字面值拼接(如 %{SECRET}123)也是允许的。
如果在数据中显示秘密变量不是问题,可以使用环境变量的默认值(如 %{NAME=default})。名称甚至可以留空(如 %{=secret}),这样总是使用默认值。
*** Variables ***
${NORMAL: Secret} ${XXX} # ${XXX} 本身必须是秘密变量。
${ENVIRON: Secret} %{EXAMPLE} # 直接支持环境变量。
${DEFAULT: Secret} %{=robot123} # 环境变量默认值也适用。
${JOIN: Secret} ${XXX}-123 # 将秘密值与字面量拼接是可以的。
${LITERAL: Secret} robot123 # 这会失败。
列表和字典变量也支持秘密值:
*** Variables ***
@{LIST: Secret} ${XXX} %{EXAMPLE} %{=robot123} ${XXX}-123
&{DICT: Secret} normal=${XXX} env=%{EXAMPLE} env_default=%{=robot123} join=${XXX}-123
上面的示例使用了 Variables 节,但使用
VAR语法创建秘密变量的语法完全相同。
在命令行上创建秘密变量
命令行变量转换直接支持秘密值:
--variable "PASSWORD: Secret:robot123"
在命令行历史记录或持续集成系统日志中直接显示秘密值可能存在安全风险。一种缓解方式是使用环境变量:
--variable "PASSWORD: Secret:$PASSWORD"
许多运行测试或任务的系统也支持隐藏命令行中使用的秘密值。
以编程方式创建秘密变量
可以使用 robot.api.types.Secret 类以编程方式创建秘密变量。这通常由库和变量文件完成,但 pre-run modifiers 和 listeners 也可以在需要时使用秘密变量。
最简单的编程用法示例是变量文件:
from robot.api.types import Secret
USERNAME = "robot"
PASSWORD = Secret("robot123")
创建一个返回秘密值的关键字也不复杂:
from robot.api.types import Secret
def get_token():
return Secret("e5805f56-92e1-11f0-a798-8782a78eb4b5")
上面两个示例中的实际秘密值在代码中是可见的。在处理真正的秘密值时,通常更好的做法是从环境变量中读取秘密值、从外部系统获取或随机生成。
内置变量
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。 |
*** Test Cases ***
Example
Create Binary File ${CURDIR}${/}input.data Some text here${\n}on two lines
Set Environment Variable CLASSPATH ${TEMPDIR}${:}${CURDIR}${/}foo.jar
数字变量
变量语法可用于创建整数和浮点数,如下面的示例所示。当关键字期望获得实际的数字而不是看起来像数字的字符串作为参数时,这很有用。
*** Test Cases ***
Example 1A
Connect example.com 80 # Connect 获得两个字符串参数
Example 1B
Connect example.com ${80} # Connect 获得一个字符串和一个整数
Example 2
Do X ${3.14} ${-1e-4} # Do X 获得浮点数 3.14 和 -0.0001
也可以使用 0b、0o 和 0x 前缀分别从二进制、八进制和十六进制值创建整数。语法不区分大小写。
*** 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 也可以使用类似数字的变量语法创建。
*** Test Cases ***
Boolean
Set Status ${true} # Set Status 获得布尔值 true 作为参数
Create Y something ${false} # Create Y 获得一个字符串和布尔值 false
None
Do XYZ ${None} # Do XYZ 获得 Python None 作为参数
这些变量不区分大小写,因此 ${True} 和 ${true} 是等价的。接受布尔值的关键字通常会进行自动参数转换,按预期处理 True 和 false 这样的字符串值。在这种情况下不需要使用变量语法。
空格和空值变量
可以分别使用 ${SPACE} 和 ${EMPTY} 变量来创建空格和空字符串。这些变量很有用,例如当需要用反斜杠转义空格或空单元格时。如果需要多个空格,可以使用扩展变量语法(如 ${SPACE * 5})。在下面的示例中,Should Be Equal 关键字接收的参数相同,但使用变量的方式比使用反斜杠更容易理解。
*** 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} 的值是不可能的。
*** 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} |
当前测试用例的 tag,按字母顺序排列。可以使用 Set Tags 和 Remove Tags 关键字动态修改。 |
测试用例 |
${TEST DOCUMENTATION} |
当前测试用例的文档。可以使用 Set Test Documentation 关键字动态设置。 |
测试用例 |
${TEST STATUS} |
当前测试用例的状态,PASS 或 FAIL。 | Test teardown |
${TEST MESSAGE} |
当前测试用例的消息。 | Test teardown |
${PREV TEST NAME} |
上一个测试用例的名称,如果尚未执行任何测试则为空字符串。 | 全局 |
${PREV TEST STATUS} |
上一个测试用例的状态:PASS、FAIL 或空字符串(尚未执行任何测试时)。 | 全局 |
${PREV TEST MESSAGE} |
上一个测试用例的可能错误消息。 | 全局 |
${SUITE NAME} |
当前测试套件的完整名称。 | 全局 |
${SUITE SOURCE} |
套件文件或目录的绝对路径。 | 全局 |
${SUITE DOCUMENTATION} |
当前测试套件的文档。可以使用 Set Suite Documentation 关键字动态设置。 |
全局 |
&{SUITE METADATA} |
当前测试套件的自由元数据。可以使用 Set Suite Metadata 关键字设置。 |
全局 |
${SUITE STATUS} |
当前测试套件的状态,PASS 或 FAIL。 | Suite teardown |
${SUITE MESSAGE} |
当前测试套件的完整消息,包括统计信息。 | Suite teardown |
${KEYWORD STATUS} |
当前关键字的状态,PASS 或 FAIL。 | 用户关键字 teardown |
${KEYWORD MESSAGE} |
当前关键字的可能错误消息。 | 用户关键字 teardown |
${LOG LEVEL} |
当前日志级别。 | 全局 |
${OUTPUT DIR} |
输出目录的绝对路径(字符串)。 | 全局 |
${OUTPUT FILE} |
输出文件的绝对路径(字符串),如果未创建输出文件则为字符串 NONE。 |
全局 |
${LOG FILE} |
日志文件的绝对路径(字符串),如果未创建日志文件则为字符串 NONE。 |
全局 |
${REPORT FILE} |
报告文件的绝对路径(字符串),如果未创建报告文件则为字符串 NONE。 |
全局 |
${DEBUG FILE} |
调试文件的绝对路径(字符串),如果未创建调试文件则为字符串 NONE。 |
全局 |
&{OPTIONS} |
暴露命令行选项的字典。字典键对应命令行选项,可以使用 ${OPTIONS}[key] 和 ${OPTIONS.key} 两种方式访问。可用的选项包括:${OPTIONS.exclude}(--exclude)、${OPTIONS.include}(--include)、${OPTIONS.skip}(--skip)、${OPTIONS.skip_on_failure}(--skip-on-failure)、${OPTIONS.console_width}(整数,--console-width)、${OPTIONS.rpa}(布尔值,--rpa)。${OPTIONS} 本身是 RF 5.0 中新增的,${OPTIONS.console_width} 是 RF 7.1 中新增的,${OPTIONS.rpa} 是 RF 7.3 中新增的。后续可能会暴露更多选项。 |
全局 |
与套件相关的变量 ${SUITE SOURCE}、${SUITE NAME}、${SUITE DOCUMENTATION} 和 &{SUITE METADATA} 以及与命令行选项相关的变量(如 ${LOG FILE} 和 &{OPTIONS})在导入库和变量文件时就已经可用。不过,这些自动变量中可能包含的变量在导入时尚未解析。
变量优先级和作用域
来自不同来源的变量具有不同的优先级,并且在不同的作用域中可用。
变量优先级
来自命令行的变量
在命令行上设置的变量在实际测试执行开始前,具有所有可设置变量中的最高优先级。它们会覆盖在测试用例文件的 Variables 节中创建的变量,以及在测试数据中导入的资源文件和变量文件中的变量。
单独设置的变量(--variable 选项)会覆盖通过变量文件(--variablefile 选项)设置的变量。如果多次指定同一个变量,最后指定的那个会覆盖之前的。这允许在启动脚本中为变量设置默认值,然后从命令行覆盖。不过要注意,如果多个变量文件包含相同的变量,先指定的文件中的变量具有最高优先级。
测试用例文件中的 Variables 节
在测试用例文件中使用 Variables 节创建的变量对该文件中的所有测试用例可用。这些变量会覆盖导入的资源文件和变量文件中的同名变量。
Variables 节中创建的变量在文件的所有其他节中都可用。这意味着它们也可以在 Settings 节中使用,例如用于从资源文件和变量文件中导入更多变量。
导入的资源文件和变量文件
从资源文件和变量文件导入的变量在测试数据中创建的所有变量中具有最低优先级。资源文件和变量文件中的变量具有相同的优先级。如果多个资源文件和/或变量文件包含相同的变量,先导入的文件中的变量会被使用。
如果资源文件导入了其他资源文件或变量文件,其自身 Variables 节中的变量比它导入的变量具有更高的优先级。所有这些变量对导入该资源文件的文件都可用。
注意,从资源文件和变量文件导入的变量在导入它们的文件的 Variables 节中不可用。这是因为 Variables 节在 Settings 节(资源文件和变量文件在此导入)之前被处理。
在测试执行期间设置的变量
在测试执行期间使用关键字返回值、VAR 语法或 Set Test/Suite/Global Variable 关键字设置的变量总是覆盖其设置作用域中可能存在的变量。在某种意义上,它们具有最高优先级,但另一方面,它们不会影响其定义作用域之外的变量。
内置变量
${TEMPDIR} 和 ${TEST_NAME} 等内置变量具有所有变量中的最高优先级。它们不能通过 Variables 节或命令行覆盖,但即使它们也可以在测试执行期间被重新设置。此规则的例外是数字变量,它们在没有找到其他匹配变量时会被动态解析。因此数字变量可以被覆盖,但这通常不是个好主意。此外,${CURDIR} 比较特殊,因为它在测试数据处理阶段就已经被替换了。
变量作用域
变量根据创建的位置和方式,可以具有全局、测试套件、测试用例或局部作用域。
全局作用域
全局变量在测试数据中的任何位置都可用。这些变量通常通过命令行使用 --variable 和 --variablefile 选项设置,但也可以在测试数据中的任何位置使用 VAR 语法或 Set Global Variable 关键字创建新的全局变量或更改已有的全局变量。内置变量也是全局的。
建议全局变量使用大写字母。
测试套件作用域
具有测试套件作用域的变量在定义或导入它们的测试套件中的任何位置都可用。它们可以在 Variables 节中创建,从资源文件和变量文件导入,或者在测试执行期间使用 VAR 语法或 Set Suite Variable 关键字设置。
测试套件作用域不是递归的,这意味着在更高级别测试套件中可用的变量在低级别套件中不可用。如有需要,可以使用资源文件和变量文件来共享变量。
由于这些变量可以被视为其所在测试套件中的全局变量,建议也使用大写字母。
测试用例作用域
具有测试用例作用域的变量在测试用例及其使用的所有用户关键字中可见。初始时此作用域中没有变量,但可以在测试用例的任何位置使用 VAR 语法或 Set Test Variable 关键字来创建。
如果在 suite setup 中创建了具有测试作用域的变量,该变量在整个 suite setup 以及对应的 suite teardown 中都可用,但测试或可能的子套件看不到它。如果在 suite teardown 中创建了这样的变量,该变量仅在该 teardown 中可用。
具有测试用例作用域的变量在某种程度上也是全局的。因此通常也建议使用大写字母。
在 Robot Framework 7.2 之前,在 suite setup 或 teardown 中创建测试作用域的变量会导致错误。
局部作用域
测试用例和用户关键字有一个局部变量作用域,其他测试或关键字看不到。局部变量可以通过执行关键字的返回值和 VAR 语法创建,用户关键字还会将参数作为局部变量获得。
建议局部变量使用小写字母。
高级变量特性
扩展变量语法
扩展变量语法允许访问赋值给变量的对象的属性(例如 ${object.attribute}),甚至调用其方法(例如 ${obj.get_name()})。
扩展变量语法是一个强大的特性,但应谨慎使用。访问属性通常不是问题,相反,一个包含多个属性的对象的变量往往比多个变量更好。另一方面,调用方法(尤其是带参数时)会使测试数据变得难以理解。如果出现这种情况,建议将代码移到库中。
下面的示例展示了扩展变量语法的最常见用法。首先假设我们有以下变量文件和测试用例:
class MyObject:
def __init__(self, name):
self.name = name
def eat(self, what):
return f'{self.name} eats {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获得字符串RobotKW 2获得字符串Robot eats CucumberKW 3获得字符串two
扩展变量语法按以下顺序求值:
-
使用完整变量名搜索变量。仅当找不到匹配变量时才会对扩展变量语法求值。
-
创建基本变量名。名称的主体由开花括号
{后的所有字符组成,直到第一个非字母数字字符、下划线或空格为止。例如,${OBJECT.name}和${DICTIONARY[2]}的基本变量分别是OBJECT和DICTIONARY。 -
搜索与基本名称匹配的变量。如果没有匹配,则抛出异常,测试用例失败。
-
花括号内的表达式作为 Python 表达式求值,基本变量名被替换为其值。如果由于语法无效或查询的属性不存在而导致求值失败,则抛出异常,测试失败。
-
整个扩展变量被替换为求值返回的值。
许多标准 Python 对象(包括字符串和数字)都有可以通过扩展变量语法显式或隐式使用的方法。有时这非常有用,可以减少设置临时变量的需要,但也容易过度使用而创建出难以理解的测试数据。以下示例展示了一些不错的用法。
*** Test Cases ***
String
VAR ${string} abc
Log ${string.upper()} # 输出 'ABC'
Log ${string * 2} # 输出 'abcabc'
Number
VAR ${number} ${-2}
Log ${number * 10} # 输出 -20
Log ${number.__abs__()} # 输出 2
注意,虽然在正常 Python 代码中推荐使用 abs(number) 而不是 number.__abs__(),但 ${abs(number)} 不可行。这是因为变量名必须在扩展语法的开头。在测试数据中使用 __xxx__ 方法已经有些值得商榷了,通常更好的做法是将这类逻辑移到测试库中。
扩展变量语法也适用于列表变量和字典变量的上下文。例如,如果赋值给变量 ${EXTENDED} 的对象有一个属性 attribute,其值包含一个列表,则可以将其作为列表变量 @{EXTENDED.attribute} 使用。
扩展变量赋值
可以使用关键字返回值和扩展变量语法的变体来设置存储在标量变量中的对象的属性。假设我们有前面示例中的变量 ${OBJECT},可以像下面的示例一样设置属性。
*** Test Cases ***
Example
${OBJECT.name} = Set Variable New name
${OBJECT.new_attr} = Set Variable New attribute
扩展变量赋值语法按以下规则求值:
-
被赋值的变量必须是标量变量且至少包含一个点。否则不使用扩展赋值语法,变量正常赋值。
-
如果存在一个具有完整名称的变量(例如上面示例中的
${OBJECT.name}),该变量将被赋新值,不使用扩展语法。 -
创建基本变量名。名称的主体由
${和最后一个点之间的所有字符组成,例如${OBJECT.name}中的OBJECT和${foo.bar.zap}中的foo.bar。如第二个示例所示,基本名称可能包含普通的扩展变量语法。 -
要设置的属性名称由最后一个点和结束的
}之间的所有字符创建,例如${OBJECT.name}中的name。如果名称不以字母或下划线开头且仅包含这些字符和数字,则该属性被视为无效,不使用扩展语法。而是使用完整名称创建新变量。 -
搜索与基本名称匹配的变量。如果找不到变量,则不使用扩展语法,而是使用完整变量名创建新变量。
-
如果找到的变量是字符串或数字,则忽略扩展语法并使用完整名称创建新变量。这是因为你不能给 Python 字符串或数字添加新属性,而且这种方式也减少了向后兼容性问题。
-
如果所有前面的规则都匹配,则在基本变量上设置属性。如果设置因任何原因失败,则抛出异常,测试失败。
与使用关键字返回值正常赋值变量不同,使用扩展赋值语法所做的更改不限于当前作用域。因为没有创建新变量,而是更改了现有变量的状态,所有能看到该变量的测试和关键字也会看到这些更改。
嵌套变量
变量内部也允许使用变量,使用这种语法时,变量从内向外解析。例如,如果有变量 ${var${x}},则 ${x} 先被解析。如果它的值是 name,最终值就是变量 ${varname} 的值。可以有多层嵌套变量,但如果其中任何一个不存在,则最外层变量的解析会失败。
在下面的示例中,Do X 获得 ${JOHN HOME} 或 ${JANE HOME} 的值,取决于 Get Name 返回 john 还是 jane。如果返回其他值,解析 ${${name} HOME} 会失败。
*** Variables ***
${JOHN HOME} /home/john
${JANE HOME} /home/jane
*** Test Cases ***
Example
${name} = Get Name
Do X ${${name} HOME}
内联 Python 求值
变量语法也可以用于求值 Python 表达式。基本语法是 ${{expression}},即表达式外面有双花括号。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 中的新特性。