lyyyuna 的小花园

动静中之动, by

RSS

Robot Framework 新版教程 - 控制结构

发表于 2026-04

控制结构

本节描述可用于控制测试执行流程的各种结构。这些结构在大多数编程语言中都很常见,它们允许条件执行、重复执行一组关键字以及细粒度的错误处理。为了可读性,应谨慎使用这些结构,更复杂的用例最好在测试库中实现。

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

使用多个循环变量

通过在 FORIN 标记之间使用多个循环变量,可以在一次迭代中遍历多个值。循环变量的数量不限,但值的数量必须能被变量数量整除。每次迭代消耗与变量数量相同的值。

如果要迭代的值很多,通常方便将它们组织在循环变量下方,如下面示例中的第一个循环所示:

*** 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 选项更改此行为,它有三个可能的值:

以下示例说明了所有这些模式:

*** 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 关键字通常更简单。该关键字接受一个关键字和重复次数作为参数。重复次数可以带有可选的后缀 timesx,使语法更易于阅读。

*** 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,也可以在值后面加 timesx 后缀,如 100 times。当限制是超时时间时,可以使用时间字符串如 10 s1 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

支持使用 timesx 后缀指定迭代次数是 Robot Framework 7.0 的新功能。

当超过限制时,循环中的关键字不会被强制停止。循环的退出方式与条件变为假时相同。主要区别在于此时循环状态将为 FAIL

从 Robot Framework 6.1 开始,可以使用 on_limit 参数配置超过限制时的行为。它支持两个值 passfail(不区分大小写)。如果值为 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 命令行选项来移除或扁平化不必要的关键字。

使用 BREAKCONTINUE 进行循环控制

FOR 和 WHILE 循环的执行都可以通过 BREAKCONTINUE 语句来控制。前者提前退出整个循环,后者停止执行当前循环迭代并继续下一次迭代。实际上,它们与 Python、Java 及许多其他编程语言中的 breakcontinue 语句具有相同的语义。

BREAKCONTINUE 通常与 IF/ELSETRY/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

BREAKCONTINUE 语句是 Robot Framework 5.0 的新功能,与 WHILE 一样。更早的版本支持使用 BuiltIn 库的 Exit For LoopExit For Loop IfContinue For LoopContinue For Loop If 关键字来控制 FOR 循环。这些关键字仍然可以使用,但将来会被废弃和移除。

RETURN 语句也可以用于退出循环。它仅在循环用于用户关键字内部时有效。

IF/ELSE 语法

有时需要有条件地执行某些关键字。从 Robot Framework 4.0 开始有了单独的 IF/ELSE 语法,但也有其他条件执行关键字的方式。如果逻辑变得复杂,通常最好将其移到测试库中。

基本 IF 语法

Robot Framework 的原生 IF 语法以 IF(区分大小写)开始,以 END(区分大小写)结束。IF 标记恰好需要一个值,即要求值的条件。条件为真时要执行的关键字位于 IFEND 标记之间的各行中。强烈建议对 IF 块中的关键字进行缩进,但这不是强制的。

在以下示例中,如果 ${rc} 大于零,将执行 Some keywordAnother 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 语法也支持 ELSEELSE 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 IFELSE 分支的内联 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

其他条件执行关键字的方式

还有其他方法可以有条件地执行关键字:

TRY/EXCEPT 语法

当关键字失败时,Robot Framework 的默认行为是停止当前测试并执行可能的 teardown。然而,在执行过程中也可能需要处理这些失败。Robot Framework 5.0 引入了原生 TRY/EXCEPT 语法来实现此目的,但也有其他处理错误的方式。

Robot Framework 的 TRY/EXCEPT 语法受到 Python 异常处理语法的启发。它与 Python 具有相同的 TRYEXCEPTELSEFINALLY 分支,工作方式也大致相同。不同之处在于 Python 使用小写的 tryexcept 等,但 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 子句的参数来更改。有效值为 GLOBREGEXPSTART(不区分大小写),分别使匹配成为 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 分支将被执行;如果它以消息 XY 失败,相应的 EXCEPT 分支将运行。在所有这些情况下,执行在整个 TRY/EXCEPT/ELSE 结构之后继续。如果 Some Keyword 以其他方式失败,EXCEPTELSE 分支不会运行,TRY/EXCEPT/ELSE 结构将失败。

要同时处理有错误和没有错误的情况,可以将不带任何消息的 EXCEPTELSE 组合使用:

*** Test Cases ***
Handle everything
    TRY
        Some Keyword
    EXCEPT    AS    ${err}
        Log    Error occurred: ${err}
    ELSE
        Log    No error occurred!
    END

使用 FINALLY 无论是否有错误都执行关键字

可选的 FINALLY 分支允许在有错误和没有错误时都执行关键字。因此它们适合在关键字执行后进行清理,类似于 teardown。只能有一个 FINALLY 分支,且必须始终在最后。它们可以与 EXCEPTELSE 分支组合使用,也可以有 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

其他处理错误的方式

还有其他方法可以有条件地执行关键字:

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')
lyyyuna 沪ICP备2025110782号-1