创建静态关键字

充当关键字的方法

当使用静态库API时, Robot Framework 利用反射机制获得类或模块实现的公有方法(public methods). 所有以下划线(_)开始的方法被排除, 在Java库里, 只在 java.lang.Object 里实现的方法也被忽略. 所有没被忽略的方法都被视为关键字.

例如, 下面的Python和Java示例实现了关键字 My Keyword.

  1. class MyLibrary:
  2.  
  3. def my_keyword(self, arg):
  4. return self._helper_method(arg)
  5.  
  6. def _helper_method(self, arg):
  7. return arg.upper()
  1. public class MyLibrary {
  2.  
  3. public String myKeyword(String arg) {
  4. return helperMethod(arg);
  5. }
  6.  
  7. private String helperMethod(String arg) {
  8. return arg.toUpperCase();
  9. }
  10. }

当库是Python模块实现的, 可以使用Python的 all 属性来限制到底哪些方法是关键字. 如果使用了 all, 只有列在其中的才会被当作关键字, 例如下面的例子中, 实现了关键字 Example KeywordSecond Example. 如果这个例子中没有 all, 那么其中的Not Exposed As KeywordCurrent Thread 也会被视作关键字. all 最重要的作用就是确保那些import进来的帮助方法,如本例中的 current_thread, 不会被意外地暴露为关键字

  1. from threading import current_thread
  2.  
  3. __all__ = ['example_keyword', 'second_example']
  4.  
  5. def example_keyword():
  6. if current_thread().name == 'MainThread':
  7. print 'Running in main thread'
  8.  
  9. def second_example():
  10. pass
  11.  
  12. def not_exposed_as_keyword():
  13. pass

关键字名称

测试数据(test data)中使用的关键字名称, 与方法名称对比, 最终确定是哪个方法实现了这个关键字. 名称的比较是忽略大小写的, 并且其中的空格和下划线也都忽略掉. 例如, 方法名 hello 最终可以映射为的关键字名称可以是:Hello, hello 甚至 h e l l o. 反过来也类似,例如, donothingdoNothing 这两个方法都可被当作 _Do Nothing关键字的实现.

Python模块实现的测试库示例如下, MyLibrary.py:

  1. def hello(name):
  2. print "Hello, %s!" % name
  3.  
  4. def do_nothing():
  5. pass

Java类实现的测试库示例如下, MyLibrary.java file:

  1. public class MyLibrary {
  2.  
  3. public void hello(String name) {
  4. System.out.println("Hello, " + name + "!");
  5. }
  6.  
  7. public void doNothing() {
  8. }
  9.  
  10. }

下面的例子用来说明如何使用上面的测试库. 如果你想自己试一下, 首先要确保库的位置在 模块搜索路径.

  1. *** Settings ***
  2. Library MyLibrary
  3.  
  4. *** Test Cases ***
  5. My Test
  6. Do Nothing
  7. Hello world

使用自定义的关键字名称

如果一个方法不想使用默认映射的关键字名称, 也可以明确指定为自定义的关键字名称.这是通过设置方法的 robot_name 属性来实现的. 可以使用装饰器方法

robot.api.deco.keyword 方便快捷的设置. 例如:
  1. from robot.api.deco import keyword
  2.  
  3. @keyword('Login Via User Panel')
  4. def login(username, password):
  5. # ...
  1. *** Test Cases ***
  2. My Test
  3. Login Via User Panel ${username} ${password}

如果使用装饰器时不带任何参数, 则这个装饰器不会改变关键字名称, 但是仍然会创建 robot_name 属性. 这种情况对 标记方法为关键字 , 同时又不改变关键字名称的时候很有用.

设置自定义的关键字名称还使得库关键字可以接受使用 嵌入参数 语法的参数.

关键字标签

从 Robot Framework 2.9 版本开始, 库关键字和 用户关键字 都可以有标签.

库关键字通过设置方法的 robot_tags 属性实现, 该属性的值是要设置的标签的列表.装饰器 robot.api.deco.keyword 可以按如下的方法来方便的指定这个属性:

  1. from robot.api.deco import keyword
  2.  
  3. @keyword(tags=['tag1', 'tag2'])
  4. def login(username, password):
  5. # ...
  6.  
  7. @keyword('Custom name', ['tags', 'here'])
  8. def another_example():
  9. # ...

