声明:本文搬运自官方操作文档,仅用作学习,有错误的地方欢迎指正。
官方文档链接:RobotFramework--扩展Robot Framework
4 扩展Robot Framework
4.1 创建测试库
4.1.1 介绍
支持的编程语言
Robot Framework本身是用Python编写的,测试库自然可以使用Python实现扩展。在Jython上运行时,库也可以用Java执行。纯 Python 代码在 Python 和 Jython 上都有效, 但是如果不使用Jython的语法和模块就不能运行在Jython上。使用 Python 时,使用Python C API也能用 C 实现库,但是使用ctypes模块通常更容易与 Python 库的 C 代码交互。
使用这些本机支持的语言执行的库也可以充当使用其他编程语言实现的功能的包装器。这种方法的一个很好的例子是Remote库,另一个广泛使用的方法是运行外部脚本或工具作为单独的进程。
提示
RobotFramework测试库开发人员的 Python 教程涵盖了足够的 Python 语言, 开始使用它编写测试库。还包含一个简单的示例库和测试案例,可以在计算机上执行和以其他方式进行研究。
不同的测试库API
Robot Framework有三种不同的测试库 API。
静态API
最简单的方法是有一个模块(在Python)或一个类(在Python或Java),其方法直接映射到关键字名称。关键字也采用与执行它们的方法相同的参数。关键字报告故障时通过编写到标准输出进行日志记录,并且可以使用return语句返回值。
动态API
动态库是实现获取其执行的关键字名称的方法的类,以及执行带有给定参数的指定关键字的另一种方法。要执行的关键字的名称以及执行方式可以在运行时动态确定,但报告状态、记录和返回值的工作与静态 API 类似。
混合API
这是静态和动态 API 之间的混合体。库是带有说明其执行的关键字的方法的类,但这些关键字必须直接可用。除了发现实现哪些关键字之外,其他一切都与静态 API 中类似。
所有这些 API 都在此章节中进行了描述。一切都基于静态 API 的工作原理,因此首先讨论其功能。动态库 API和混合库 API与它有什么不同,然后在他们自己的部分讨论。
本章中的例子主要是关于使用 Python 的,但对于Java开发人员来说也很容易理解。在少数 API 有差异的情况下,这两种用法都用充分的例子来解释。
4.1.2创建测试库类或模块
测试库可以作为 Python 模块和 Python 或 Java 类执行。
测试库名称
导入库时使用的测试库的名称与执行该库的模块或类的名称相同。例如,如果你有一个Python模块MyLibrary(即文件*MyLibrary.py),它将创建一个库的名字MyLibrary。同样,Java 类YourLibrary,当它不在任何包中时,创建一个完全具有该名称的库。
Python 类始终在模块内。如果执行库的类的名称与模块的名称相同,则Robot Framework允许在导入库时删除模块名称。例如,MyLib.py文件中的MyLib类可以用作具有*MyLib*名称的库。如果模块名称和类名称不同,则必须使用模块和类名称(如mymodule.MyLibrary)。
必须使用非默认包中的 Java 类,并使用全名。例如com.mycompany.myproject包中的MyLib类必须输入名称*com.mycompany.myproject.MyLib*。
提示
如果库名真的很长,例如当 Java 包名称很长时,建议使用WITH NAME 语法给库提供更简单的别名。
规定测试库参数
作为类执行的所有测试库都可以携带参数。这些参数在库名之后的"设置"表中指定,当Robot Framework创建导入库的实例时,它将它们传递给其构造者。作为模块实现的库不能接受任何参数,如果使用会报错。
库所需的参数数与库的构造者接受的参数数相同。默认值和可变参数数与关键字参数类似,但 Java 库没有可变参数支持。传递给库的参数以及库名称本身可以使用变量进行指定,因此可以从命令行中更改。
导入带参数的测试库

