lyyyuna 的小花园

动静中之动, by

RSS

Robot Framework 新版教程 - 监听器接口

发表于 2026-04

监听器接口

Robot Framework 的监听器接口(Listener interface)提供了一种强大的机制,用于在执行过程中获取通知、检查和修改数据及结果。监听器在例如测试套件、测试用例和关键字开始和结束时被调用,在输出文件准备就绪时被调用,以及在整个执行结束时被调用。典型的使用场景包括:与外部测试管理系统通信、在测试失败时发送消息,以及在执行过程中修改测试。

监听器以类或模块的形式实现,其中包含特定的方法。监听器可以从命令行启用,也可以由库注册。前者在整个执行过程中处于活动状态,而后者仅在执行导入了注册它们的库的测试套件时才处于活动状态。

目前有两个受支持的监听器接口版本:监听器版本 2监听器版本 3。它们大部分方法相同,但这些方法被调用时传入的参数不同。较新的监听器版本 3 更强大,通常推荐使用。

监听器结构

监听器以模块或类的形式实现,与创建测试库类似。监听器可以实现特定名称的钩子方法,具体取决于它们对哪些事件感兴趣。例如,如果监听器想在测试开始时获取通知,可以实现 start_test 方法。如后续章节所述,不同的监听器版本拥有略有不同的可用方法集合,并且调用时传入的参数也不同。

# 以模块形式实现的监听器,使用监听器 API 版本 3。

def start_suite(data, result):
    print(f"Suite '{data.name}' starting.")

def end_test(data, result):
    print(f"Test '{result.name}' ended with status {result.status}.")

监听器不需要实现任何显式接口,只需实现所需的方法即可自动识别。不过,可以使用基类 robot.api.interfaces.ListenerV2robot.api.interfaces.ListenerV3 来在编辑器中获得方法名补全、类型提示等功能。

# 与上面的例子相同,但使用了可选的基类和类型提示。

from robot import result, running
from robot.api.interfaces import ListenerV3


class Example(ListenerV3):

    def start_suite(self, data: running.TestSuite, result: result.TestSuite):
        print(f"Suite '{data.name}' starting.")

    def end_test(self, data: running.TestCase, result: result.TestCase):
        print(f"Test '{result.name}' ended with status {result.status}.")

可选的监听器基类是 Robot Framework 6.1 中新增的。

除了使用 "snake case"(如 start_test)来命名监听器方法外,还可以使用 "camel case"(如 startTest)。这种支持是在 Robot Framework 可以运行在 Jython 上并使用 Java 实现监听器时添加的。为了向后兼容而保留了这种方式,但不推荐在新的监听器中使用。

监听器接口版本

目前有两个受支持的监听器接口版本,版本号分别为 2 和 3。监听器可以通过设置 ROBOT_LISTENER_API_VERSION 属性的值为 2 或 3 来指定使用哪个版本。从 Robot Framework 7.0 开始,如果未指定版本,默认使用监听器版本 3。

监听器版本 2监听器版本 3 大部分方法相同,但传递给这些方法的参数不同。传递给监听器版本 2 方法的参数是包含执行信息的字符串和字典。这些信息可以被检查和转发,但无法直接修改。监听器版本 3 的方法获取的是 Robot Framework 自身使用的模型对象,这些模型对象既可以被检查也可以被修改。

监听器版本 3 比旧版的监听器版本 2 更强大,通常推荐使用。

监听器版本 2

使用监听器 API 版本 2 的监听器在执行过程中会收到各种事件的通知,但它们无法访问实际执行的测试,因此不能直接影响执行或创建的结果。

监听器 API 版本 2 中的方法列在下面的表格中,也列在可选基类 ListenerV2 的 API 文档中。所有与测试执行进度相关的方法都具有相同的签名 method(name, attributes),其中 attributes 是一个包含事件详细信息的字典。监听器方法可以自由处理接收到的信息,但无法直接修改它。如果需要修改,可以改用监听器版本 3