设置标签的另一个方法是在 关键字文档 的最后一行给出, 以 Tags: 作为前缀开始, 后面跟着按逗号分开的标签. 例如:

  1. def login(username, password):
  2. """Log user in to SUT.
  3.  
  4. Tags: tag1, tag2
  5. """
  6. # ...

关键字参数

对于静态和混合API, 关于一个关键字的参数表信息是直接从实现它的方法上获取的.而使用了 动态库API 的库则使用其它的方式来传递这些信息, 所以本章不适用于动态库.

最常见也是最简单的情况是关键字需要确定数目的参数. 在这种情况下, Python和Java方法简单地接受这些参数即可. 例如, 没有参数的方法对应的关键字也不需要参数, 只需一个参数的方法对应的关键字也只需要一个参数, 以此类推.

下面Python关键字示例接受不同数量的参数:

  1. def no_arguments():
  2. print "Keyword got no arguments."
  3.  
  4. def one_argument(arg):
  5. print "Keyword got one argument '%s'." % arg
  6.  
  7. def three_arguments(a1, a2, a3):
  8. print "Keyword got three arguments '%s', '%s' and '%s'." % (a1, a2, a3)

注解

使用静态库API实现的Java库有一个很大的限制, 即不支持 命名参数. 如果你觉得这是一个障碍, 那要么改使用Python来实现, 要么切换到 动态库API

关键字的缺省值

和函数类似, 关键字的有些参数有时需要有缺省值. Python 和 Java 对于处理方法的缺省值使用不同的语法,

Python中的缺省值

Python中, 方法总是只有一个实现, 在方法的签名中可能指定若干缺省值.这种语法对Python程序员来说应该非常熟悉, 例如:

  1. def one_default(arg='default'):
  2. print "Argument has value %s" % arg
  3.  
  4. def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
  5. print "Got arguments %s, %s and %s" % (arg1, arg2, arg3)

上例中的第一个关键字, 可以接受 0 个或 1 个参数, 当 0 个参数时, 参数 arg使用缺省值 default; 当有 1 个参数时, 参数 arg 就使用这个传入的值;而如果参数个数大于 1 , 则调用该关键字会报错失败.

第二个关键字中, 第1个参数总是需要指定的, 但是 第2和第3个都有缺省值, 所以,使用该关键字可以传入 1 至 3 个参数.

  1. *** Test Cases ***
  2. Defaults
  3. One Default
  4. One Default argument
  5. Multiple Defaults required arg
  6. Multiple Defaults required arg optional
  7. Multiple Defaults required arg optional 1 optional 2

Java中的缺省值

Java中, 一个方法可以有多个实现, 分别是不同的签名(重载). Robot Framework 将所有这些实现都视作一个关键字, 这个关键字可以接受不同的参数, 以此实现了缺省值的支持. 下面的例子在功能上和上面的Python例子完全一样:

  1. public void oneDefault(String arg) {
  2. System.out.println("Argument has value " + arg);
  3. }
  4.  
  5. public void oneDefault() {
  6. oneDefault("default");
  7. }
  8.  
  9. public void multipleDefaults(String arg1, String arg2, String arg3) {
  10. System.out.println("Got arguments " + arg1 + ", " + arg2 + " and " + arg3);
  11. }
  12.  
  13. public void multipleDefaults(String arg1, String arg2) {
  14. multipleDefaults(arg1, arg2, "default 2");
  15. }
  16.  
  17. public void multipleDefaults(String arg1) {
  18. multipleDefaults(arg1, "default 1");
  19. }

可变数量的参数

Robot Framework 的关键字还支持接受任何数量的参数. 类似于缺省值,实际的语法在Python和Java中有所差异.

Python中的可变数量的参数