示例实现,第一个是 Python,第二个是 Java :
from example import Connection
class MyLibrary:
def __init__(self, host, port=80):
self._conn = Connection(host, int(port))
def send_message(self, message):
self._conn.send(message)
public class AnotherLib {
private String setting = null;
public AnotherLib(String setting) {
setting = setting;
}
public void doSomething() {
if setting.equals("42") {
// do something ...
}
}
}
测试库作用域
作为类执行的库可以具有内部状态,可以通过关键字和与库构造者进行参数更改。由于状态会影响关键字的实际行为方式,因此确保一个测试案例的变化不会影响其他测试案例就非常重要。此类依赖项可能会产生难以验证的问题,例如,当添加新的测试案例且它们使用库不一致时。
Robot Framework试图使测试案例彼此独立:默认情况下,它为每个测试案例创建新的测试库实例。然而,这种行为并不总是可取,因为有时测试案例应该能够共享一个共同的状态。此外,所有库都没有状态,根本不需要创建新的实例。
测试库可以控制何时创建带有类属性ROBOT_LIBRARY_SCOPE的新库。此属性必须是一个字符串,它可以具有以下三个值:
TEST CASE
为每个测试案例创建一个新实例。可能的套件设置和套件拆解共享另一个实例。这是默认值。
TEST SUITE
为每个测试套件创建一个新实例。最低级别的测试套件由测试案例文件创建,包含测试案例,具有自己的实例,而高级套件都拥有自己的实例,用于可能的设置和拆解。
GLOBAL
在整个测试执行过程中只创建了一个实例,所有测试案例和测试套件都共享该实例。从模块创建的库始终是全局性的。
当TEST SUITE或GLOBAL与具有状态的测试库一起使用时,建议库具有一些用于清理状态的特殊关键字。然后,此关键字可用于套件设置或拆解中,以确保下一个测试套件中的测试案例可以从已知状态开始。例如,SeleniumLibrary*使用GLOBAL在不同的测试情况下启用同一浏览器,而无需重新打开它,并且它还具有*Close All Browsers*关键字,可轻松关闭所有打开的浏览器。
使用TEST SUITE示例的 Python 库示例:
class ExampleLibrary:
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
def __init__(self):
self._counter = 0
def count(self):
self._counter += 1
print self._counter
def clear_counter(self):
self._counter = 0
使用GLOBAL范围的示例Java库:
public class ExampleLibrary {
public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL";
private int counter = 0;
public void count() {
counter += 1;
System.out.println(counter);
}
public void clearCounter() {
counter = 0;
}
}
指定库版本
当测试库投入使用时,Robot Framework会尝试确定其版本。然后将此信息写入系统以提供调试信息。库文档工具libdoc还将此信息编写到它生成的关键字文档中。
版本信息是从属性ROBOT_LIBRARY_VERSION读取的,就像测试库范围从ROBOT_LIBRARY_SCOPE读取一样。如果ROBOT_LIBRARY_VERSION不存在,则尝试从__version__属性读取信息。这些属性是类属性还是模块属性,具体取决于库是作为类还是模块实现的。对于 Java 库,版本属性必须声明为static final。
使用__version__的 Python 模块示例:
__version__ = '0.1'
def keyword():
pass
使用ROBOT_LIBRARY_VERSION的Java类:
public class VersionExample {
public static final String ROBOT_LIBRARY_VERSION = "1.0.2";
public void keyword() {
}
}
4.1.3 创建静态关键字
哪些方法被视为关键字
使用静态库 API 时,Robot Framework使用反射来找出库类或模块执行的公共方法。它将排除所有从下划线开始的方法,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__最重要的用途是确保导入的帮助方法(如下面示例中的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
注意
从RobotFramework 2.5.5 开始支持__all__属性。
关键字名称
将测试数据中使用的关键字名称与方法名称进行比较以查找实现这些关键字的方法。名称比对不区分大小写,空格和下划线也被忽略。例如,方法hello映射的关键字名称Hello, hello 甚至h e l l o同样,do_nothing和doNothing 的方法都可以用作测试数据中Do Nothing的关键字。
示例 Python 库作为MyLibrary.py文件中的模块实现:
<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">def hello(name): print "Hello, %s!" % name def do_nothing(): pass</pre>
示例 Java 库在MyLibrary .java文件中作为一个类实现:
def hello(name):
print "Hello, %s!" % name
def do_nothing():
pass
下面的示例说明了如何使用上述示例库。如果您想自己尝试,请确保库位于库搜索路径中。

关键字参数
使用静态和混合 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 multiple_arguments(a1, a2, a3):
print "Keyword got three arguments '%s', '%s' and '%s'" % (a1, a2, a3)
关键字默认值
关键字使用带默认值的参数通常是有用的。Python 和 Java 处理默认值的语法不同,在为Robot Framework创建测试库时可以使用这些语言的自然语法。
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)
上面的第一个示例关键字可以使用零或一个参数。如果没有参数,arg将获得值默认值。如果有一个参数,arg将获得该值,并且用多个参数调用关键字失败。在第二个示例中,始终需要一个参数,但第二个和第三个参数具有默认值,因此可以使用关键字和一到三个参数。

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 Case | Action | Argument | Argument | Argument |
|---|---|---|---|---|
| Varargs | Any Arguments | |||
| Any Arguments | argument | |||
| Any Arguments | arg 1 | arg 2 | arg 2 | |
| ... | 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支持Java可变参数语法来定义参数的变量数。Robot还将将数组翻译为关键字签名中的最后一个参数,作为参数的变量数列表。在这种情况下,关键字的所有剩余参数都打包到列表中。如果使用的关键字与一个参数少于签名中的实际参数数,这也有效——在这种情况下,末端的阵列将是空的。
Robot对 Java 参数的变量数的支持有一个限制:它仅在方法只有一个签名时才有效。因此,不可能有具有默认值和参数变量的 Java 关键字。当然,仍有可能需要的参数,如下示例所示:
public void anyNumberOfArguments(String... varargs) {
System.out.println("Got arguments:");
for (String arg: varargs) {
System.out.println(arg);
}
}
public void oneArgumentRequired(String required, String... others) {
System.out.println("Required: " + required + "\nOthers:");
for (String arg: others) {
System.out.println(arg);
}
}
public void usingAnArray(String[] args) {
System.out.println("Got arguments:");
for (String arg: args) {
System.out.println(arg);
}
}
参数类型
通常关键字参数以字符串的身份出现在Robot Framework中。如果关键字需要其他类型,则可以使用变量或将字符串转换为关键字内所需的类型。Java关键字基本类型也自动强制的。
Python的参数类型
由于 Python 中的参数没有任何类型信息,因此在使用 Python 库时,不可能自动将字符串转换为其他类型。调用 Python 方法以使用正确数量的参数实现关键字总是成功的,但如果参数不兼容,则以后执行将失败。幸运的是,Python 将参数转换为关键字内的合适类型非常简单:
def connect_to_host(address, port=25):
port = int(port)
# ...
Java的参数类型
对 Java 方法的参数具有类型,并且所有基本类型都自动处理。这意味着测试数据中正常字符串的参数被强制在运行时更正类型。可以强制的类型是:
- 整数类型(
byte,short,int,long) - 浮点类型(
float和double) - 布尔类型(
boolean) - 上述类型的对象版本,例如
java.lang.Integer
强制是针对关键字方法的所有签名具有相同或兼容类型的参数进行的。在以下示例中,可对关键字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) {}
如果测试数据包含包含数字的字符串,则强制与数字类型配合使用,并且与布尔类型一起,数据必须包含真或假字符串。只有在原始值是来自测试数据的字符串时才进行强制,但当然仍可能使用包含 这些关键字正确类型的变量。如果关键字的签名相互冲突,则使用变量是唯一的选项。
| Test Case | Action | Argument | Argument | Argument |
|---|---|---|---|---|
| Coercion | Double Argument | 3.14 | ||
| Double Argument | 2e16 | # scientific notation | ||
| 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 |
4.1.4 Robot Framework通信
在调用执行关键字的方法后,它可以使用任何机制与受测试的系统进行通信。然后,它还可以向Robot Framework的日志文件发送消息,将可以保存到变量的信息返回,最重要的是,如果关键字是否通过,则报告。
报告关键字状态
报告关键字状态可以简单使用异常来完成。如果执行的方法抛了一个异常,关键字状态是FAIL,如果它返回正常,状态是PASS。
日志、报告和控制台中显示的错误消息是从异常类型及其消息创建的。除一般异常(例如AssertionError, Exception, 和 RuntimeError)外,仅使用异常消息,而与其他例外消息一起,创建的消息格式为ExceptionType: Actual message。在这两种情况下,对于用户来说,异常信息尽可能详细是最重要的。
如果错误消息长于 40 行,则会自动从中间剪切,以防止报告过长且难以读取。完整的错误消息始终显示在失败关键字的日志消息中。
还使用DEBUG 日志级别记录异常的跟踪。默认情况下,这些消息在日志文件中不可见,因为它们对普通用户来说用处不大。在开发库时,通常建议使用DEBUG日志级运行测试。
停止测试执行
从Robot Framework 2.5 开始,测试案例可能会失败,从而停止整个测试执行。这仅仅是通过在关键字中提出的异常值上设置具有真实值的特殊ROBOT_EXIT_ON_FAILURE属性来完成的。下面的例子说明了这一点。
Python:
class MyFatalError(RuntimeError):
ROBOT_EXIT_ON_FAILURE = True
Java:
public class MyFatalError extends RuntimeException {
public static final boolean ROBOT_EXIT_ON_FAILURE = true;
}
失败后继续测试执行
从Robot Framework 2.5 开始,即使出现故障也可以继续测试执行。从测试库发出此信号的方法是向用于传达故障的异常添加具有真实值的特殊ROBOT_CONTINUE_ON_FAILURE属性。以下示例就证明了这一点。
Python:
class MyContinuableError(RuntimeError):
ROBOT_CONTINUE_ON_FAILURE = True
Java:
public class MyContinuableError extends RuntimeException {
public static final boolean ROBOT_CONTINUE_ON_FAILURE = true;
}
日志信息
异常消息不是向用户提供信息的唯一方法。除了它们,方法还可以通过编写到标准输出流(Stdout)或标准错误流(stderr)来发送消息到日志文件,甚至可以使用不同的日志级别。另一种,而且通常更好是使用程序化日志API记录日志。
默认情况下,通过方法写入标准输出的所有内容都写入日志文件,每条日志级别都是INFO。写入标准错误的消息处理方式类似,但在关键字执行完成后,会回应到原始的 stderr。因此,如果您需要一些消息在执行测试的控制台上可见,则可以使用 stderr。
使用日志级别
要使用INFO以外的其他日志级别,或创建多个消息,通过在格式*LEVEL* Actual log message中嵌入级别来明确指定日志级别,其中*LEVEL*必须在行的开头,LEVEL有TRACE、DEBUG、INFO、WARN和HTML。
警告
带有WARN级别的消息会自动写入控制台,并写入日志文件中的单独测试执行错误部分。这使得警告比其他消息更明显,并允许使用它们向用户报告重要但非关键问题。
记录HTML
库通常记录的所有内容都将转换为可以安全地表示为 HTML 的格式。例如,<b>foo</b>在日志中将显示与之完全相同的foo。 如果库想要使用格式、链接、显示图像等,他们可以使用特殊的伪日志级别HTML。Robot Framework将直接将这些消息与INFO级别一起写入日志,以便他们可以使用他们想要的任何HTML语法。注意此功能需要小心使用,例如,放置不当的 </table>表标签可能会严重破坏日志文件。
使用公共记录 API时,各种记录方法具有可选的html属性,可设置为True,以便以 HTML 格式进行记录。
时间戳
默认情况下,通过标准输出或错误流记录的消息在执行关键字结束时会获取其时间戳。这意味着时间戳不准确,调试问题特别是运行时间较长的关键字可能会有问题。
从Robot Framework 2.6 关键字开始,如果需要,可以向其记录的消息添加准确的时间戳。时间戳单位是毫秒且必须在日志界别之后,以冒号分隔:
*INFO:1308435758660* Message with timestamp
*HTML:1308435758661* <b>HTML</b> message with timestamp
如下所示,使用 Python 和 Java 都很容易添加时间戳。Python使用程序化日志API更容易获得准确的时间戳。明确添加时间戳的一大好处是适用于远程库界面
Python:
import time
def example_keyword():
print '*INFO:%d* Message with timestamp' % (time.time()*1000)
Java:
public void exampleKeyword() {
System.out.println("*INFO:" + System.currentTimeMillis() + "* Message with timestamp");
}
控制台输出日志
如果库需要向控制台写东西,他们有几个选项。正如已经讨论过的那样,写入标准错误流的警告和所有消息都写入日志文件和控制台。这两个选项都有一个限制,即消息仅在当前执行的关键字完成后才结束到主机。一个好处是,这些方法都适用于Python和Java库。
另一种选择,即仅适用于 Python,是向sys.__stdout__或sys.__stderr__发送消息。使用此方法时,消息会立即写入控制台,且不写入日志文件:
import sys
def my_keyword(arg):
sys.__stdout__.write('Got arg %s\n' % arg)
最后一种选择是使用程序化日志API:
from robot.api import logger
def log_to_console(arg):
logger.console('Got arg %s' % arg)
def log_to_console_and_log_file(arg)
logger.info('Got arg %s' % arg, also_console=True)
记录示例
大多数情况INFO级别是足够的。更低级别的DEBUG和TRACE是用来记录调试信息。这些消息通常不显示,但它们可以协助调试库本身中可能出现的问题。WARN级别可用于使消息更加可见,如果需要任何格式,则 HTML更好。
以下示例阐明了不同级别日志工作原理。Java 程序员应将代码print 'message'改为System.out.println("message")。
print 'Hello from a library.'
print '*WARN* Warning from a library.'
print '*INFO* Hello again!'
print 'This will be part of the previous message.'
print '*INFO* This is a new message.'
print '*INFO* This is <b>normal text</b>.'
print '*HTML* This is <b>bold</b>.'
print '*HTML* <a href="http://robotframework.org">Robot Framework</a>'
16:18:42.123 信息 你好,从图书馆。
16:18:42.123 警告 来自库的警告。
16:18:42.123 信息 您好!
这将是上一条消息的一部分。
16:18:42.123 信息 这是一条新消息。
16:18:42.123 信息 这是 [lt; b] 正常文本。
16:18:42.123 信息 这是大胆的。
16:18:42.123 信息 机器人框架
程序化日志API
程序化 API 提供比使用标准输出和错误流更清洁的方式来记录信息。目前,这些接口仅适用于 Python 基础测试库。
公共日志API
Robot Framework 2.6有基于 Python 的新型日志API,用于向日志文件和控制台发送消息。测试库可以使用此 API(如logger.info('My message')),而不是通过print '*INFO* My message'等标准输出进行记录。除了编程界面更加简洁以外,这个API有一个好处就是日志消息有准确的时间戳。缺点是,日志API 的测试库要依赖Robot Framework。
公共日志API 作为 API 文档的一部分进行记录,下面是一个简单的使用示例:
from robot.api import logger
def my_keyword(arg):
logger.debug('Got argument %s' % arg)
do_something()
logger.info('<i>This</i> is a boring example', html=True)
logger.console('Hello, console!')
使用Python标准logging模块
除了新的公共日志API,Robot Framework2.6还增加了一个内置的支持Python的标准日志模块。这样,模块的根记录器接收到的所有消息都会自动传播到Robot Framework的日志文件中。此外,此 API 会生成具有准确时间戳的日志消息,但不支持记录 HTML 消息或向控制台发送消息。好处是此日志 API 不依赖Robot Framework,以下示例也说明了这一点。
import logging
def my_keyword(arg):
logging.debug('Got argument %s' % arg)
do_something()
logging.info('This is a boring example')
logging模块的日志级别与Robot Framework略有不同。其级别DEBUG和INFO直接映射到匹配的Robot Framework日志级别和警告,上面的所有内容都映射到WARN。DEBUG级别以下的自定义级别映射到DEBUG,DEBUG和WARNING之间的一切都映射到INFO。
库初始化期间的日志
库还可以在测试库导入和初始化期间进行日志记录。这些消息不像正常日志一样显示在日志文件中,而是被写入syslog。这允许记录库初始化相关的所有有用的调试信息。使用WARN级别记录的消息也可见于日志文件中的测试执行错误部分。
在导入和初始化过程中进行登录既可使用标准输出流和错误流,也可使用程序日志 ABI。这两个都如下所示。
Java库在初始化过程中借助标准输出进行记录:
public class LoggingDuringInitialization {
public LoggingDuringInitialization() {
System.out.println("*INFO* Initializing library");
}
public void keyword() {
// ...
}
}
Python 库在导入期间使用日志 API 进行记录:
from robot.api import logger
logger.debug("Importing library")
def keyword():
# ...
注意
如果您在初始化过程中记录某些信息,即在 Python init或 Java 构造器中,则消息可能会根据测试库范围多次记录。在库初始化期间将日志消息编写到 syslog 的支持是Robot Framework 2.6 中的一个新功能。
返回值
关键字通信回核心框架的最后方式是返回从受测试的系统中检索或通过其他方式生成的信息。返回的值可以分配给测试数据中的变量,然后用作其他关键字的输入,甚至来自不同的测试库。
返回值都是使用 的Python 和 Java 方法的return语句。通常一个值被分配到一个标量变量,如下所示。此示例还说明,可以返回任何对象并使用扩展变量语法访问对象的属性。
from mymodule import MyObject
def return_string():
return "Hello, world!"
def return_object(name):
return MyObject(name)
从关键字返回一个值

