Robot Framework 新版教程 - 解析器接口
(Robot Framework 7.x 教程, Part 21)
解析器接口
Robot Framework 支持外部解析器(Parser),可以处理自定义数据格式,甚至覆盖 Robot Framework 自身的解析器。
自定义解析器是 Robot Framework 6.1 中新增的功能。
启用解析器
解析器通过命令行的 --parser 选项启用,其语义与监听器完全相同。包括以名称或路径指定解析器、向解析器类传递参数等:
robot --parser MyParser tests.custom
robot --parser path/to/MyParser.py tests.custom
robot --parser Parser1:arg --parser Parser2:a1:a2 path/to/tests
解析器 API
解析器可以用模块或类的形式实现。本节说明它们必须包含哪些属性和方法。
EXTENSION 或 extension 属性
该属性指定解析器支持的一个或多个文件扩展名。EXTENSION 和 extension 两个名称都可以使用,如果两者同时存在,前者优先。属性的值可以是字符串,也可以是字符串序列。扩展名不区分大小写,可以带或不带前导点号。如果解析器以类的形式实现,可以将该属性设置为类属性或实例属性。
包含多个部分的扩展名也受支持,如 .example.ext 或 .robot.zip。
如果解析器支持
.robot扩展名,它将替代标准解析器来解析这些文件。
parse 方法
必需的 parse 方法负责解析套件文件。对于每个具有解析器所支持扩展名的被解析文件,都会调用该方法。方法必须返回一个 TestSuite 对象。
在简单情况下,parse 可以只接受一个参数,即指向待解析文件的 pathlib.Path 对象。如果解析器需要获取在上级套件初始化文件中设置的 Test Setup、Test Teardown、Test Tags 和 Test Timeout 的默认值,parse 方法必须接受两个参数。此时第二个参数是一个 TestDefaults 对象。
parse_init 方法
可选的 parse_init 方法负责解析套件初始化文件,即格式为 __init__.ext 的文件,其中 .ext 是解析器支持的扩展名。该方法必须返回一个 TestSuite 对象,代表整个目录。从子套件文件和子目录创建的套件将被添加到其子套件中。
parse_init 同样可以接受一个或两个参数,取决于它是否需要与测试相关的默认值。如果它接受默认值,可以修改传入的 TestDefaults 对象,修改结果在解析子套件文件时可见。
只有在解析器需要支持套件初始化文件时,才需要实现该方法。
可选基类
解析器不需要实现任何显式接口,但继承可选的 Parser 基类可能会有帮助。主要好处是基类提供了文档和类型提示,同时也充当了一个更正式的 API 规范。
示例
以模块形式实现的解析器
第一个示例展示了一个简单的以模块形式实现的解析器,它支持一个硬编码的扩展名。它只是创建一个虚拟套件,并不实际解析任何内容。
from robot.api import TestSuite
EXTENSION = '.example'
def parse(source):
suite = TestSuite(name='Example', source=source)
test = suite.tests.create(name='Test')
test.body.create_keyword(name='Log', args=['Hello!'])
return suite
以类形式实现的解析器
第二个解析器以类的形式实现,接受要使用的扩展名作为参数。解析器读取给定的源文件,并为其中的每一行创建一个虚拟测试。
from pathlib import Path
from robot.api import TestSuite
class ExampleParser:
def __init__(self, extension: str):
self.extension = extension
def parse(self, source: Path) -> TestSuite:
name = TestSuite.name_from_source(source, self.extension)
suite = TestSuite(name, source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line)
test.body.create_keyword(name='Log', args=['Hello!'])
return suite
继承可选基类的解析器
这个解析器继承了可选的 Parser 基类。它支持解析套件初始化文件,使用了 TestDefaults,并注册了多个扩展名。
from pathlib import Path
from robot.api import TestSuite
from robot.api.interfaces import Parser, TestDefaults
class ExampleParser(Parser):
extension = ('example', 'another')
def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""创建一个套件,并将初始化文件中可能的默认值设置到测试中。"""
suite = TestSuite(TestSuite.name_from_source(source), source=source)
for line in source.read_text().splitlines():
test = suite.tests.create(name=line, doc='Example')
test.body.create_keyword(name='Log', args=['Hello!'])
defaults.set_to(test)
return suite
def parse_init(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""创建一个虚拟套件并设置一些默认值。
该方法仅在存在具有受支持扩展名的初始化文件时才会被调用。
"""
defaults.tags = ('tags', 'from init')
defaults.setup = {'name': 'Log', 'args': ['Hello from init!']}
return TestSuite(TestSuite.name_from_source(source.parent), doc='Example',
source=source, metadata={'Example': 'Value'})
解析器作为预处理器
最后一个示例解析器充当 Robot Framework 数据文件的预处理器,除了标准的 *** Test Cases *** 格式外,还支持 === Test Cases === 格式的 header。在这种用法中,使用 TestSuite.from_string、TestSuite.from_model 和 TestSuite.from_file_system 工厂方法来构造返回的套件会很方便。
from pathlib import Path
from robot.running import TestDefaults, TestSuite
class RobotPreprocessor:
extension = '.robot'
def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
data = source.read_text()
for header in 'Settings', 'Variables', 'Test Cases', 'Keywords':
data = data.replace(f'=== {header} ===', f'*** {header} ***')
suite = TestSuite.from_string(data, defaults=defaults)
return suite.config(name=TestSuite.name_from_source(source), source=source)