监听器 API 2 中的方法

方法 参数 说明
start_suite name, attributes 当测试套件开始时调用。属性字典的内容:id:套件 id。顶层套件为 s1,其第一个子套件为 s1-s1,第二个子套件为 s1-s2,依此类推。longname:包含父套件的套件名称。doc:套件文档。metadata自由套件元数据,以字典形式提供。source:创建该套件的文件/目录的绝对路径。suites:该套件直接子套件的名称列表。tests:该套件中测试用例的名称列表,不包括子套件中的测试用例。totaltests:该套件及其所有子套件中测试用例的总数,以整数形式提供。starttime:套件执行开始时间。
end_suite name, attributes 当测试套件结束时调用。属性字典的内容:id:同 start_suitelongname:同 start_suitedoc:同 start_suitemetadata:同 start_suitesource:同 start_suitestarttime:同 start_suiteendtime:套件执行结束时间。elapsedtime:总执行时间(毫秒),以整数形式提供。status:套件状态,字符串 PASSFAILSKIPstatistics:套件统计信息(套件中通过和失败的测试数量),以字符串形式提供。message:如果套件的 setup 或 teardown 失败则为错误消息,否则为空。
start_test name, attributes 当测试用例开始时调用。属性字典的内容:id:测试 id,格式如 s1-s2-t2,开头部分为父套件 id,最后部分表示该套件中的测试索引。longname:包含父套件的测试名称。originalname:测试名称,其中可能的变量未解析。RF 3.2 中新增。doc:测试文档。tags:测试 tag,以字符串列表形式提供。template:用于测试的模板名称。如果测试未使用模板,则为空字符串。source:测试用例源文件的绝对路径。RF 4.0 中新增。lineno:测试在源文件中的起始行号。RF 3.2 中新增。starttime:测试执行开始时间。
end_test name, attributes 当测试用例结束时调用。属性字典的内容:id:同 start_testlongname:同 start_testoriginalname:同 start_testdoc:同 start_testtags:同 start_testtemplate:同 start_testsource:同 start_testlineno:同 start_teststarttime:同 start_testendtime:测试执行结束时间。elapsedtime:总执行时间(毫秒),以整数形式提供。status:测试状态,字符串 PASSFAILSKIPmessage:状态消息,通常为错误消息或空字符串。
start_keyword name, attributes 当关键字或控制结构(如 IF/ELSETRY/EXCEPT)开始时调用。对于关键字,name 是包含可能的库或资源名称前缀的完整关键字名称,如 MyLibrary.Example Keyword。对于控制结构,name 包含参数的字符串表示。关键字和控制结构共享大部分属性,但控制结构可能根据其 type 有额外的属性。共享属性: type:指定开始项类型的字符串。可能的值为:KEYWORDSETUPTEARDOWNFORWHILEITERATIONIFELSE IFELSETRYEXCEPTFINALLYVARRETURNBREAKCONTINUEERROR。所有类型值在 RF 4.0 中发生了变化,RF 5.0 中 FOR ITERATION 更改为 ITERATIONkwname:不带库或资源前缀的关键字名称。对于控制结构是参数的字符串表示。libname:关键字所属的库或资源文件名称。对于测试用例文件中的用户关键字和控制结构,为空字符串。doc:关键字文档。args:关键字参数,以字符串列表形式提供。assign:关键字返回值赋值的变量名列表。tags关键字 tag,以字符串列表形式提供。source:使用该关键字的文件的绝对路径。RF 4.0 中新增。lineno:使用该关键字的行号。通常为整数,但如果关键字由监听器执行,则可能为 None。RF 4.0 中新增。status:关键字初始状态。如果关键字不执行(例如由于先前的失败),则为 NOT RUN,否则为 NOT SET。RF 4.0 中新增。starttime:关键字执行开始时间。FOR 类型的额外属性: variables:每次循环迭代的赋值变量,以字符串列表形式提供。flavor:循环类型(例如 IN RANGE)。values:被遍历的值列表,以字符串列表形式提供。start:起始配置。仅用于 IN ENUMERATE 循环。RF 6.1 中新增。mode:模式配置。仅用于 IN ZIP 循环。RF 6.1 中新增。fill:填充值配置。仅用于 IN ZIP 循环。RF 6.1 中新增。FOR 循环的 ITERATION 类型的额外属性: variables:一次 FOR 循环迭代中变量及其内容的字符串表示,以字典形式提供。WHILE 类型的额外属性: condition:循环条件。limit:最大迭代限制。on_limit:超过限制时的行为。有效值为 passfail。RF 7.0 中新增。on_limit_message:达到 WHILE 循环限制时引发的自定义错误。RF 6.1 中新增。IFELSE IF 类型的额外属性: condition:正在求值的条件表达式。ELSE IF 的支持在 RF 6.1 中新增。EXCEPT 类型的额外属性: patterns:正在匹配的异常模式,以字符串列表形式提供。pattern_type:模式匹配的类型(例如 GLOB)。variable:包含捕获异常的变量。RETURN 类型的额外属性: values:从关键字返回的值,以字符串列表形式提供。VAR 类型的额外属性: name:变量名。value:变量值。标量变量为字符串,其他情况为列表。scope:变量作用域(例如 GLOBAL),以字符串形式提供。控制结构的额外属性通常在 RF 6.0 中新增。VAR 在 RF 7.0 中新增。
end_keyword name, attributes 当关键字或控制结构结束时调用。name 是包含可能的库或资源名称前缀的完整关键字名称,例如 MyLibrary.Example Keyword。控制结构有额外的属性,这些属性根据 type 属性而变化。所有可能属性的描述请参阅 start_keyword 部分。属性字典的内容:type:同 start_keywordkwname:同 start_keywordlibname:同 start_keyworddoc:同 start_keywordargs:同 start_keywordassign:同 start_keywordtags:同 start_keywordsource:同 start_keywordlineno:同 start_keywordstarttime:同 start_keywordendtime:关键字执行结束时间。elapsedtime:总执行时间(毫秒),以整数形式提供。status:关键字状态,字符串 PASSFAILSKIPNOT RUNSKIPNOT RUN 在 RF 4.0 中新增。
log_message message 当执行的关键字写入日志消息时调用。message 是一个字典,包含以下内容:message:消息内容。level:记录消息时使用的日志级别timestamp:消息创建时间,格式为 YYYY-MM-DD hh:mm:ss.milhtml:字符串 yesno,表示是否应将消息解释为 HTML。如果消息级别低于当前的阈值级别,则不会调用此方法。
message message 当框架自身写入 syslog 消息时调用。message 是与 log_message 方法中内容相同的字典。
library_import name, attributes 当库被导入时调用。name 是导入的库的名称。如果在使用 AS 导入时给库指定了自定义名称,则 name 是指定的别名。属性字典的内容:args:传递给库的参数,以列表形式提供。originalname:如果使用 AS 给库设置了别名,则为原始库名称,否则同 namesource:库源文件的绝对路径。如果获取库的源文件失败,则为空字符串。importer:导入该库的文件的绝对路径。导入 BuiltIn 以及使用 Import Library 关键字时为 None
resource_import name, attributes 当资源文件被导入时调用。name 是导入的资源文件名称(不含文件扩展名)。属性字典的内容:source:导入的资源文件的绝对路径。importer:导入该资源文件的文件的绝对路径。使用 Import Resource 关键字时为 None
variables_import name, attributes 当变量文件被导入时调用。name 是导入的变量文件名称(含文件扩展名)。属性字典的内容:args:传递给变量文件的参数,以列表形式提供。source:导入的变量文件的绝对路径。importer:导入该变量文件的文件的绝对路径。使用 Import Variables 关键字时为 None
output_file path 当写入输出文件完成时调用。path 是文件的绝对路径(字符串形式),如果禁用了创建输出文件,则为字符串 None
log_file path 当写入日志文件完成时调用。path 是文件的绝对路径(字符串形式)。如果禁用了创建日志文件,则不调用此方法。
report_file path 当写入报告文件完成时调用。path 是文件的绝对路径(字符串形式)。如果禁用了创建报告文件,则不调用此方法。
xunit_file path 当写入 xunit 文件完成时调用。path 是文件的绝对路径(字符串形式)。仅在启用了创建 xunit 文件时才调用。
debug_file path 当写入调试文件完成时调用。path 是文件的绝对路径(字符串形式)。仅在启用了创建调试文件时才调用。
close 当整个测试执行结束时调用。对于库监听器,当库超出作用域时调用。