Python的语法本身就支持让方法可以接受任意数量的参数. 相同的语法同样作用于测试库, 同时, 还可以与指定缺省值结合, 如下面的例子:

  1. def any_arguments(*args):
  2. print "Got arguments:"
  3. for arg in args:
  4. print arg
  5.  
  6. def one_required(required, *others):
  7. print "Required: %s\nOthers:" % required
  8. for arg in others:
  9. print arg
  10.  
  11. def also_defaults(req, def1="default 1", def2="default 2", *rest):
  12. print req, def1, def2, rest
  1. *** Test Cases ***
  2. Varargs
  3. Any Arguments
  4. Any Arguments argument
  5. Any Arguments arg 1 arg 2 arg 3 arg 4 arg 5
  6. One Required required arg
  7. One Required required arg another arg yet another
  8. Also Defaults required
  9. Also Defaults required these two have defaults
  10. Also Defaults 1 2 3 4 5 6

Java中的可变数量的参数

Robot Framework 支持 Java可变数量参数的语法. 下面的例子和上面Python的例子在功能上是一样的:

  1. public void anyArguments(String... varargs) {
  2. System.out.println("Got arguments:");
  3. for (String arg: varargs) {
  4. System.out.println(arg);
  5. }
  6. }
  7.  
  8. public void oneRequired(String required, String... others) {
  9. System.out.println("Required: " + required + "\nOthers:");
  10. for (String arg: others) {
  11. System.out.println(arg);
  12. }
  13. }

Robot Framework 从 2.8.3 版本开始, 还支持另一种方式来实现可变数量参数, 即使用数组或者 java.util.List 作为最后一个参数, 或倒数第二个参数(如果最后一个参数是

任意关键字参数 **kwargs). 例如, 下面的示例和上面的功能是相同的:
  1. public void anyArguments(String[] varargs) {
  2. System.out.println("Got arguments:");
  3. for (String arg: varargs) {
  4. System.out.println(arg);
  5. }
  6. }
  7.  
  8. public void oneRequired(String required, List<String> others) {
  9. System.out.println("Required: " + required + "\nOthers:");
  10. for (String arg: others) {
  11. System.out.println(arg);
  12. }
  13. }

注解

只有 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中的测试库是如何实现的就非常简单了. 下面的例子展示了最基础的功能:

  1. def example_keyword(**stuff):
  2. for name, value in stuff.items():
  3. print name, value
  1. *** Test Cases ***
  2. Keyword Arguments
  3. Example Keyword hello=world # Logs 'hello world'.
  4. Example Keyword foo=1 bar=42 # Logs 'foo 1' and 'bar 42'.

基本上, 所有以 命名参数 name=value 形式跟在关键字调用最后面, 且不匹配其它任何参数的参数, 将以 kwargs 传入给关键字.如果想要避免一个字面字符串被当作任意关键字参数, 则其中的等号 = 必须被 转义, 例如 foo=quux 要写作 foo\=quux.

下面的例子展示了综合使用普通参数, 可变数量参数(varargs), 和任意关键字参数(kwargs)的情况:

  1. def various_args(arg, *varargs, **kwargs):
  2. print 'arg:', arg
  3. for value in varargs:
  4. print 'vararg:', value
  5. for name, value in sorted(kwargs.items()):
  6. print 'kwarg:', name, value
  1. *** Test Cases ***
  2. Positional
  3. Various Args hello world # Logs 'arg: hello' and 'vararg: world'.
  4.  
  5. Named
  6. Various Args arg=value # Logs 'arg: value'.
  7.  
  8. Kwargs
  9. Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
  10. Various Args c=3 a=1 b=2 # Same as above. Order does not matter.
  11.  
  12. Positional and kwargs
  13. Various Args 1 2 kw=3 # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.
  14.  
  15. Named and kwargs
  16. Various Args arg=value hello=world # Logs 'arg: value' and 'kwarg: hello world'.
  17. Various Args hello=world arg=value # Same as above. Order does not matter.

