创建静态关键字
充当关键字的方法
当使用静态库API时, Robot Framework 利用反射机制获得类或模块实现的公有方法(public methods). 所有以下划线(_)开始的方法被排除, 在Java库里, 只在 java.lang.Object
里实现的方法也被忽略. 所有没被忽略的方法都被视为关键字.
例如, 下面的Python和Java示例实现了关键字 My Keyword.
- class MyLibrary:
- def my_keyword(self, arg):
- return self._helper_method(arg)
- def _helper_method(self, arg):
- return arg.upper()
- public class MyLibrary {
- public String myKeyword(String arg) {
- return helperMethod(arg);
- }
- private String helperMethod(String arg) {
- return arg.toUpperCase();
- }
- }
当库是Python模块实现的, 可以使用Python的 all
属性来限制到底哪些方法是关键字. 如果使用了 all
, 只有列在其中的才会被当作关键字, 例如下面的例子中, 实现了关键字 Example Keyword 和Second Example. 如果这个例子中没有 all, 那么其中的Not Exposed As Keyword 和 Current Thread 也会被视作关键字. all 最重要的作用就是确保那些import进来的帮助方法,如本例中的 current_thread, 不会被意外地暴露为关键字
- from threading import current_thread
- __all__ = ['example_keyword', 'second_example']
- def example_keyword():
- if current_thread().name == 'MainThread':
- print 'Running in main thread'
- def second_example():
- pass
- def not_exposed_as_keyword():
- pass
关键字名称
测试数据(test data)中使用的关键字名称, 与方法名称对比, 最终确定是哪个方法实现了这个关键字. 名称的比较是忽略大小写的, 并且其中的空格和下划线也都忽略掉. 例如, 方法名 hello
最终可以映射为的关键字名称可以是:Hello, hello 甚至 h e l l o. 反过来也类似,例如, donothing
和 doNothing
这两个方法都可被当作 _Do Nothing关键字的实现.
Python模块实现的测试库示例如下, MyLibrary.py
:
- def hello(name):
- print "Hello, %s!" % name
- def do_nothing():
- pass
Java类实现的测试库示例如下, MyLibrary.java
file:
- public class MyLibrary {
- public void hello(String name) {
- System.out.println("Hello, " + name + "!");
- }
- public void doNothing() {
- }
- }
下面的例子用来说明如何使用上面的测试库. 如果你想自己试一下, 首先要确保库的位置在 模块搜索路径.
- *** Settings ***
- Library MyLibrary
- *** Test Cases ***
- My Test
- Do Nothing
- Hello world
使用自定义的关键字名称
如果一个方法不想使用默认映射的关键字名称, 也可以明确指定为自定义的关键字名称.这是通过设置方法的 robot_name
属性来实现的. 可以使用装饰器方法
robot.api.deco.keyword
方便快捷的设置. 例如:
- from robot.api.deco import keyword
- @keyword('Login Via User Panel')
- def login(username, password):
- # ...
- *** Test Cases ***
- My Test
- Login Via User Panel ${username} ${password}
如果使用装饰器时不带任何参数, 则这个装饰器不会改变关键字名称, 但是仍然会创建 robot_name
属性. 这种情况对 标记方法为关键字 , 同时又不改变关键字名称的时候很有用.
设置自定义的关键字名称还使得库关键字可以接受使用 嵌入参数 语法的参数.
关键字标签
从 Robot Framework 2.9 版本开始, 库关键字和 用户关键字 都可以有标签.
库关键字通过设置方法的 robot_tags
属性实现, 该属性的值是要设置的标签的列表.装饰器 robot.api.deco.keyword
可以按如下的方法来方便的指定这个属性:
- from robot.api.deco import keyword
- @keyword(tags=['tag1', 'tag2'])
- def login(username, password):
- # ...
- @keyword('Custom name', ['tags', 'here'])
- def another_example():
- # ...
设置标签的另一个方法是在 关键字文档 的最后一行给出, 以 Tags:
作为前缀开始, 后面跟着按逗号分开的标签. 例如:
- def login(username, password):
- """Log user in to SUT.
- Tags: tag1, tag2
- """
- # ...
关键字参数
对于静态和混合API, 关于一个关键字的参数表信息是直接从实现它的方法上获取的.而使用了 动态库API 的库则使用其它的方式来传递这些信息, 所以本章不适用于动态库.
最常见也是最简单的情况是关键字需要确定数目的参数. 在这种情况下, Python和Java方法简单地接受这些参数即可. 例如, 没有参数的方法对应的关键字也不需要参数, 只需一个参数的方法对应的关键字也只需要一个参数, 以此类推.
下面Python关键字示例接受不同数量的参数:
- def no_arguments():
- print "Keyword got no arguments."
- def one_argument(arg):
- print "Keyword got one argument '%s'." % arg
- def three_arguments(a1, a2, a3):
- print "Keyword got three arguments '%s', '%s' and '%s'." % (a1, a2, a3)
注解
使用静态库API实现的Java库有一个很大的限制, 即不支持 命名参数. 如果你觉得这是一个障碍, 那要么改使用Python来实现, 要么切换到 动态库API
关键字的缺省值
和函数类似, 关键字的有些参数有时需要有缺省值. Python 和 Java 对于处理方法的缺省值使用不同的语法,
Python中的缺省值
Python中, 方法总是只有一个实现, 在方法的签名中可能指定若干缺省值.这种语法对Python程序员来说应该非常熟悉, 例如:
- def one_default(arg='default'):
- print "Argument has value %s" % arg
- def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
- print "Got arguments %s, %s and %s" % (arg1, arg2, arg3)
上例中的第一个关键字, 可以接受 0 个或 1 个参数, 当 0 个参数时, 参数 arg
使用缺省值 default
; 当有 1 个参数时, 参数 arg
就使用这个传入的值;而如果参数个数大于 1 , 则调用该关键字会报错失败.
第二个关键字中, 第1个参数总是需要指定的, 但是 第2和第3个都有缺省值, 所以,使用该关键字可以传入 1 至 3 个参数.
- *** Test Cases ***
- Defaults
- One Default
- One Default argument
- Multiple Defaults required arg
- Multiple Defaults required arg optional
- Multiple Defaults required arg optional 1 optional 2
Java中的缺省值
Java中, 一个方法可以有多个实现, 分别是不同的签名(重载). Robot Framework 将所有这些实现都视作一个关键字, 这个关键字可以接受不同的参数, 以此实现了缺省值的支持. 下面的例子在功能上和上面的Python例子完全一样:
- public void oneDefault(String arg) {
- System.out.println("Argument has value " + arg);
- }
- public void oneDefault() {
- oneDefault("default");
- }
- public void multipleDefaults(String arg1, String arg2, String arg3) {
- System.out.println("Got arguments " + arg1 + ", " + arg2 + " and " + arg3);
- }
- public void multipleDefaults(String arg1, String arg2) {
- multipleDefaults(arg1, arg2, "default 2");
- }
- public void multipleDefaults(String arg1) {
- multipleDefaults(arg1, "default 1");
- }
可变数量的参数
Robot Framework 的关键字还支持接受任何数量的参数. 类似于缺省值,实际的语法在Python和Java中有所差异.
Python中的可变数量的参数
Python的语法本身就支持让方法可以接受任意数量的参数. 相同的语法同样作用于测试库, 同时, 还可以与指定缺省值结合, 如下面的例子:
- def any_arguments(*args):
- print "Got arguments:"
- for arg in args:
- print arg
- def one_required(required, *others):
- print "Required: %s\nOthers:" % required
- for arg in others:
- print arg
- def also_defaults(req, def1="default 1", def2="default 2", *rest):
- print req, def1, def2, rest
- *** Test Cases ***
- Varargs
- Any Arguments
- Any Arguments argument
- Any Arguments arg 1 arg 2 arg 3 arg 4 arg 5
- One Required required arg
- One Required required arg another arg yet another
- Also Defaults required
- Also Defaults required these two have defaults
- Also Defaults 1 2 3 4 5 6
Java中的可变数量的参数
Robot Framework 支持 Java可变数量参数的语法. 下面的例子和上面Python的例子在功能上是一样的:
- public void anyArguments(String... varargs) {
- System.out.println("Got arguments:");
- for (String arg: varargs) {
- System.out.println(arg);
- }
- }
- public void oneRequired(String required, String... others) {
- System.out.println("Required: " + required + "\nOthers:");
- for (String arg: others) {
- System.out.println(arg);
- }
- }
Robot Framework 从 2.8.3 版本开始, 还支持另一种方式来实现可变数量参数, 即使用数组或者 java.util.List
作为最后一个参数, 或倒数第二个参数(如果最后一个参数是
- public void anyArguments(String[] varargs) {
- System.out.println("Got arguments:");
- for (String arg: varargs) {
- System.out.println(arg);
- }
- }
- public void oneRequired(String required, List<String> others) {
- System.out.println("Required: " + required + "\nOthers:");
- for (String arg: others) {
- System.out.println(arg);
- }
- }
注解
只有 java.util.List 支持作为 varargs, 它的任何子类型都不可以.
对于Java关键字来说, 支持可变数量的参数有一个限制: 只在方法只有一个签名时有效.也就是说, Java实现的关键字不可能既使用缺省值又使用varargs. 而且, 只有 2.8 或更新版本的 Robot Framework 支持在 库的构造器 中使用varargs.
任意关键字参数
Robot Framework 2.8版本增加了任意关键字参数, 即Python中的 **kwargs
语法.如何在测试用例中使用这种语法的讨论在 创建测试用例 章节下的 任意关键字参数 小节中.
本章我们来看一下如何在测试库中使用它.
Robot Framework 2.8 added the support for free keyword arguments using Python’s**kwargs syntax. How to use the syntax in the test data is discussedin Free keyword arguments section under Creating test cases
_. In thissection we take a look at how to actually use it in custom test libraries.
Python中的任意关键字参数
如果你对Python中的 kwargs 如何工作比较熟悉, 那么理解Robot Framework中的测试库是如何实现的就非常简单了. 下面的例子展示了最基础的功能:
- def example_keyword(**stuff):
- for name, value in stuff.items():
- print name, value
- *** Test Cases ***
- Keyword Arguments
- Example Keyword hello=world # Logs 'hello world'.
- Example Keyword foo=1 bar=42 # Logs 'foo 1' and 'bar 42'.
基本上, 所有以 命名参数 name=value
形式跟在关键字调用最后面, 且不匹配其它任何参数的参数, 将以 kwargs 传入给关键字.如果想要避免一个字面字符串被当作任意关键字参数, 则其中的等号 =
必须被 转义, 例如 foo=quux
要写作 foo\=quux
.
下面的例子展示了综合使用普通参数, 可变数量参数(varargs), 和任意关键字参数(kwargs)的情况:
- def various_args(arg, *varargs, **kwargs):
- print 'arg:', arg
- for value in varargs:
- print 'vararg:', value
- for name, value in sorted(kwargs.items()):
- print 'kwarg:', name, value
- *** Test Cases ***
- Positional
- Various Args hello world # Logs 'arg: hello' and 'vararg: world'.
- Named
- Various Args arg=value # Logs 'arg: value'.
- Kwargs
- Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
- Various Args c=3 a=1 b=2 # Same as above. Order does not matter.
- Positional and kwargs
- Various Args 1 2 kw=3 # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.
- Named and kwargs
- Various Args arg=value hello=world # Logs 'arg: value' and 'kwarg: hello world'.
- Various Args hello=world arg=value # Same as above. Order does not matter.
要查看真实测试库中相同示例, 请参考 Process_ 库中的关键字 Run Process 和 Start Keyword.
For a real world example of using a signature exactly like in the aboveexample, see Run Process and Start Keyword keywords in theProcess_ library.
Java中的任意关键字参数
从Robot Framework 2.8.3版本开始, Java测试库也开始支持这种语法. Java语言本身是不支持kwargs语法的, 但是关键字可以利用 java.util.Map
类型作为最后一个参数, 来接受 kwargs.
如果一个Java关键字接受 kwargs, Robot Framework 会自动将关键字调用的末尾所有形如 name=value
的参数打包放入一个 Map
, 然后传递给关键字方法. 例如, 下面的例子中的关键字使用起来和前面的Python示例完全一样:
- public void exampleKeyword(Map<String, String> stuff):
- for (String key: stuff.keySet())
- System.out.println(key + " " + stuff.get(key));
- public void variousArgs(String arg, List<String> varargs, Map<String, Object> kwargs):
- System.out.println("arg: " + arg);
- for (String varg: varargs)
- System.out.println("vararg: " + varg);
- for (String key: kwargs.keySet())
- System.out.println("kwarg: " + key + " " + kwargs.get(key));
注解
kwargs 参数的类型必须是 java.util.Map, 而不是其子类.
注解
和 Java中的varargs 一样, kwargs的关键字也只能有一个方法签名.
参数类型
正常情况下, 关键字的参数以字符串的形式传递给 Robot Framework. 如果关键字需要其它的类型, 可以使用 变量 或者在关键字的内部将字符串转换为所需的类型.使用 Java关键字, 基础类型会自动的强制转换.
Python中的参数类型
因为Python的参数并没有任何的类型信息, 所以使用Python的库时不可能自动的将字符串转换为其它类型. 调用Python方法实现的关键字, 只要参数的数量正确, 调用就总是能够成功, 只不过如果参数不兼容, 后面的执行会失败. 幸运地是, 在Python中转换参数类型是很简单的事情:
- def connect_to_host(address, port=25):
- port = int(port)
- # ...
Java中的参数类型
Java方法的参数都有类型, 而且所有基础类型会自动处理. 这意味着, test data 中的字符串类型的参数, 在运行时刻强制转换为正确的类型. 可以转换的类型有:
- 整数型 (
byte
,short
,int
,long
) - 浮点数 (
float
和double
) - 布尔型 (
boolean
) - 上述类型的对象版本, 如.
java.lang.Integer
Java的关键字方法可能会有多个签名, 强制转换只有在有相同的或兼容的签名才会发生. 下面的例子中, 关键字 doubleArgument
和 compatibleTypes
可以强制转换, 但是 conflictingTypes
会发生冲突.
- public void doubleArgument(double arg) {}
- public void compatibleTypes(String arg1, Integer arg2) {}
- public void compatibleTypes(String arg2, Integer arg2, Boolean arg3) {}
- public void conflictingTypes(String arg1, int arg2) {}
- public void conflictingTypes(int arg1, String arg2) {}
对于数值型的类型, 如果测试数据中的字符串包含数字, 就可以强制转换, 对于布尔型, 则必须包含字符串 true
或者 false
.
强制转换只在测试数据的原始值是字符串的情况下才会发生, 当然还可以使用包含了正确数据类型的变量. 要应对冲突的方法签名, 使用变量是唯一的选择.
- *** Test Cases ***
- Coercion
- Double Argument 3.14
- Double Argument 2e16
- Compatible Types Hello, world! 1234
- Compatible Types Hi again! -10 true
- No Coercion
- Double Argument ${3.14}
- Conflicting Types 1 ${2} # must use variables
- Conflicting Types ${1} 2
从 Robot Framework 2.8 版本开始, 参数类型的强制转换在 Java库的构造函数 中也起作用.
使用装饰器
当编写静态关键字时, 有时候使用Python的装饰器修改它们会很方便. 但是, 装饰器修改了函数的签名, 这会让 Robot Framework 在判断关键字能接受什么参数时产生混乱. 特别是用 Libdoc_ 创建库文档和使用 RIDE_ 时. 为了避免这种情况, 要么就不要用装饰器, 要么使用方便的 装饰器模块 创建保留签名的装饰器.
提示
译注: 上面的链接貌似已经失效.
关键字中嵌入参数
库关键字还能接受使用 嵌入参数语法 传递的参数. 可以使用装饰器 robot.api.deco.keyword
来创建 自定义关键字名称, 其中包括所需语法.
- from robot.api.deco import keyword
- @keyword('Add ${quantity:\d+} Copies Of ${item} To Cart')
- def add_copies_to_cart(quantity, item):
- # ...
- *** Test Cases ***
- My Test
- Add 7 Copies Of Coffee To Cart