监听器版本 3

监听器版本 3 与监听器版本 2 大部分方法相同,但与测试执行相关的方法参数不同。这些方法获取的是 Robot Framework 自身使用的实际运行和结果模型对象,监听器既可以查询所需信息,也可以即时修改模型对象。

对数据的修改也可以通过预执行修改器来完成。使用监听器的主要好处是可以根据执行过程中发生的情况动态地进行修改。另一个区别是,与选择测试用例相关的命令行选项会影响预执行修改器添加的测试,但不影响监听器添加的测试。

监听器版本 3 在 Robot Framework 7.0 中得到了大幅增强,增加了与关键字和控制结构相关的方法。在 Robot Framework 7.1 中进一步增强,增加了与库、资源文件和变量文件导入相关的方法。

监听器版本 3 对库关键字、用户关键字和所有控制结构有单独的方法。如果需要监听所有与关键字相关的事件,可以实现 start_keywordend_keyword。除此之外,还可以实现 start_body_itemend_body_item 来获取所有关键字和控制结构相关的通知。如果实现了更具体的方法(如 start_library_keywordend_if),则不会调用这些更高层级的监听器方法。

监听器 API 版本 3 中的方法列在下面的表格中,也列在可选基类 ListenerV3 的 API 文档中。

监听器 API 3 中的方法

