.. role:: name(emphasis) .. role:: setting(emphasis) .. _creating user keywords: 创建用户关键字 ============== 在关键字表格(Keyword Table)中组合已有的关键字来创建新的高层次的关键字. 这些高级的关键字被称之为 *用户关键字*, 以区别于测试库所提供的底层的 *库关键字*. 创建用户关键字的语法和创建测试用例的语法非常接近, 所以很容易掌握. .. contents:: :depth: 2 :local: .. _User keyword syntax: 用户关键字语法 -------------- 基础语法 ~~~~~~~~ 用户关键字的语法绝大部分都和 :ref:`test case syntax` 一致. 用户关键字创建在关键字表格中, 而用例是在用例表格中. 如用例名称一样, 用户关键字的名称占表格的第一列. 同样, 用户关键字也是由关键字组成的, 包括库关键字和其它用户关键字. 这些关键字一般写在第二列, 但是在要接受返回值的情况下, 向后顺延. .. sourcecode:: robotframework *** Keywords *** Open Login Page Open Browser http://host/login.html Title Should Be Login Page Title Should Start With [Arguments] ${expected} ${title} = Get Title Should Start With ${title} ${expected} 大部分情况下用户关键字可以接受参数. 上面第二个例子已经展示了这一重要的特性, 并将在 下面的 :ref:`章节 ` 中详细介绍, 同时还将介绍 :ref:`user keyword return values`. 用户关键字可以在 :ref:`test case files`, :ref:`resource files`, 和 :ref:`test suite initialization files` 中创建. 在资源文件中创建的关键字在导入了该资源文件后即可使用, 其它文件中创建的关键字只在所在文件内可用. .. _Settings in the Keyword table: 关键字表格中的设置项 ~~~~~~~~~~~~~~~~~~~~ 用户关键字和 :ref:`test cases ` 有相似的设置项, 并且它们同样使用方括号来和关键字名称区分开. 所有可用的设置项都列在了下面, 并在后面的章节中详细说明. `[Documentation]`:setting: Used for setting a :ref:`user keyword documentation`. `[Tags]`:setting: Sets :ref:`tags ` for the keyword. `[Arguments]`:setting: Specifies :ref:`user keyword arguments`. `[Return]`:setting: Specifies :ref:`user keyword return values`. `[Teardown]`:setting: Specify :ref:`user keyword teardown`. `[Timeout]`:setting: Sets the possible :ref:`user keyword timeout`. :ref:`Timeouts` are discussed in a section of their own. .. _User keyword documentation: .. _User keyword name and documentation: 用户关键字的名称和文档 ---------------------- 用户关键字的名称在关键字表格的第一列中定义. 当然, 关键字的名称应该具备描述性, 使用很长的关键字名称也是可以的. 实际上, 当创建用户场景类(use-case-like)的测试用例时, 所使用的高层关键字往往看起来就像是句子甚至段落. 用户关键字的文档通过 :setting:`[Documentation]` 设置, 这点和 :ref:`test case documentation` 完全一样. 文档在测试数据中设置, 但是一般在更正式的关键字文档中显示, 这个文档由 Libdoc_ 工具从 :ref:`resource files` 中读取. 最后, 文档内容的第一行会作为关键字的文档在 :ref:`test logs` 中显示. 有时候关键字由于某种原因需要移出, 或者被新的替换, 或者被弃用. 在关键字文档的开始部分使用 ``*DEPRECATED*`` 可以标记该用户关键字已经不建议使用, 如果使用则会触发一个警告. 更多的说明请参见 :ref:`Deprecating keywords` . .. _User keyword tags: 用户关键字的标签 ---------------- 从Robot Framework 2.9 版本开始, 关键字也可以打标签了. 和 :ref:`test case tags` 类似, 用户关键字通过 :setting:`[Tags]` 设置标签. 不过 :setting:`Force Tags` 和 :setting:`Default Tags` 不会影响关键字. 此外, 关键字的标签可以在文档的最后一行中使用 `Tags:` 前缀后面跟逗号分隔的标签. 例如, 下面两个关键字都有相同的3个标签. .. sourcecode:: robotframework *** Keywords *** Settings tags using separate setting [Tags] my fine tags No Operation Settings tags using documentation [Documentation] I have documentation. And my documentation has tags. ... Tags: my, fine, tags No Operation 关键字的标签在日志和 Libdoc_ 生成的文档中都有显示, 同时还可以通过标签来搜索关键字. 命令行选项 :ref:`--removekeywords ` 和 :ref:`--flattenkeywords ` 同样支持按标签选择关键字, 可能还会有更多关于关键字标签的功能将在今后的版本中添加. Keyword tags are shown in logs and in documentation generated by Libdoc_, where the keywords can also be searched based on tags. The `--removekeywords`__ and `--flattenkeywords`__ commandline options also support selecting keywords by tag, and new usages for keywords tags are possibly added in later releases. 和 :ref:`test case tags` 类似, 带 ``robot-`` 前缀的标签名将作为 :ref:`保留 `, 以备Robot Framework实现特殊功能之用. 普通用户因此需避免使用 `robot-` 前缀的标签, 除非是为了触发这个特殊功能. .. _user keyword arguments: 用户关键字的参数 ---------------- 大部分的用户关键字需要接受一定的参数. 用来设置参数的语法可能是Robot Framework最复杂的特性, 但即使如此, 在大部分情况下也还是比较容易的. 参数通过 :setting:`[Arguments]` 设置, 参数名称的语法格式和 :ref:`variables` 一样, 如 ``${arg}``. .. _positional arguments: 位置参数 ~~~~~~~~ 指定参数最简单的方式(除了完全不使用参数的情况)是只使用位置参数. 大部分情况下, 位置参数就足够了. 首先是 :setting:`[Arguments]`, 后面的单元格中是声明的参数名称. 每个参数占一个单元格, 参数名称的语法格式和变量一样. 当关键字被调用时, 需要传递和此处定义相同数量的参数. 对于框架来说, 参数的名称并不重要, 不过从用户的角度看, 还是需要尽可能的描述清楚. 推荐使用小写字母作为名称, 如: ``${my_arg}``, ``${my arg}`` 或 ``${myArg}``. .. sourcecode:: robotframework *** Keywords *** One Argument [Arguments] ${arg_name} Log Got argument ${arg_name} Three Arguments [Arguments] ${arg1} ${arg2} ${arg3} Log 1st argument: ${arg1} Log 2nd argument: ${arg2} Log 3rd argument: ${arg3} .. _Default values with user keywords: 默认值参数 ~~~~~~~~~~ 大部分情况下仅用位置参数创建用户关键字就足够了. 不过, 有时候为关键字的某些参数指定 :ref:`default values` 也是很有用的. 要支持默认值, 在基础语法上添加少量改动即可. 为参数添加默认值只需要在参数名后面跟上等号(``=``), 然后跟上默认值即可, 例如 ``${arg}=default``. 可以为多个参数指定默认值, 指定默认值的参数必须跟在普通的位置参数后面. 除了本地局部作用域的变量不可作为默认值外, 参数的默认值可以包含来自 :ref:`其它作用域 ` 的 :ref:`variables`. 从Robot Framework 3.0版本开始, 默认值可以基于关键字先前接受的参数来定义. .. note:: 默认值的语法是对空格敏感的. 等号 ``=`` 前面不允许有空格, 而等号后面的空格则被视作默认值的一部分. .. sourcecode:: robotframework *** Keywords *** One Argument With Default Value [Arguments] ${arg}=default value [Documentation] This keyword takes 0-1 arguments Log Got argument ${arg} Two Arguments With Defaults [Arguments] ${arg1}=default 1 ${arg2}=${VARIABLE} [Documentation] This keyword takes 0-2 arguments Log 1st argument ${arg1} Log 2nd argument ${arg2} One Required And One With Default [Arguments] ${required} ${optional}=default [Documentation] This keyword takes 1-2 arguments Log Required: ${required} Log Optional: ${optional} Default Based On Earlier Argument [Arguments] ${a} ${b}=${a} ${c}=${a} and ${b} Should Be Equal ${a} ${b} Should Be Equal ${c} ${a} and ${b} 当一个关键字可接受多个带默认值的参数, 如果调用时仅需要覆盖其中一些, 则此时使用 :ref:`named arguments` 将会非常方便. 使用这种语法时, 指定值的参数名称要脱去 ``${}``. 例如, 上面第二个关键字可以按照下面的方法调用, 其中 ``${arg1}`` 仍然是其默认值. .. sourcecode:: robotframework *** Test Cases *** Example Two Arguments With Defaults arg2=new value Pythonistas肯定都已经注意到了, 这种指定默认值的语法几乎就是参照Python中函数的默认值语法实现的. .. _varargs with user keywords: 不定参数 ~~~~~~~~ 有时仅有默认值也不够, 关键字可能会需要接受 :ref:`variable number of arguments`. 用户关键字同样也支持这个特性, 只需在关键字的参数签名中使用 :ref:`list variable` 如 `@{varargs}` 作为参数声明. 同时不定数量的参数可以和默认值参数混用, 前面的参数先进行匹配, 列表参数最终接受所有剩余的参数, 数量可以是0个到任意个. .. sourcecode:: robotframework *** Keywords *** Any Number Of Arguments [Arguments] @{varargs} Log Many @{varargs} One Or More Arguments [Arguments] ${required} @{rest} Log Many ${required} @{rest} Required, Default, Varargs [Arguments] ${req} ${opt}=42 @{others} Log Required: ${req} Log Optional: ${opt} Log Others: : FOR ${item} IN @{others} \ Log ${item} 注意到上例中最后一个关键字如果使用多个参数调用, 则第2个参数总是会传给 ``${opt}`` 覆盖掉默认值. 最后的例子同时展示了如何在 :ref:`for loop` 中逐个获取任意数量的参数. 同样, Pythonistas会注意到这个语法和Python中的很相似. .. _kwargs with user keywords: 用户关键字的Kwargs ~~~~~~~~~~~~~~~~~~ 用户关键字同样可以接受 :ref:`free keyword arguments`, 只需在参数列表的最后, 在位置参数和任意数量参数的后面, 使用 :ref:`dictionary variable` 如 ``&{kwargs}``. 当该关键字被调用时, 前面没有被匹配的 :ref:`named arguments` 都会传递给该参数. .. sourcecode:: robotframework *** Keywords *** Kwargs Only [Arguments] &{kwargs} Log ${kwargs} Log Many @{kwargs} Positional And Kwargs [Arguments] ${required} &{extra} Log Many ${required} @{extra} Run Program [Arguments] @{varargs} &{kwargs} Run Process program.py @{varargs} &{kwargs} 上面最后一个例子展示了如何封装一个关键字, 可以接受任意的位置或命名参数, 并传递给被封装的关键字. 更详细的例子参见 :ref:`kwargs examples`. 这个语法同样和Python中的kwargs很相似. ``&{kwargs}`` 的用法和 ``**kwargs`` 基本雷同. .. _Embedded argument syntax: .. _Embedding arguments into keyword name: 关键字名称中嵌入参数 -------------------- Robot Framework 除了常规的在关键字名称后指定参数的方法外, 还支持一种把参数嵌入到关键字名称中的做法. 这样做的最大好处是可以使得关键字如同正常语言中的句子一样易读. 基础语法 ~~~~~~~~ 平常我们可以创建用户关键字如 :name:`Select dog from list` 和 :name:`Selects cat from list`, 不过这些关键字需要各自独立创建. 而在关键字名称中嵌入参数的想法是只需要实现一个关键字 :name:`Select ${animal} from list`. .. sourcecode:: robotframework *** Keywords *** Select ${animal} from list Open Page Pet Selection Select Item From List animal_list ${animal} 嵌入参数的关键字不可接受"普通的"参数(即 :setting:`[Arguments]` 设置), 其它创建方式则没什么两样. 当关键字被调用时, 其名称中的不同的值自然地传递给对应位置的参数. 例如, 上例中的 ``${animal}`` 在使用 :name:`Select dog from list` 时的值就是 ``dog``. 显然, 在关键字的内部这些参数不一定全部都得用上, 因此可以使用通配符. 这种类型的关键字和其它关键字的用法一样, 只是名称中的空格和下划线不能再忽略了. 不过大小写仍然是忽略的. 例如, 上例中的关键字可以是 :name:`select x from list`, 但是不能是 :name:`Select x fromlist`. 嵌入式参数不支持使用默认值和任意数量参数. 在调用这类关键字时也可以使用变量, 不过这样做会降低可读性. 另外请注意嵌入参数只适用于用户关键字. .. Notice also that embedded arguments only work with user keywords. .. _embedded arguments matching too much: 嵌入参数过多匹配 ~~~~~~~~~~~~~~~~ 使用嵌入参数的一个关键在于确保传入的值正确地匹配到参数. 特别在有多个参数, 并且参数值里面还有分隔字符存在时. 例如, 关键字 :name:`Select ${city} ${team}` 在城市名中包含多个部分时就会错误, 如 :name:`Select Los Angeles Lakers`. 一个简单的解决方法是把参数用引号括起来(例如 :name:`Select "${city}" "${team}"`), 然后调用时同样使用引号传参(例如 :name:`Select "Los Angeles" "Lakers"`). 强烈建议使用这种方式来调用, 尽管不能解决所有冲突, 但是它把参数和其它关键字区分了出来. 另一个更强大同时也更复杂的解决方案是使用 :ref:`using custom regular expressions` 来定义变量, 下节详细讨论. 此外, 如果这一切让事情变得非常复杂, 那么也许最好的方法是用回普通的位置型参数. 参数匹配过多的问题经常发生在为行为驱动用例创建 :ref:`忽略前缀 ` 的关键字的时候. 例如, :name:`${name} goes home` 匹配 :name:`Given Janne goes home` 得到值为 `Given Janne`. 使用引号可以轻松的解决这个问题, 例如 :name:`"${name}" goes home`. .. _Using custom regular expressions: 使用自定义正则表达式 ~~~~~~~~~~~~~~~~~~~~ 当调用嵌入参数的关键字时, 参数值在内部使用 :ref:`正则表达式 ` (简称 regexps)来进行匹配. 默认的逻辑是每个参数用 ``.*?`` 模式替代, 该模式基本上可以匹配任何字符串. 正常情况下, 这样就足以胜任. 不过, 如前面讨论的一样, 有时候关键字会 :ref:`匹配过多 `. 使用引号或其它分隔符有所帮助, 不过有时候情况比较复杂, 如下面的例子, 测试用例会执行失败, 因为关键字 :name:`I execute "ls" with "-lh"` 同时匹配两个已定义的关键字. .. sourcecode:: robotframework *** Test Cases *** Example I execute "ls" I execute "ls" with "-lh" *** Keywords *** I execute "${cmd}" Run Process ${cmd} shell=True I execute "${cmd}" with "${opts}" Run Process ${cmd} ${opts} shell=True 此时就可以使用自定义正则表达式来确保关键字只匹配到想要的确定的内容. 想要使用这个特性, 并且完全理解本节的例子, 你至少需要对正则表达式的基础语法有所了解. 自定义正则表达式的定义跟在参数的名称后面, 两者之间使用一个冒号(``:``)隔开. 例如, 一个只应该匹配数字的参数应该定义为 ``${arg:\d+}``. 请看下面的例子: .. sourcecode:: robotframework *** Test Cases *** Example I execute "ls" I execute "ls" with "-lh" I type 1 + 2 I type 53 - 11 Today is 2011-06-27 *** Keywords *** I execute "${cmd:[^"]+}" Run Process ${cmd} shell=True I execute "${cmd}" with "${opts}" Run Process ${cmd} ${opts} shell=True I type ${a:\d+} ${operator:[+-]} ${b:\d+} Calculate ${a} ${operator} ${b} Today is ${date:\d{4\}-\d{2\}-\d{2\}} Log ${date} 上例中, 关键字 :name:`I execute "ls" with "-lh"` 仅匹配 :name:`I execute "${cmd}" with "${opts}"`. 这是由 :name:`I execute "${cmd:[^"]}"` 其中的正则表达式 ``[^"]+`` 所保证的, 这个正则表达式意思是该参数不能包含任何引号. 在这个例子中, 对另外那个关键字 :name:`I execute` 来说, 已经不必要再添加正则表达式. .. tip:: 如果使用了引号, 使用正则表达式 ``[^"]+`` 以确保参数的右引号匹配正确. .. _Supported regular expression syntax: 支持的正则表达式语法 '''''''''''''''''''' 因为Robot Framework是使用Python开发的, 所以其正则表达式语法很自然地是使用 :name:`re` 模块中定义的 :ref:`语法 `. 除了不能使用 ``(?...)`` 格式, 其它语法都可被支持. 注意嵌入参数的匹配是忽略大小写的. 如果正则表达式的语法非法, 则该关键字会创建失败, 并且在 :ref:`测试执行错误区 ` 中显示错误. .. _python re: https://docs.python.org/2/library/re.html .. _Escaping special characters: 转义特殊字符 '''''''''''' 当使用嵌入参数regexp时, 某些特殊字符需要被转义才能使用. 首先, 正则模式中的右花括号 (``}``) 需要使用反斜杠转义(``\}``), 否则该参数会提前结束. 例如, 在上面例子中的关键字 :name:`Today is ${date:\\d{4\\}-\\d{2\\}-\\d{2\\}}`. 反斜杠 (``\``) 作为Python正则表达式语法中的特殊字符, 如果你想要表示字面的反斜杠字符, 也需要进行转义. 这种情况最安全的做法是使用4个反斜杠序列(``\\\\``), 不过根据其后字符的不同, 有的情况两个反斜杠也已经足够. 注意, 按照正常的 :ref:`测试数据转义规则 `, 关键字的名称和可能嵌入其中的参数名 **不需要** 被转义. 这意味着表达式 ``${name:\w+}`` 中的反斜杠就不需要被转义. .. Notice also that keyword names and possible embedded arguments in them .. should *not* be escaped using the normal `test data escaping .. rules`__. This means that, for example, backslashes in expressions .. like `${name:\w+}` should not be escaped. .. _Using variables with custom embedded argument regular expressions: 在嵌入参数正则表达式中使用变量 ''''''''''''''''''''''''''''' 不论何时使用到嵌入参数regexp, Robot Framework自动增强该正则表达式, 使得变量可以被匹配到. 这意味着总是可以在嵌入参数regexp的关键字中使用变量. 例如, 下面的用例使用到前面例子中的关键字, 测试结果为通过. .. sourcecode:: robotframework *** Variables *** ${DATE} 2011-06-27 *** Test Cases *** Example I type ${1} + ${2} Today is ${DATE} 自动匹配变量的一个缺陷是变量的值有可能实际上并不真正匹配该特定的正则表达式. 例如, 上例中的 ``${DATE}`` 可以包含任意值, 与此同时, :name:`Today is ${DATE}` 将仍将会匹配到相同的关键字 .. _Behavior-driven development example: 行为驱动开发示例 ~~~~~~~~~~~~~~~~ 把参数嵌入到关键字名称中最大的好处是可以使高层关键字更像是一句话, 从而写出 :ref:`行为驱动样式` 的测试用例. 详见下面的例子. 注意, 其中的前缀 :name:`Given`, :name:`When` 和 :name:`Then` :ref:`并不是关键字名称的一部分 `. .. sourcecode:: robotframework *** Test Cases *** Add two numbers Given I have Calculator open When I add 2 and 40 Then result should be 42 Add negative numbers Given I have Calculator open When I add 1 and -2 Then result should be -1 *** Keywords *** I have ${program} open Start Program ${program} I add ${number 1} and ${number 2} Input Number ${number 1} Push Button + Input Number ${number 2} Push Button = Result should be ${expected} ${result} = Get Result Should Be Equal ${result} ${expected} .. note:: Embedded arguments feature in Robot Framework is inspired by how *step definitions* are created in a popular BDD tool :ref:`Cucumber`. .. _Cucumber: http://cukes.info .. _user keyword return values: 用户关键字返回值 ---------------- 和库关键字类似, 用户关键字也可以返回值. 常见的做法是通过 :setting:`[Return]` 设置, 不过还可以使用 BuiltIn_ 关键字 :name:`Return From Keyword` 和 :name:`Return From Keyword If` 来实现. 不管使用何种方式返回值, 返回值都可以被 :ref:`赋值给变量 ` .. Using :setting:`[Return]` setting 设置 :setting:`[Return]` ~~~~~~~~~~~~~~~~~~~~~~~~~ 最常见的情况是用户关键字返回一个值, 并且赋值给一个标量变量. 直接将返回值放在 :setting:`[Return]` 设置后面的单元格内. 用户关键字还可以返回多个值, 这些值可以一次性赋给多个标量, 或者一个列表变量, 或者两者混合. 多个值只需依次跟在 :setting:`[Return]` 后面的单元格中即可. .. sourcecode:: robotframework *** Test Cases *** One Return Value ${ret} = Return One Value argument Some Keyword ${ret} Multiple Values ${a} ${b} ${c} = Return Three Values @{list} = Return Three Values ${scalar} @{rest} = Return Three Values *** Keywords *** Return One Value [Arguments] ${arg} Do Something ${arg} ${value} = Get Some Value [Return] ${value} Return Three Values [Return] foo bar zap .. _Using special keywords to return: 通过特殊关键字来返回值 ~~~~~~~~~~~~~~~~~~~~~~ 内置关键字 :name:`Return From Keyword` 和 :name:`Return From Keyword If` 可以在用户关键字中间根据条件来返回值. 这两个关键字都支持返回多个值. 下面的第一个例子在功能上和前面使用 :setting:`[Return]` 的例子一样. 第二个例子则更高级点, 演示了如何在 :ref:`for loop` 中根据条件来返回值. .. sourcecode:: robotframework *** Test Cases *** One Return Value ${ret} = Return One Value argument Some Keyword ${ret} Advanced @{list} = Create List foo baz ${index} = Find Index baz @{list} Should Be Equal ${index} ${1} ${index} = Find Index non existing @{list} Should Be Equal ${index} ${-1} *** Keywords *** Return One Value [Arguments] ${arg} Do Something ${arg} ${value} = Get Some Value Return From Keyword ${value} Fail This is not executed Find Index [Arguments] ${element} @{items} ${index} = Set Variable ${0} :FOR ${item} IN @{items} \ Return From Keyword If '${item}' == '${element}' ${index} \ ${index} = Set Variable ${index + 1} Return From Keyword ${-1} # Could also use [Return] .. note:: :name:`Return From Keyword` 和 :name:`Return From Keyword If` 这两个关键字在 Robot Framework 2.8 版本后才支持. .. _User keyword teardown: 用户关键字的Teardown --------------------- 用户关键字可以通过设置 :setting:`[Teardown]` 定义一个Teardown操作. 关键字的Teardown和 :ref:`测试用例的Teardown ` 的作用一样. 记住, teardown 总是一个单独的关键字(可以是另一个用户关键字), teardown在当前关键字执行失败时也会被调用. 此外, teardown内的所有步骤都会执行到, 即使其中某个步骤失败. 不过, teardown的失败会导致当前用例执行失败, 并且该用例余下的步骤将不再执行. 此外, 可以使用变量来给定teardown的关键字名称. .. sourcecode:: robotframework *** Keywords *** With Teardown Do Something [Teardown] Log keyword teardown Using variables [Documentation] Teardown given as variable Do Something [Teardown] ${TEARDOWN}