本文系《pytest源码剖析》系列内容
29. reports
插件路径:_pytest.reports
实现的 hook
调用的 hook
pytest_report_teststatus
定义的 fixture
无
插件功能
定义用例收集结果类CollectReport
定义用例执行结果类TestReport
实现结果的序列化、反序列化方法
代码片段
class TestReport(BaseReport):
def __init__( self, nodeid: str, location: Tuple[str, Optional[int], str], keywords: Mapping[str, Any], outcome: "Literal['passed', 'failed', 'skipped']", longrepr: Union[ None,TerminalRepr], when: "Literal['setup', 'call', 'teardown']", sections: Iterable[Tuple[str, str]] = (), duration: float = 0, user_properties: Optional[Iterable[Tuple[str, object]]] = None, **extra, ) -> None: pass class CollectReport(BaseReport):
when = "collect"
def __init__( self, nodeid: str, outcome: "Literal['passed', 'failed', 'skipped']", longrepr: Union[ None,TerminalRepr], result: Optional[List[Union[Item, Collector]]], sections: Iterable[Tuple[str, str]] = (), **extra, ) -> None:
TestReport 和 CollectReport 继承了相同的父类
CollectReport 比 TestReport 少了以下属性:
location
keywords
duration
user_properties
简评
虽然这个插件的名字叫 reports,但它并不是测试报告,而是测试报告的结果
此外,生成 HTML 报告的第三方插件比如:
pytest-html
allure-pytest
也是从 reports 插件中的 TestRepor 读取结果、生成报告的
...
这个插件实现了两个序列化相关的 hook,但是搜遍 pytest 源码,也没有发现在哪里调用过
于是推测在分布式场景下,用来传输测试结果的。
转手检索了 pytest-xdist 源码,果然证实了这个猜测
...
runner 收集测试用例时,会调用钩子 pytest_make_collect_report,将其返回值正是 CollectReport 对象
def pytest_make_collect_report(collector: Collector) -> CollectReport: call = CallInfo.from_call(lambda: list(collector.collect()), "collect") ... rep = CollectReport(collector.nodeid, outcome, longrepr, result) rep.call = call return rep
实例化对象时传递了 4 个参数,实例化之后,又赋值了 1 个属性,分别是:
call:用例收集的调用对象,包含返回值、异常信息、开始时间、结束时间、耗时
nodeid:收集对象的 id
outcome:收集状态
passed:没有异常信息
skipped:出现 skip 异常:
failed:出现其他异常
longrepr:skip 信息或异常信息,passed 没有信息
result:call 的返回值,可能是包、文件、类、用例本身
...
runner 执行测试用例时,会调用钩子 pytest_runtest_makereport,将其返回值正是 TestReport 对象
def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: return TestReport.from_item_and_call(item, call)
调用 from_item_and_call 时传递了 2 个参数:
item:用例对象
call:用例执行的调用对象,包含返回值、异常信息、开始时间、结束时间、耗时
在 from_item_and_call 内会进行一系列的判断,构造合适的 TestReport 实例化参数
def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": ... return cls( item.nodeid, item.location, keywords, outcome, longrepr, when, sections, duration, user_properties=item.user_properties, )
其中:
item.nodeid:用例 id (唯一标识符)
item.location:用例位置(文件路径、行号、用例名)
keywords:用例关键字(目录名、文件名、用例名等)
outcome:执行状态(passed、failed、skipped 等)
longrepr:skip 信息或异常信息,passed 没有信息
when:执行阶段(setup、call、teardown)
sections:item._report_sections 中的信息
duration:执行耗时
user_properties:通过插件 junitxml 附加的信息
...
总结一下:
在pytest中runner 插件制作结果,reports 插件存储结果,tearminal 插件展示结果
领取专属 10元无门槛券
私享最新 技术干货