方法 参数 说明
start_suite data, result 当测试套件开始时调用。dataresult 分别是代表被执行的测试套件其执行结果的模型对象。
end_suite data, result 当测试套件结束时调用。参数同 start_suite
start_test data, result 当测试用例开始时调用。dataresult 分别是代表被执行的测试用例其执行结果的模型对象。
end_test data, result 当测试用例结束时调用。参数同 start_test
start_keyword data, result 当关键字开始时调用。dataresult 分别是代表被执行的关键字调用其执行结果的模型对象。默认情况下,此方法在用户关键字、库关键字和无效关键字调用时被调用。如果实现了更具体的 start_user_keywordstart_library_keywordstart_invalid_keyword 方法,则不会调用此方法。
end_keyword data, result 当关键字结束时调用。参数和其他语义同 start_keyword
start_user_keyword data, implementation, result 当用户关键字开始时调用。dataresultstart_keywordimplementation 是实际执行的用户关键字。如果实现了此方法,则用户关键字不会调用 start_keyword
end_user_keyword data, implementation, result 当用户关键字结束时调用。参数和其他语义同 start_user_keyword
start_library_keyword data, implementation, result 当库关键字开始时调用。dataresultstart_keywordimplementation 代表执行的库关键字。如果实现了此方法,则库关键字不会调用 start_keyword
end_library_keyword data, implementation, result 当库关键字结束时调用。参数和其他语义同 start_library_keyword
start_invalid_keyword data, implementation, result 当无效的关键字调用开始时调用。dataresultstart_keywordimplementation 代表无效的关键字调用。可能是关键字未找到、有多个匹配,或者关键字调用本身无效。如果实现了此方法,则无效的关键字调用不会调用 start_keyword
end_invalid_keyword data, implementation, result 当无效的关键字调用结束时调用。参数和其他语义同 start_invalid_keyword
start_for, start_for_iteration, start_while, start_while_iteration, start_if, start_if_branch, start_try, start_try_branch, start_group, start_var, start_continue, start_break, start_return data, result 当控制结构开始时调用。更多信息请参阅可选基类 ListenerV3 的文档和类型提示。
end_for, end_for_iteration, end_while, end_while_iteration, end_if, end_if_branch, end_try, end_try_branch, end_group, end_var, end_continue, end_break, end_return data, result 当控制结构结束时调用。更多信息请参阅可选基类 ListenerV3 的文档和类型提示。
start_error data, result 当无效语法开始时调用。
end_error data, result 当无效语法结束时调用。
start_body_item data, result 当关键字或控制结构开始时调用,除非实现了更具体的方法(如 start_keywordstart_if)。
end_body_item data, result 当关键字或控制结构结束时调用,除非实现了更具体的方法(如 end_keywordend_if)。
log_message message 当执行的关键字写入日志消息时调用。message 是代表已记录消息的模型对象。如果消息级别低于当前的阈值级别,则不会调用此方法。
message message 当框架自身写入 syslog 消息时调用。messagelog_message 中的对象相同。
library_import library, importer 当库被导入后调用。library 代表导入的库,可以被检查和修改。importer 包含库被导入位置的信息。
resource_import resource, importer 当资源文件被导入后调用。resource 代表导入的资源文件,可以被检查和修改。importer 包含资源文件被导入位置的信息。
variables_import attrs, importer 当变量文件被导入后调用。attrs 以字典形式包含导入的变量文件的信息,可以被检查,但修改不会产生效果。importer 包含变量文件被导入位置的信息。此方法将来会被修改,attrs 字典将被替换为代表导入的变量文件的对象。
output_file path 当写入输出文件完成时调用。path 是文件的绝对路径(pathlib.Path 对象),如果禁用了创建输出文件,则为 None 对象。
log_file path 当写入日志文件完成时调用。path 是文件的绝对路径(pathlib.Path 对象)。如果禁用了创建日志文件,则不调用此方法。
report_file path 当写入报告文件完成时调用。path 是文件的绝对路径(pathlib.Path 对象)。如果禁用了创建报告文件,则不调用此方法。
xunit_file path 当写入 xunit 文件完成时调用。path 是文件的绝对路径(pathlib.Path 对象)。仅在启用了创建 xunit 文件时才调用。
debug_file path 当写入调试文件完成时调用。path 是文件的绝对路径(pathlib.Path 对象)。仅在启用了创建调试文件时才调用。
close 当整个测试执行结束时调用。对于库监听器,当库超出作用域时调用。