要查看真实测试库中相同示例, 请参考 Process_ 库中的关键字 Run ProcessStart 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示例完全一样:

  1. public void exampleKeyword(Map<String, String> stuff):
  2. for (String key: stuff.keySet())
  3. System.out.println(key + " " + stuff.get(key));
  4.  
  5. public void variousArgs(String arg, List<String> varargs, Map<String, Object> kwargs):
  6. System.out.println("arg: " + arg);
  7. for (String varg: varargs)
  8. System.out.println("vararg: " + varg);
  9. for (String key: kwargs.keySet())
  10. System.out.println("kwarg: " + key + " " + kwargs.get(key));

注解

kwargs 参数的类型必须是 java.util.Map, 而不是其子类.

注解

Java中的varargs 一样, kwargs的关键字也只能有一个方法签名.

参数类型

正常情况下, 关键字的参数以字符串的形式传递给 Robot Framework. 如果关键字需要其它的类型, 可以使用 变量 或者在关键字的内部将字符串转换为所需的类型.使用 Java关键字, 基础类型会自动的强制转换.

Python中的参数类型

因为Python的参数并没有任何的类型信息, 所以使用Python的库时不可能自动的将字符串转换为其它类型. 调用Python方法实现的关键字, 只要参数的数量正确, 调用就总是能够成功, 只不过如果参数不兼容, 后面的执行会失败. 幸运地是, 在Python中转换参数类型是很简单的事情:

  1. def connect_to_host(address, port=25):
  2. port = int(port)
  3. # ...

Java中的参数类型

Java方法的参数都有类型, 而且所有基础类型会自动处理. 这意味着, test data 中的字符串类型的参数, 在运行时刻强制转换为正确的类型. 可以转换的类型有:

  • 整数型 (byte, short, int, long)
  • 浮点数 (floatdouble)
  • 布尔型 (boolean)
  • 上述类型的对象版本, 如. java.lang.Integer

Java的关键字方法可能会有多个签名, 强制转换只有在有相同的或兼容的签名才会发生. 下面的例子中, 关键字 doubleArgumentcompatibleTypes 可以强制转换, 但是 conflictingTypes 会发生冲突.

  1. public void doubleArgument(double arg) {}
  2.  
  3. public void compatibleTypes(String arg1, Integer arg2) {}
  4. public void compatibleTypes(String arg2, Integer arg2, Boolean arg3) {}
  5.  
  6. public void conflictingTypes(String arg1, int arg2) {}
  7. public void conflictingTypes(int arg1, String arg2) {}

对于数值型的类型, 如果测试数据中的字符串包含数字, 就可以强制转换, 对于布尔型, 则必须包含字符串 true 或者 false.

强制转换只在测试数据的原始值是字符串的情况下才会发生, 当然还可以使用包含了正确数据类型的变量. 要应对冲突的方法签名, 使用变量是唯一的选择.

  1. *** Test Cases ***
  2. Coercion
  3. Double Argument 3.14
  4. Double Argument 2e16
  5. Compatible Types Hello, world! 1234
  6. Compatible Types Hi again! -10 true
  7.  
  8. No Coercion
  9. Double Argument ${3.14}
  10. Conflicting Types 1 ${2} # must use variables
  11. Conflicting Types ${1} 2

从 Robot Framework 2.8 版本开始, 参数类型的强制转换在 Java库的构造函数 中也起作用.

使用装饰器

当编写静态关键字时, 有时候使用Python的装饰器修改它们会很方便. 但是, 装饰器修改了函数的签名, 这会让 Robot Framework 在判断关键字能接受什么参数时产生混乱. 特别是用 Libdoc_ 创建库文档和使用 RIDE_ 时. 为了避免这种情况, 要么就不要用装饰器, 要么使用方便的 装饰器模块 创建保留签名的装饰器.

提示

译注: 上面的链接貌似已经失效.

关键字中嵌入参数

库关键字还能接受使用 嵌入参数语法 传递的参数. 可以使用装饰器 robot.api.deco.keyword 来创建 自定义关键字名称, 其中包括所需语法.

  1. from robot.api.deco import keyword
  2.  
  3. @keyword('Add ${quantity:\d+} Copies Of ${item} To Cart')
  4. def add_copies_to_cart(quantity, item):
  5. # ...
  1. *** Test Cases ***
  2. My Test
  3. Add 7 Copies Of Coffee To Cart