Robot Framework 新版教程 - 控制结构
(Robot Framework 7.x 教程, Part 10)
控制结构
本节描述可用于控制测试执行流程的各种结构。这些结构在大多数编程语言中都很常见,它们允许条件执行、重复执行一组关键字以及细粒度的错误处理。为了可读性,应谨慎使用这些结构,更复杂的用例最好在测试库中实现。
FOR 循环
在测试自动化中,重复执行相同操作是很常见的需求。使用 Robot Framework 时,测试库可以包含任何类型的循环结构,大多数情况下循环应在库中实现。Robot Framework 也有自己的 FOR 循环语法,例如当需要重复调用来自不同库的关键字时非常有用。
FOR 循环可以在测试用例和用户关键字中使用。除了非常简单的情况外,用户关键字更好,因为它们隐藏了 FOR 循环引入的复杂性。基本的 FOR 循环语法 FOR item IN sequence 源自 Python,但许多其他编程语言也支持类似的语法。
简单的 FOR 循环
在普通的 FOR 循环中,一个变量根据值列表进行赋值,每次迭代一个值。语法以 FOR(区分大小写)作为标记开始,然后是循环变量,接着是必须的 IN(区分大小写)作为分隔符,最后是要迭代的值。这些值可以包含变量,包括列表变量。
FOR 循环中使用的关键字位于后续行中,循环以单独一行的 END(区分大小写)结束。循环内的关键字不需要缩进,但强烈建议缩进以使语法更易于阅读。
*** 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 逐一对所有元素执行。
*** Test Cases ***
Example
FOR ${element} IN @{ELEMENTS}
Start Element ${element}
END
旧的 FOR 循环语法
在 Robot Framework 3.1 之前,FOR 循环语法与现在不同。启动循环的标记是 :FOR 而不是 FOR,循环内容需要用反斜杠显式标记,而不是使用 END 标记来结束循环。上面的第一个示例使用旧语法如下:
*** 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 循环:
*** Keywords ***
Handle Table
[Arguments] @{table}
FOR ${row} IN @{table}
FOR ${cell} IN @{row}
Handle Cell ${cell}
END
END
可以有多层嵌套,循环也可以与其他控制结构组合使用:
*** 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 标记之间使用多个循环变量,可以在一次迭代中遍历多个值。循环变量的数量不限,但值的数量必须能被变量数量整除。每次迭代消耗与变量数量相同的值。
如果要迭代的值很多,通常方便将它们组织在循环变量下方,如下面示例中的第一个循环所示:
*** 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 标记(区分大小写),后跟循环限制。
在最简单的情况下,只指定循环的上限。此时循环索引从零开始,每次增加一,直到(但不包括)上限。也可以同时给出起始和结束限制,索引从起始限制开始,增长方式与简单情况相同。最后,还可以给出步长值来指定增量。如果步长为负数,则用作递减量。
可以对范围限制使用简单的算术运算,如加法和减法。当限制由变量指定时,这特别有用。起始值、结束值和步长通常以整数给出,但也可以使用浮点值。
*** 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,以此类推。
例如,以下两个测试用例做的是同样的事情:
*** 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 开始,可以使用 start=<index> 语法作为 FOR ... IN ENUMERATE ... 头部的最后一项来指定自定义起始索引:
*** 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=<index> 语法必须在 FOR 头部中显式使用,不能来自变量。如果最后一个实际要枚举的项以 start= 开头,需要转义为 start\=。
与普通 FOR 循环一样,只要列表中的值数量能被循环变量数量(不包括索引变量)整除,就可以在每次迭代中遍历多个值:
*** 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 元组:
*** 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() 函数。
用示例来说明可能最为直观:
*** 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},产生实际的迭代列表)的形式给出。前一种方式更常见,上面已经演示过了。后一种方式如下:
*** 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 元组。
*** 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选项指定的值填充,如果未使用则为None。这与 Python 的 zip_longest 函数相同,只是它使用fill参数而不是fillvalue。
以下示例说明了所有这些模式:
*** Variables ***
@{CHARACTERS} a b c d f
@{NUMBERS} 1 2 3
*** Test Cases ***
STRICT mode
[Documentation] This loop fails due to lists lengths being different.
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=STRICT
Log ${c}: ${n}
END
SHORTEST mode
[Documentation] This loop executes three times.
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=SHORTEST
Log ${c}: ${n}
END
LONGEST mode
[Documentation] This loop executes five times.
... On last two rounds `${n}` has value `None`.
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=LONGEST
Log ${c}: ${n}
END
LONGEST mode with custom fill value
[Documentation] This loop executes five times.
... On last two rounds `${n}` has value `0`.
FOR ${c} ${n} IN ZIP ${CHARACTERS} ${NUMBERS} mode=LONGEST fill=0
Log ${c}: ${n}
END
如果列表长度不同,未来的默认行为将更改为
STRICT模式。如果不希望如此,需要显式使用SHORTEST模式。
字典迭代
普通 FOR 循环和 FOR-IN-ENUMERATE 循环支持遍历字典中的键和值。此语法要求至少有一个循环值是字典变量。可以使用多个字典变量,也可以用 key=value 语法提供额外的项。项按定义的顺序迭代,如果同一个键有多个值,将使用最后一个值。
*** 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 循环使用两个变量意味着将索引赋给第一个变量,第二个变量成为包含键和值的元组。
*** 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
除了遍历字典中的名称和值之外,还可以只遍历键,然后根据键获取值。此语法要求将字典作为列表变量使用:
*** Test Cases ***
Iterate over keys
FOR ${key} IN @{DICT}
Log Key is '${key}' and value is '${DICT}[${key}]'.
END
遍历字典中的键和值是 Robot Framework 3.2 的新功能。在更早的版本中,只能像上面最后一个示例那样遍历字典的键。
循环变量转换
变量类型转换也适用于 FOR 循环变量。可以使用熟悉的 ${name: type} 语法为任何循环变量添加所需的类型。
*** Test Cases ***
Variable conversion
FOR ${value: bytes} IN Hello! Hyvä! \x00\x00\x07
Log ${value} formatter=repr
END
FOR ${index} ${date: date} IN ENUMERATE 2023-06-15 2025-05-30 today
Log ${date} formatter=repr
END
FOR ${item: tuple[str, date]} IN ENUMERATE 2023-06-15 2025-05-30 today
Log ${item} formatter=repr
END
变量类型转换是 Robot Framework 7.3 的新功能。
从输出中移除不必要的关键字
迭代次数多的 FOR 循环通常会产生大量输出,并显著增加生成的 output 和 log 文件的大小。可以使用 --removekeywords 和 --flattenkeywords 命令行选项来移除或扁平化不必要的关键字。
重复执行单个关键字
在只需要重复执行单个关键字的情况下,FOR 循环可能显得过于繁重。这种情况下使用 BuiltIn 库的 Repeat Keyword 关键字通常更简单。该关键字接受一个关键字和重复次数作为参数。重复次数可以带有可选的后缀 times 或 x,使语法更易于阅读。
*** 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 语法
*** 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 这样的用法。普通变量如上例中的 ${rc} 在求值前会被替换,但变量也可以使用特殊的 $rc 语法在求值命名空间中使用。后一种方式在变量的字符串表示不能直接用于条件时很方便。例如,字符串需要引号,多行字符串和本身包含引号的字符串会带来额外的问题。更多信息和与求值语法相关的示例,请参阅"表达式求值"附录。
从 Robot Framework 6.1 开始,WHILE 语句中的条件可以省略。这被解释为条件始终为真,与下面描述的 limit 选项配合使用时可能很有用。
限制 WHILE 循环迭代次数
使用 WHILE 循环时,总有可能出现无限循环的情况,无论是有意还是无意。当循环条件永远不变为假时就会发生这种情况。虽然无限循环在应用程序编程中有一些用途,但在自动化中,无限循环很少是期望的结果。如果在 Robot Framework 中出现这样的循环,执行必须被强制停止,且无法创建 log 或 report。因此,Robot Framework 中的 WHILE 循环默认限制为 10000 次迭代。如果超过限制,循环将失败。
可以通过 limit 配置参数设置限制,既可以是最大迭代次数,也可以是整个循环的最大时间。当限制是迭代次数时,可以只使用整数如 100,也可以在值后面加 times 或 x 后缀,如 100 times。当限制是超时时间时,可以使用时间字符串如 10 s 或 1 hour 10 minutes。也可以使用 NONE(不区分大小写)完全禁用限制。以下示例说明了所有这些选项。
*** 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
支持使用
times和x后缀指定迭代次数是 Robot Framework 7.0 的新功能。
当超过限制时,循环中的关键字不会被强制停止。循环的退出方式与条件变为假时相同。主要区别在于此时循环状态将为 FAIL。
从 Robot Framework 6.1 开始,可以使用 on_limit 参数配置超过限制时的行为。它支持两个值 pass 和 fail(不区分大小写)。如果值为 pass,达到限制时执行将正常继续,WHILE 循环的状态为 PASS。值 fail 的行为与默认行为相同,即如果超过限制,循环和测试将失败。
*** 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 配置参数更改错误消息。
*** 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 循环可以嵌套,也可以与其他控制结构组合使用:
*** Test Cases ***
Nesting WHILE
VAR ${x} 10
WHILE ${x} > 0
VAR ${y} ${x}
WHILE ${y} > 0
${y} = Evaluate ${y} - 1
END
IF ${x} > 5
${x} = Evaluate ${x} - 1
ELSE
${x} = Evaluate ${x} - 2
END
END
从输出中移除不必要的关键字
迭代次数多的 WHILE 循环通常会产生大量输出,并显著增加生成的 output 和 log 文件的大小。可以使用 --removekeywords 和 --flattenkeywords 命令行选项来移除或扁平化不必要的关键字。
使用 BREAK 和 CONTINUE 进行循环控制
FOR 和 WHILE 循环的执行都可以通过 BREAK 和 CONTINUE 语句来控制。前者提前退出整个循环,后者停止执行当前循环迭代并继续下一次迭代。实际上,它们与 Python、Java 及许多其他编程语言中的 break 和 continue 语句具有相同的语义。
BREAK 和 CONTINUE 通常与 IF/ELSE 或 TRY/EXCEPT 结构配合条件使用,尤其是内联 IF 语法与它们搭配使用时很方便。这些语句必须在循环体中使用,可以在上述控制结构内部使用,但在循环体中调用的关键字中使用是无效的。
*** 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:
*** Test Cases ***
Example
IF ${rc} > 0
Some keyword
Another keyword
END
条件在 Python 中求值,因此 Python 内置函数如 len() 可用,模块也会自动导入以支持如 platform.system() == 'Linux' 和 math.ceil(${x}) == 1 这样的用法。普通变量如上例中的 ${rc} 在求值前会被替换,但变量也可以使用特殊的 $rc 语法在求值命名空间中使用。后一种方式在变量的字符串表示不能直接用于条件时很方便。例如,字符串需要引号,多行字符串和本身包含引号的字符串会带来额外的问题。更多信息和与求值语法相关的示例,请参阅"表达式求值"附录。
ELSE 分支
与大多数支持条件执行的语言一样,Robot Framework 的 IF 语法也支持 ELSE 分支,当 IF 条件不为真时执行。
在此示例中,如果 ${rc} 大于零则执行 Some keyword,否则执行 Another keyword:
*** 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),执行不同的关键字:
*** 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 标记。例如,以下两个关键字是等价的:
*** 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 分支:
*** 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。
*** 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 结构可以相互嵌套,也可以与 FOR 循环嵌套。以下示例使用了高级功能,如 FOR-IN-ENUMERATE 循环、用户关键字的仅限命名参数(named-only argument)和内联 Python 求值语法(${{len(${items})}})来说明这一点:
*** 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
其他条件执行关键字的方式
还有其他方法可以有条件地执行关键字:
-
用作测试套件、测试用例和关键字的 setup 或 teardown 的关键字名称可以使用变量指定。这便于从命令行等位置更改它们。
-
BuiltIn 库的
Run Keyword关键字接受一个实际要执行的关键字作为参数,因此它可以是变量。变量的值可以从之前的关键字动态获取或从命令行传入。 -
BuiltIn 库的
Run Keyword If和Run Keyword Unless关键字仅在特定表达式为真或假时才执行指定的关键字。但通常建议使用上面介绍的新IF/ELSE语法。 -
BuiltIn 库的另一个关键字
Set Variable If可以根据给定表达式动态设置变量。 -
BuiltIn 库中有多个关键字允许仅在测试用例或测试套件失败或通过时执行指定的关键字。
TRY/EXCEPT 语法
当关键字失败时,Robot Framework 的默认行为是停止当前测试并执行可能的 teardown。然而,在执行过程中也可能需要处理这些失败。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 语法可以基于错误消息处理失败:
*** 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 也可以有多个要匹配的消息,如果任何消息匹配则执行该分支。在所有这些情况下,消息可以使用变量,也可以使用字面字符串。
*** Test Cases ***
Multiple EXCEPT branches
TRY
Some Keyword
EXCEPT Error message # 先尝试匹配这个。
Error Handler 1
EXCEPT Another error # 如果上面没匹配到则尝试这个。
Error Handler 2
EXCEPT ${message} # 最后一次匹配尝试,这次使用变量。
Error Handler 3
END
Multiple messages with one EXCEPT
TRY
Some Keyword
EXCEPT Error message Another error ${message} # 匹配其中任一个。
Error handler
END
也可以有不带消息的 EXCEPT,此时它匹配任何错误。这样的 EXCEPT 只能有一个,且必须跟在其他可能的 EXCEPT 分支之后:
*** Test Cases ***
Match any error
TRY
Some Keyword
EXCEPT # 匹配任何错误。
Error Handler
END
Match any after testing more specific errors
TRY
Some Keyword
EXCEPT Error message # 先尝试匹配这个
Error Handler 1
EXCEPT # 匹配上面没有匹配到的任何错误。
Error Handler 2
END
使用模式匹配错误
默认情况下,使用 EXCEPT 匹配错误需要精确匹配。可以通过配置选项 type= 作为 EXCEPT 子句的参数来更改。有效值为 GLOB、REGEXP 或 START(不区分大小写),分别使匹配成为 glob 模式匹配、正则表达式匹配或只匹配错误的开头。使用值 LITERAL 与默认行为相同。如果一个 EXCEPT 有多个消息,此选项适用于所有消息。选项的值也可以用变量定义。
*** 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+ (Invalid|Bad) usage type=Regexp # 反斜杠需要转义。
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} 来将错误消息捕获到变量中:
*** 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 分支之后:
*** 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 组合使用:
*** Test Cases ***
Handle everything
TRY
Some Keyword
EXCEPT AS ${err}
Log Error occurred: ${err}
ELSE
Log No error occurred!
END
使用 FINALLY 无论是否有错误都执行关键字
可选的 FINALLY 分支允许在有错误和没有错误时都执行关键字。因此它们适合在关键字执行后进行清理,类似于 teardown。只能有一个 FINALLY 分支,且必须始终在最后。它们可以与 EXCEPT 和 ELSE 分支组合使用,也可以有 TRY/FINALLY 结构:
*** 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相同。指定错误消息的语法也相同,只是该关键字默认使用 glob 模式匹配而非精确匹配。通常建议使用原生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的包装。现在建议使用原生语法替代。 -
测试 teardown 和关键字 teardown 可以用于清理活动,类似于
FINALLY分支。 -
当关键字在基于 Python 的库中实现时,Python 的所有错误处理功能都可以直接使用。尤其当所需逻辑变得更复杂时,推荐使用这种方式。
GROUP 语法
GROUP 语法允许将相关的关键字和控制结构组合在一起:
*** Test Cases ***
Valid login
GROUP Open browser to login page
Open Browser ${LOGIN URL}
Title Should Be Login Page
END
GROUP Submit credentials
Input Username username_field demo
Input Password password_field mode
Click Button login_button
END
GROUP Login should have succeeded
Title Should Be Welcome Page
END
Anonymous group
GROUP
Log Group name is optional.
END
Nesting
GROUP
GROUP Nested group
Log Groups can be nested.
END
IF True
GROUP
Log Groups can also be nested with other control structures.
END
END
END
如上面的示例所示,GROUP 可以有名称,但名称是可选的。GROUP 也可以与其他 GROUP 以及其他控制结构自由嵌套。
通常建议使用用户关键字而非 GROUP 语法,因为用户关键字可重用,且通过隐藏和封装底层细节简化了使用它们的测试或关键字。不过在 log 文件中,用户关键字和 GROUP 看起来相同,只是标签是 GROUP 而不是 KEYWORD。
同一测试或关键字内的所有 GROUP 共享相同的变量命名空间。这意味着,与使用关键字不同,不需要使用参数或返回值来共享值。在简单情况下这可以是一个好处,但如果变量很多,这个好处可能会变成问题并造成混乱。
GROUP语法是 Robot Framework 7.2 的新功能。
GROUP 与模板
GROUP 语法可以用于对测试模板的迭代进行分组:
*** Settings ***
Library String
Test Template Upper case should be
*** Test Cases ***
Template example
GROUP ASCII characters
a A
z Z
END
GROUP Latin-1 characters
ä Ä
ß SS
END
GROUP Numbers
1 1
9 9
END
*** Keywords ***
Upper case should be
[Arguments] ${char} ${expected}
${actual} = Convert To Upper Case ${char}
Should Be Equal ${actual} ${expected}
编程式使用
GROUP 的主要用途之一是使得以编程方式创建结构化的测试和用户关键字成为可能。例如,以下 pre-run modifier 在每个修改的测试末尾添加一个包含两个关键字的 GROUP。GROUP 也可以由使用 listener API version 3 的 listener 添加。
from robot.api import SuiteVisitor
class GroupAdder(SuiteVisitor):
def start_test(self, test):
group = test.body.create_group(name='Example')
group.body.create_keyword(name='Log', args=['Hello, world!'])
group.body.create_keyword(name='No Operation')