与关键字和控制结构相关的方法在 Robot Framework 7.0 中新增。

与库、资源文件和变量文件导入相关的方法在 Robot Framework 7.1 中新增。

在 Robot Framework 7.0 之前,传递给结果文件相关的监听器版本 3 方法的路径是字符串。

启用监听器

从命令行注册监听器

需要在整个执行过程中处于活动状态的监听器必须从命令行启用。这通过 --listener 选项完成,将监听器的名称作为参数传递给它。监听器名称取自实现监听器的类或模块的名称,与库名称取自实现库的类或模块的方式相同。指定的监听器必须位于导入测试库时搜索的模块搜索路径中。除了通过名称注册监听器外,还可以提供监听器文件的绝对路径或相对路径,与测试库的方式类似。可以通过多次使用此选项来启用多个监听器:

robot --listener MyListener tests.robot
robot --listener path/to/MyListener.py tests.robot
robot --listener module.Listener --listener AnotherListener tests.robot

也可以从命令行向监听器类传递参数。参数在监听器名称(或路径)之后使用冒号(:)作为分隔符指定。如果监听器以 Windows 绝对路径形式给出,驱动器盘符后的冒号不会被视为分隔符。此外,可以使用分号(;)作为替代的参数分隔符。这在监听器参数本身包含冒号时很有用,但在类 UNIX 操作系统上需要用引号将整个值括起来:

