Robot Framework 新版教程 - 远程库接口
(Robot Framework 7.x 教程, Part 19)
远程库接口
远程库接口(Remote library interface)提供了一种方式,使得测试库可以运行在与 Robot Framework 不同的机器上,也可以使用 Python 之外的其他语言来实现测试库。对于测试用例来说,远程库看起来和其他测试库几乎没有区别,使用远程库接口来开发测试库也与创建普通测试库非常相似。
简介
使用远程库 API 有两个主要原因:
-
实际的测试库可以部署在与 Robot Framework 运行环境不同的机器上。这为分布式测试提供了很多可能性。
-
测试库可以使用任何支持 XML-RPC 协议的语言来实现。针对 Python、Java、Ruby、.NET 等各种语言,已经有现成的通用远程服务器可用。
远程库接口由 Remote 库提供,它是标准库之一。这个库本身没有任何关键字,但它充当核心框架与其他地方实现的关键字之间的代理。Remote 库通过远程服务器与实际的库实现交互,Remote 库和服务器之间使用基于 XML-RPC 通道的简单远程协议进行通信。整体的高层架构如下图所示:

Remote 客户端使用 Python 标准的 XML-RPC 模块。它不支持某些 XML-RPC 服务器实现的自定义 XML-RPC 扩展。
使用 Remote 库
导入 Remote 库
Remote 库需要知道远程服务器的地址,除此之外,导入它和使用它提供的关键字与使用其他库没有任何不同。如果需要在同一个测试套件中多次使用 Remote 库,或者想给它一个更具描述性的名字,可以在导入时给它设置别名。
*** Settings ***
Library Remote http://127.0.0.1:8270 AS Example1
Library Remote http://example.com:8080/ AS Example2
Library Remote http://10.0.0.2/example 1 minute AS Example3
上面第一个例子使用的 URL 也是 Remote 库在未指定地址时使用的默认地址。
上面最后一个例子展示了如何通过可选的第二个参数为 Remote 库设置自定义超时时间。这个超时时间在初始连接服务器时以及连接意外断开时使用。超时时间可以使用 Robot Framework 的时间格式来指定,如 60s 或 2 minutes 10 seconds。默认超时时间通常是几分钟,但具体取决于操作系统及其配置。注意,如果设置的超时时间比关键字执行时间短,会导致关键字被中断。
端口
8270是远程服务器预期使用的默认端口,它已经被 IANA 注册用于此目的。选择这个端口号是因为 82 和 70 分别是字母R和F的 ASCII 码。
连接本地机器时,建议使用 IP 地址
127.0.0.1而不是机器名localhost。这可以避免地址解析,地址解析至少在 Windows 上可能会非常慢。
如果 URI 在服务器地址之后没有包含路径,Remote 库使用的 XML-RPC 模块默认会使用
/RPC2路径。实际上使用http://127.0.0.1:8270和使用http://127.0.0.1:8270/RPC2是完全相同的。根据远程服务器的不同,这可能会也可能不会成为问题。如果地址包含路径,即使路径只是/,也不会附加额外的路径。例如,http://127.0.0.1:8270/和http://127.0.0.1:8270/my/path都不会被修改。
启动和停止远程服务器
在导入 Remote 库之前,提供实际关键字的远程服务器必须先启动。如果服务器在启动测试执行之前就已经运行,可以像上面的例子那样使用普通的 Library 设置。也可以使用其他关键字来启动服务器,例如使用 Process 库或 SSH 库中的关键字,但这种情况下可能需要使用 Import Library 关键字,因为在测试执行开始时该库尚不可用。
如何停止远程服务器取决于它的实现方式。通常服务器支持以下几种方法:
- 无论使用什么库,远程服务器都应该提供
Stop Remote Server关键字,可以方便地在执行的测试中使用。 - 远程服务器应该在其 XML-RPC 接口中有
stop_remote_server方法。 - 在服务器运行的控制台中按
Ctrl-C应该可以停止服务器。 - 可以使用操作系统提供的工具终止服务器进程(例如
kill)。
服务器可以被配置为用户无法通过
Stop Remote Server关键字或stop_remote_server方法来停止它。
支持的参数和返回值类型
因为 XML-RPC 协议不支持所有可能的对象类型,在 Remote 库和远程服务器之间传输的值必须转换为兼容的类型。这既适用于 Remote 库传递给远程服务器的关键字参数,也适用于服务器返回给 Remote 库的返回值。
Remote 库和 Python 远程服务器按照以下规则处理 Python 值。其他远程服务器也应该有类似的行为。
-
字符串、数字和布尔值不做修改直接传递。
-
Python 的
None被转换为空字符串。 -
所有列表、元组和其他可迭代对象(字符串和字典除外)都作为列表传递,其内容会被递归转换。
-
字典和其他映射类型作为 dict 传递,其键被转换为字符串,值被递归转换为支持的类型。
-
返回的字典会被转换为所谓的点访问字典(dot-accessible dicts),允许使用扩展变量语法通过
${result.key}的形式以属性方式访问键。这对嵌套字典也有效,如${root.child.leaf}。 -
包含 ASCII 范围内无法在 XML 中表示的字节的字符串(例如空字节)会作为 Binary 对象发送,内部使用 XML-RPC base64 数据类型。接收到的 Binary 对象会自动转换为字节字符串。
-
其他类型被转换为字符串。
远程协议
本节解释 Remote 库与远程服务器之间使用的协议。这些信息主要面向想要创建新远程服务器的开发者。
远程协议基于 XML-RPC 实现,XML-RPC 是一种使用 XML over HTTP 的简单远程过程调用协议。大多数主流语言(Python、Java、C、Ruby、Perl、Javascript、PHP 等)都内置或通过扩展支持 XML-RPC。
Python 远程服务器可以作为参考实现。
必需的方法
远程服务器有两种方式来提供关于其包含的关键字的信息。下面简要说明这两种方式,后续章节会有更详细的文档。
-
远程服务器可以实现与动态库 API 相同的方法。即
get_keyword_names方法以及可选的get_keyword_arguments、get_keyword_types、get_keyword_tags和get_keyword_documentation方法。注意,不能像普通动态 API 那样使用驼峰命名风格(如getKeywordNames)。 -
从 Robot Framework 4.0 开始,远程服务器可以有一个单独的
get_library_information方法,以单个字典的形式返回所有库和关键字信息。如果远程服务器有这个方法,其他 getter 方法如get_keyword_names将完全不被使用。这种方式的好处是只需要一次 XML-RPC 调用就能获取所有信息,而上述第一种方式每个关键字都需要多次调用。对于较大的库,这个差异可能非常显著。
无论远程服务器如何提供关键字信息,它们都必须有 run_keyword 方法,在关键字执行时被调用。实际关键字如何实现与 Remote 库无关。远程服务器既可以像现有的通用远程服务器那样作为真实测试库的包装器,也可以自己实现关键字。
远程服务器还应该在其公共接口中有 stop_remote_server 方法,以便于停止服务器。它们还应该自动将该方法公开为 Stop Remote Server 关键字,以便在测试数据中使用,无论测试库是什么。允许用户停止服务器并不总是合适的,服务器可以通过某种方式支持禁用此功能。该方法和公开的关键字应该根据是否允许停止返回 True 或 False。这使得外部工具可以知道停止服务器是否成功。
使用 get_keyword_names 和关键字特定的 getter 方法
本节解释当服务器实现了 get_keyword_names 时,Remote 库如何获取关键字名称和其他信息。下一节介绍使用较新的 get_library_info 方法。
get_keyword_names 方法必须以字符串列表的形式返回服务器包含的关键字名称。远程服务器还可以(也应该)实现 get_keyword_arguments、get_keyword_types、get_keyword_tags 和 get_keyword_documentation 方法来提供关于关键字的更多信息。所有这些方法都接受关键字名称作为参数,它们必须返回的内容在下表中说明。
| 方法 | 返回值 |
|---|---|
get_keyword_arguments |
以字符串列表形式返回参数,格式与动态库相同。 |
get_keyword_types |
以字符串列表或字典形式返回类型信息。详见下文。 |
get_keyword_documentation |
以字符串形式返回文档。 |
get_keyword_tags |
以字符串列表形式返回 tag。 |
用于参数转换的类型信息可以作为列表返回(按位置将类型名称映射到参数),也可以作为字典返回(直接将参数名称映射到类型名称)。实际上,这与在普通库中使用 @keyword 装饰器指定类型的工作方式相同。区别在于,由于 XML-RPC 协议不支持任意值,类型信息需要使用类型名称或别名来指定,如 'int' 或 'integer',而不是使用实际类型如 int。此外,XML-RPC 服务器可能不允许 None 或 null 值,但可以使用空字符串来表示某个参数没有类型信息。
基于默认值的参数转换也受支持,使用与普通库相同的逻辑。要使其正常工作,带有默认值的参数必须以元组而非字符串的形式返回,这与动态库的方式相同。例如,如果参数信息以 [('count', 1), ('caseless', True)] 的形式返回,参数转换可以正常工作,但如果是 ['count=1', 'caseless=True'] 则不行。
远程服务器还可以提供通用库文档,供 Libdoc 工具生成文档时使用。这些信息通过使用特殊值 __intro__ 和 __init__ 调用 get_keyword_documentation 来获取。
get_keyword_types是 Robot Framework 3.1 中新增的,基于默认值的参数转换支持是 Robot Framework 4.0 中新增的。
使用 get_library_information
get_library_information 方法允许在一次 XML-RPC 调用中返回整个库的信息。信息必须以字典的形式返回,其中键是关键字名称,值是包含关键字信息的嵌套字典。字典还可以包含通用库信息的单独条目。
关键字信息字典可以包含关键字参数、文档、tag 和类型,对应的键分别是 args、doc、tags 和 types。信息必须使用与上一节讨论的 get_keyword_arguments、get_keyword_documentation、get_keyword_tags 和 get_keyword_types 相同的语义来提供。如果某些信息不可用,可以从信息字典中完全省略。
get_library_information 还支持返回通用库文档,供 Libdoc 使用。方法是在返回的库信息字典中包含特殊的 __intro__ 和 __init__ 条目。
例如,如下 Python 库:
"""Library documentation."""
from robot.api.deco import keyword
@keyword(tags=['x', 'y'])
def example(a: int, b=True):
"""Keyword documentation."""
pass
def another():
pass
可以映射为如下库信息字典:
{
'__intro__': {'doc': 'Library documentation'}
'example': {'args': ['a', 'b=True'],
'types': ['int'],
'doc': 'Keyword documentation.',
'tags': ['x', 'y']}
'another': {'args': []}
}
get_library_information是 Robot Framework 4.0 中新增的。
执行远程关键字
当 Remote 库需要服务器执行某个关键字时,它会调用远程服务器的 run_keyword 方法,并传递关键字名称、参数列表,以及可能的自由命名参数字典。基本类型可以直接作为参数使用,但更复杂的类型会被转换为支持的类型。
服务器必须在结果字典(或映射,取决于术语)中返回执行结果,其中包含下表中说明的条目。注意只有 status 条目是必需的,其他条目在不适用时可以省略。
| 名称 | 说明 |
|---|---|
| status | 必需的执行状态。值为 PASS 或 FAIL。 |
| output | 可能需要写入日志文件的输出。必须以单个字符串给出,但可以包含多条消息和不同的日志级别,格式为 *INFO* First message\n*HTML* <b>2nd</b>\n*WARN* Another message。也可以在日志消息中嵌入时间戳,如 *INFO:1308435758660* Message with timestamp。 |
| return | 可能的返回值。必须是支持的类型之一。 |
| error | 可能的错误信息。仅在执行失败时使用。 |
| traceback | 可能的堆栈跟踪,在执行失败时以 DEBUG 级别写入日志文件。 |
| continuable | 设置为 True 或任何在 Python 中被视为 True 的值时,发生的失败被视为可继续的。 |
| fatal | 与 continuable 类似,但表示发生的失败是致命的。 |
不同的参数语法
Remote 库是一个动态库,一般来说它按照与其他动态库相同的规则处理不同的参数语法。这包括必需参数、默认值、可变参数(varargs),以及命名参数语法。
自由命名参数(**kwargs)的工作方式也与其他动态库基本相同。首先,get_keyword_arguments 必须返回一个包含 **kwargs 的参数规范,与其他动态库完全一样。主要的区别在于,远程服务器的 run_keyword 方法必须有一个可选的第三个参数来接收用户指定的 kwargs。第三个参数必须是可选的,因为出于向后兼容的原因,Remote 库只在测试数据中使用了 kwargs 时才将其传递给 run_keyword 方法。
实际上 run_keyword 应该类似于以下 Python 和 Java 示例,具体取决于语言如何处理可选参数。
def run_keyword(name, args, kwargs=None):
# ...
public Map run_keyword(String name, List args) {
// ...
}
public Map run_keyword(String name, List args, Map kwargs) {
// ...
}