关键字也可以返回值,以便它们可以一次分配到多个标量变量中,放入列表变量,或分配到标量变量和列表变量中。所有这些用法都要求返回的值是 Python 列表或元组,或在 Java数组、列表或迭代器中。
def return_two_values():
return 'first value', 'second value'
def return_multiple_values():
return ['a', 'list', 'of', 'strings']
返回多个值

多线程通信
如果库使用多线程,则通常只能从主线程与框架通信。例如,如果运行的线程要报告失败或记录什么,则应首先将信息传递到主线程,主线程可以使用本节中解释的异常或其他机制与框架进行通信。
当线程在后台运行,而其他关键字正在运行时,这一点尤其重要。在这种情况下,与框架通信的结果未定义,在工作情况下可能导致崩溃或输出文件损坏。如果关键字在后台启动某些内容,则应该有另一个关键字来检查执行线程的状态,并相应地报告收集的信息。
注意
使用编程记录 API的非主线程记录的消息从RobotFramework 2.6.2 开始被忽略。
4.1.5 分配测试库
库文档
没有关于它包含哪些关键字和这些关键字做什么的文档的测试库是相当无用的。为了便于维护,我们强烈建议将库文档包含在源代码中并由此生成。基本上,这意味着要使用Python的docstring和Java的Javadoc,如下示例。
class MyLibrary:
"""This is an example library with some documentation."""
def keyword_with_short_documentation(self, argument):
"""This keyword has only a short documentation"""
pass
def keyword_with_longer_documentation(self):
"""First line of the documentation is here.
Longer documentation continues here and it can contain
multiple lines or paragraphs.
"""
pass
/**
* This is an example library with some documentation.
*/
public class MyLibrary {
/**
* This keyword has only a short documentation
*/
public void keywordWithShortDocumentation(String argument) {
}
/**
* First line of the documentation is here.
*
* Longer documentation continues here and it can contain
* multiple lines or paragraphs.
*/
public void keywordWithLongerDocumentation() {
}
}
Python 和 Java 都有创建上述文档的 API 文档的工具。但对于某些用户来说,这些工具的输出可能略带技术性。另一种选择是使用RobotFramework自己的文档工具libdoc。此工具可以使用静态库 API(如上面的 API)创建来自 Python 和 Java 库的库文档,但它也使用动态库 API和混合库 API处理库。
关键字文档的第一行用于特殊目的,并且应包含关键字的简短整体描述。它被libdoc用作**short documentation,例如作为工具提示,也显示在测试日志中。但是,后者不使用静态 API 与 Java 库合作,因为它们的文档在编译中丢失,在运行时不可用。
注意
如果您想在 Python 库的文档中使用非 ASCII 字符,则必须使用 UTF-8 编码,或将文档字符串创建为 Unicode。
测试库
任何重要的测试库都需要经过彻底测试以防止其中出现错误。当然,此测试应实现自动化,以在更改库时便于重新运行测试。
Python 和 Java 都拥有出色的单元测试工具,它们非常适合测试库。与用于其他测试相比,为此目的使用它们没有重大区别。熟悉这些工具的开发人员不需要学习任何新内容,不熟悉这些工具的开发人员无论如何都应该学习它们。
也很容易使用RobotFramework本身测试库,这样可以为他们进行实际的端到端验收测试。为此内置库中有很多有用的关键字。特别提及的是***Run Keyword And Expect Error*,这是有用的,为测试关键字报告正确的错误。
是否使用单元级或验收级测试方法取决于上下文。如果需要模拟正在测试的实际系统,则在单元级别上通常更容易。另一方面,验收测试确保关键字运行在RobotFramework。如果不能决定,也可以同时使用这两种方法。
库打包
库实施、记录和测试后,仍需要分发给用户。使用由单个文件组成的简单库,要求用户在某处复制该文件并相应地设置库搜索路径通常就足够了。应打包更复杂的库以便于安装。
由于库是正常的编程代码,因此可以使用普通打包工具进行封装。在Python,选择包括distutils,包括Python的标准库,和较新的setuptools。这些工具的一个好处是将库模块安装到自动在库搜索路径中的位置。
使用 Java 时,自然将库打包到 JAR 存档中。JAR 包在运行测试之前必须放入库搜索路径中,但也可以很容易创建自动操作的启动脚本。
弃用关键字
有时需要用新的关键字替换现有关键字或将其完全删除。仅仅将更改告知用户可能不够,而且在运行时间获得警告效率更高。为了支持这一点,RobotFramework提供了标记被弃用的关键字的能力*deprecated*。这使得从测试数据中查找旧关键字并删除或替换它们变得更加容易。
关键字被弃用,以*DEPRECATED*开头他们的文档。执行这些关键字时,包含其余简短文档的警告将同时写入控制台,并写入日志文件中的单独测试执行错误部分。例如,如果执行以下关键字,日志文件中将显示如下警告。
def example_keyword(argument):
"""*DEPRECATED* Use keyword `Other Keyword` instead.
This keyword does something to given `argument` and returns the result.
"""
return do_something(argument)
WARN Keyword 'SomeLibrary.Example Keyword' is deprecated. Use keyword `Other Keyword` instead.
此弃用系统适用于大多数测试库和用户关键字。唯一例外是在Java测试库中实现的关键字,该库使用静态库接口,因为它们的文档在运行时间不可用。有了这些关键字,就可以使用用户关键字作为装饰器并弃用它们。
有一个计划来实现一个工具,可以使用弃用信息自动替换废弃的关键字。该工具很可能从文档中获取新关键字的名称,以便搜索到引号 (```)中的单词。因此,它会从前面的例子中找到Other Keyword。请注意,libdoc还使用相同的语法自动创建内部链接。
4.1.6 动态库API
动态 API 在大多数方面与静态 API 相似。例如,报告关键字状态、日志记录和返回值的工作方式完全相同。最重要的是,与其他库相比,导入动态库和使用其关键字没有区别,因此您甚至不需要知道库使用什么 API。
静态库和动态库之间的唯一区别是Robot Framework如何发现库实现的关键字、它们具有哪些参数和文档以及这些关键字的实际执行方式。使用静态 API,所有这一切都是使用反射完成的(Java库的文档除外),但动态库具有用于这些用途的特殊方法。
动态 API 的优点之一是在组织库方面具有更大的灵活性。对于静态 API,所有关键字在类(或模块)中,而对于动态 API,可以将每个关键字作为单独的类实现。此使用案例对于 Python 来说不是那么重要,因为它的动态功能和多重继承已经提供了足够的灵活性,混合库 API通常是一个更好的选择。
动态 API 的另一个主要用法是实现库,以便它仅是其他计算机或其他 JVM 上实际库的代理。这种代理库可能非常小,并且由于关键字名称是动态获取的,因此当将新关键字添加到实际库时,无需更新代理。
本节解释了动态 API 如何在Robot Framework和动态库之间工作。Robot Framework实际上如何实现这些库并不重要(例如,如何将执行run_keyword方法映射到正确的关键字),并且有许多不同的方法是可能的。但是,如果您使用 Java,您可能需要在实施自己的系统之前检查JavalibCore。此可重复使用的工具集合支持创建关键字的几种方法,而且它很可能已经有一个机制来满足您的需求。
获取关键字名称
动态库使用get_keyword_names方法了解它们实施的关键字。该方法还具有在编写 Java 时推荐的别名getKeywordNames。此方法不能携带任何参数,并且它必须返回字符串列表(在 Python 中)或包含库实现的关键字名称的字符串数组(在 Java 中)。
如果返回的关键字名称包含几个单词,则可以用空格或下划线或驼峰格式将它们分开返回。例如,['first keyword', 'second keyword'], ['first_keyword', 'second_keyword']和['firstKeyword', 'secondKeyword']都会导致关键字First Keyword和Second Keyword。
动态库必须始终采用此方法。如果缺少它,或者如果出于某种原因调用失败,则库将被视为静态库。
执行关键字
动态库具有执行关键字的特殊方法run_keyword(别名runKeyword)。当测试数据中使用动态库的关键字时,Robot Framework使用库的run_keyword方法执行。此方法需要两个参数,第一个是包含要执行的关键字的名称的字符串,其格式与get_keyword_names返回的格式相同,第二个是提供给测试数据中关键字的参数列表(Java 中的对象数组)。
库获得关键字名称和参数后,可以自由执行关键字,但必须使用与静态库的框架相同的机制进行通信。这意味着使用异常报告关键字状态,通过编写到标准输出进行记录,并在run_keyword中使用返回语句返回某些内容。
每个动态库都必须具有get_keyword_names和run_keyword方法。动态 API 中的其余方法是可选的,因此下面是一个可用动态库。
class DynamicExample:
def get_keyword_names(self):
return ['first keyword', 'second keyword']
def run_keyword(self, name, args):
print "Running keyword %s with arguments %s" % (name, args)
获取关键字参数
如果动态库仅实现get_keyword_names和run_keyword方法,则Robot Framework没有任何有关已实施关键字所需的参数的信息。例如,上述示例中的第一个关键字和第二个关键字都可用于任意数量的参数。这是有问题的,因为大多数真正的关键字期望一定数量的关键字,在这种情况下,他们需要检查参数计数本身。
动态库可以使用get_keyword_arguments(别名获取关键字)方法告诉Robot Framework它实现的关键字实际期望的参数。此方法以关键字的名称为参数,并返回包含该关键字所接受的参数的字符串列表(Java 中的字符串阵列)。
与静态关键字类似,动态关键字可能需要任意数量的参数、具有默认值和接受可变数的参数。下表解释了如何表示所有这些不同情况的语法。请注意,示例使用 Python 字符串列表,但 Java 开发人员应该能够将其翻译为字符串阵列。
| Expected arguments | How to represent | Examples | Min / Max |
|---|---|---|---|
| No arguments | Empty list. | [] | 0/0 |
| One or more argument | List of strings containing argument names. | ['one_argument'], ['a1', 'a2', 'a3'] | 1/1, 3/3 |
| Default values for arguments | Default values separated from names with =. Default values are always considered to be strings. | ['arg=default value'], ['a', 'b=1', 'c=2'] | 0/1, 1/3 |
| Variable number of arguments | Last argument has * before its name. | ['arguments'], ['a', 'b=42', 'rest'] | 0/any, 1/any |
当使用get_keyword_arguments时,Robot Framework会自动计算关键字需要多少参数。如果使用关键字的参数数为无效,则会出现错误,并且甚至没有调用run_keyword。上表的最后一列显示了从所示示例中计算的最低和最大参数计数。
执行测试时,实际参数名称并不重要,因为Robot Framework只关注参数计数。另一方面,如果libdoc工具用于记录库,则文档中显示参数,在这种情况下,它们需要有有意义的名称。
获取关键字文档
动态图书馆可以实施的最后一种特殊方法是get_keyword_documentation(别名获取关键字文件)。它将关键字名称作为参数,并且,正如方法名称所暗示的那样,将其文档返回为字符串。
返回的文档同样用作使用 Python 实现的静态库的关键字文档字符串。主要用途是将关键字的文档放入由libdoc生成的库文档中。此外,文档的第一行(直到第一行\n)显示在测试日志中。
获取总库文档
get_keyword_documentation方法也可用于指定整体库文档。执行测试时不使用此文档,但它可以使libdoc生成的文档变得更好。
动态图书馆可以同时提供与使用图书馆相关的一般图书馆文档和文档。前者是通过调用具有特殊价值intro的get_keyword_documentation获得,后者是利用价值init。文档的呈现方式在实践中最好地通过libdoc进行测试。
基于 Python 的动态库还可以将代码中的一般库文档直接指定为库类的文档或其init方法。如果非空文档直接来自代码和get_keyword_documentation方法,则后者具有更高的优先级。
注意
获取一般库文档支持Robot Framework 2.6.2 和较新的文档。
总结
动态 API 中的所有特殊方法都列在下表中。方法名称列在下划线格式中,但其骆驼箱别名的工作方式完全相同。
动态 API 中的所有特殊方法
可以按照以下方式在 Java 中编写正式的界面规范。但是,请记住,库不需要实施任何明确的界面,因为Robot Framework直接检查与反射,如果库有所需的get_keyword_names和run_keyword方法。此外,get_keyword_arguments和get_keyword_documentation是完全可选的。
public interface RobotFrameworkDynamicAPI {
String[] getKeywordNames();
Object runKeyword(String name, Object[] arguments);
String[] getKeywordArguments(String name);
String getKeywordDocumentation(String name);
}
使用动态API的一个很好的例子是Robot Framework自己的远程库。