robot --listener listener.py:arg1:arg2 tests.robot
robot --listener "listener.py;arg:with:colons" tests.robot
robot --listener c:\path\listener.py;d:\first\arg;e:\second\arg tests.robot

除了逐个传递位置参数外,还可以使用命名参数语法传递参数,与使用关键字时的方式相同:

robot --listener listener.py:name=value tests.robot
robot --listener "listener.py;name=value:with:colons;second=argument" tests.robot

监听器参数会基于类型提示默认值,使用与关键字相同的规则进行自动转换。例如,这个监听器

class Listener:

    def __init__(self, port: int, log=True):
        self.port = post
        self.log = log

可以这样使用:

robot --listener Listener:8270:false

第一个参数会基于类型提示转换为整数,第二个参数会基于默认值转换为布尔值。

命名参数语法和参数转换在 Robot Framework 4.0 中新增。

库作为监听器

有时测试库获取测试执行的通知也是有用的。这使得它们可以在测试套件或整个测试执行结束时自动执行某些清理活动。

注册监听器

测试库可以通过使用 ROBOT_LIBRARY_LISTENER 属性来注册监听器。该属性的值应该是要使用的监听器的实例。它可以是一个完全独立的监听器,也可以是库本身充当监听器。在后一种情况下,为了避免监听器方法被暴露为关键字,可以在方法名前加上下划线前缀。例如,可以使用 _end_suite 而不是 end_suite

以下示例分别展示了使用外部监听器和库本身作为监听器:

from listener import Listener


class LibraryWithExternalListener:
    ROBOT_LIBRARY_SCOPE = 'GLOBAL'
    ROBOT_LIBRARY_LISTENER = Listener()

    def example_keyword(self):
         ...
class LibraryItselfAsListener:
    ROBOT_LIBRARY_SCOPE = 'SUITE'
    ROBOT_LISTENER_API_VERSION = 2

    def __init__(self):
        self.ROBOT_LIBRARY_LISTENER = self

    # 使用 '_' 前缀以避免监听器方法变成关键字。
    def _end_suite(self, name, attrs):
        print(f"Suite '{name}' ending with status {attrs['id']}.")

    def example_keyword(self):
         ...

如上面第二个例子所示,库监听器可以通过 ROBOT_LISTENER_API_VERSION 属性指定监听器接口版本,与其他监听器完全相同。

从 Robot Framework 7.0 开始,监听器也可以通过使用字符串 SELF(不区分大小写)来将自身注册为监听器。这在使用 @library 装饰器时特别方便:

from robot.api.deco import keyword, library


@library(scope='SUITE', listener='SELF')
class LibraryItselfAsListener:

    # 未指定监听器版本,因此默认使用监听器版本 3。
    # 使用 @library 装饰器时,关键字必须使用 @keyword 装饰器,
    # 所以这里不需要使用 '_' 前缀。
    def end_suite(self, data, result):
        print(f"Suite '{data.name}' ending with status {result.status}.")

    @keyword
    def example_keyword(self):
         ...

也可以通过将 ROBOT_LIBRARY_LISTENER 的值设为列表来为单个库指定多个监听器:

from listeners import Listener1, Listener2, Listener3


class LibraryWithMultipleListeners:
    ROBOT_LIBRARY_LISTENER = [Listener1(), Listener2(), Listener3()]

    def example_keyword(self):
         ...

被调用的监听器方法

库监听器会收到导入了使用它们的库的测试套件中所有事件的通知。实际上这意味着与套件、测试、关键字、控制结构和日志消息相关的方法都会被调用。除此之外,close 方法会在库超出作用域时被调用。

如果库在每次实例化时都创建一个新的监听器实例,则实际使用的监听器实例会根据库作用域而改变。

监听器调用顺序

默认情况下,监听器按照启用的顺序被调用,从命令行注册的监听器在库监听器之前被调用。不过,可以通过将特殊的 ROBOT_LISTENER_PRIORITY 属性设置为整数或浮点值来控制调用顺序。数值越大,监听器的优先级越高,越早被调用。该数值可以是正数或负数,默认为零。

自定义顺序不影响库监听器的 close 方法。该方法始终在库超出其作用域时被调用。

控制监听器调用顺序在 Robot Framework 7.1 中新增。

监听器示例

本节包含使用监听器接口的示例。前面的示例展示了在执行过程中获取通知,后面的示例展示了修改执行的测试和创建的结果。

获取信息

第一个示例以 Python 模块的形式实现。它使用监听器版本 2,但同样可以使用监听器版本 3 来实现。

"""在测试失败时停止执行的监听器。"""

ROBOT_LISTENER_API_VERSION = 2

def end_test(name, attrs):
    if attrs['status'] == 'FAIL':
        print(f"Test '{name}'" failed: {attrs['message']}")
        input("Press enter to continue.")

如果将上面的示例保存为 PauseExecution.py 文件,可以像这样从命令行使用:

robot --listener path/to/PauseExecution.py tests.robot

下一个示例仍然使用监听器版本 2,稍微复杂一些。它将接收到的所有信息写入临时目录中的一个文本文件,没有太多格式化。文件名可以从命令行指定,也有默认值。在实际使用中,通过命令行选项 --debugfile 提供的调试文件功能可能比这个示例更有用。

import os.path
import tempfile


class Example:
    ROBOT_LISTENER_API_VERSION = 2

    def __init__(self, file_name='listen.txt'):
        path = os.path.join(tempfile.gettempdir(), file_name)
        self.file = open(path, 'w')

    def start_suite(self, name, attrs):
        self.file.write("%s '%s'\n" % (name, attrs['doc']))

    def start_test(self, name, attrs):
        tags = ' '.join(attrs['tags'])
        self.file.write("- %s '%s' [ %s ] :: " % (name, attrs['doc'], tags))

    def end_test(self, name, attrs):
        if attrs['status'] == 'PASS':
            self.file.write('PASS\n')
        else:
            self.file.write('FAIL: %s\n' % attrs['message'])

    def end_suite(self, name, attrs):
         self.file.write('%s\n%s\n' % (attrs['status'], attrs['message']))

    def close(self):
         self.file.close()

修改数据和结果

以下示例展示了如何修改执行的测试和套件以及执行结果。所有这些示例都需要使用监听器版本 3

修改执行的套件和测试

修改执行内容就像修改传递给监听器方法的代表执行数据的模型对象一样简单。下面的示例展示了向每个执行的套件添加一个新测试,以及向每个测试添加一个新的关键字调用。

def start_suite(data, result):
    data.tests.create(name='New test')

def start_test(data, result):
    data.body.create_keyword(name='Log', args=['Keyword added by listener!'])

这个 API 与预执行修改器 API 非常相似,后者可以在整个测试执行开始之前修改套件和测试。使用监听器 API 的主要好处是可以根据执行结果或其他条件动态地进行修改。这为基于模型的测试等提供了有趣的可能性。

虽然监听器接口不像预执行修改器 API 那样构建在 Robot Framework 内部的访问者接口之上,但监听器仍然可以自行使用访问者接口。例如,预执行修改器示例中使用的 SelectEveryXthTest 访问者可以这样使用:

from SelectEveryXthTest import SelectEveryXthTest


def start_suite(suite, result):
    selector = SelectEveryXthTest(x=2)
    suite.visit(selector)

访问库或资源文件

可以获取关于实际执行的关键字及其所属的库或资源文件的更多信息:

from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult


def start_library_keyword(data: KeywordData,
                          implementation: LibraryKeyword,
                          result: KeywordResult):
    library = implementation.owner
    print(f"Keyword '{implementation.name}' is implemented in library "
          f"'{library.name}' at '{implementation.source}' on line "
          f"{implementation.lineno}. The library has {library.scope.name} "
          f"scope and the current instance is {library.instance}.")

如上面的示例所示,可以获取实际的库实例。这意味着监听器可以检查库状态,也可以修改它。对于用户关键字,甚至可以修改关键字本身,或者通过 owner 资源文件修改资源文件中的任何其他关键字。

修改结果

测试执行结果可以通过修改传递给监听器方法的结果对象来改变。以下监听器以类的形式实现,并使用了类型提示:

from robot import result, running


class ResultModifier:

    def __init__(self, max_seconds: float = 10.0):
        self.max_seconds = max_seconds

    def start_suite(self, data: running.TestSuite, result: result.TestSuite):
        result.doc = 'Documentation set by listener.'
        # 此时关于测试的信息只能通过 data 获取。
        smoke_tests = [test for test in data.tests if 'smoke' in test.tags]
        result.metadata['Smoke tests'] = len(smoke_tests)

    def end_test(self, data: running.TestCase, result: result.TestCase):
        elapsed_seconds = result.elapsed_time.total_seconds()
        if result.status == 'PASS' and elapsed_seconds > self.max_seconds:
            result.status = 'FAIL'
            result.message = 'Test execution took too long.'

    def log_message(self, msg: result.Message):
        if msg.level == 'WARN' and not msg.html:
            msg.message = f'<b style="font-size: 1.5em">{msg.message}</b>'
            msg.html = True
        if self._message_is_not_relevant(msg.message):
            msg.message = None

    def _message_is_not_relevant(self, message: str) -> bool:
        ...

一个限制是无法修改当前测试套件或测试用例的名称,因为在监听器被调用时它已经被写入了 output.xml 文件。出于同样的原因,在 end_suite 方法中修改已经完成的测试也不会产生效果。

在修改日志消息时,可以通过将 message 设置为 None 来完全移除消息,如上面的示例所示。这可以用于移除敏感或不相关的消息,使其在日志文件中不可见。

这个 API 与预 Rebot 修改器 API 非常相似,后者可以在生成报告和日志之前修改结果。主要区别是监听器还会修改创建的 output.xml 文件。

通过将消息设置为 None 来完全移除消息在 Robot Framework 7.2 中新增。

改变关键字和控制结构的状态

监听器还可以通过改变执行的关键字和控制结构的状态来影响执行流程。例如,如果监听器将一个通过的关键字的状态改为 FAIL,该关键字将被视为失败,就像它正常失败一样。类似地,可以将通过或失败的关键字的状态改为 SKIP,以使该关键字和整个测试被跳过。也可以通过将状态改为 PASS 来消除失败,但这应该仅在特殊情况下谨慎使用,以避免隐藏真正的失败。

以下示例通过使执行时间过长的关键字失败来演示如何改变状态。前面的示例对测试有类似的逻辑,但这个监听器在遇到太慢的关键字时还会立即停止执行。如示例所示,监听器不仅可以改变状态,还可以改变错误消息。

from robot import result, running


class KeywordPerformanceMonitor:

    def __init__(self, max_seconds: float = 0.1):
        self.max_seconds = max_seconds

    def end_keyword(self, data: running.Keyword, result: result.Keyword):
        elapsed_seconds = result.elapsed_time.total_seconds()
        if result.status == 'PASS' and elapsed_seconds > self.max_seconds:
            result.status = 'FAIL'
            result.message = 'Keyword execution took too long.'

状态的改变从 Robot Framework 7.1 开始才会影响执行流程。

更多示例

关键字和控制结构相关的监听器版本 3 方法功能非常丰富,在用户指南中无法完全覆盖。更多示例可以参阅以各种方式使用这些方法的验收测试

lyyyuna 沪ICP备2025110782号-1