前言
在pytest-rerunfailures:优化测试稳定性的失败重试工具这篇文章中,我们查看源码时,看到了这样一行代码reports = runtestprotocol(item, nextitem=nextitem, log=False)
,runtestprotocol
这个函数是干嘛用的呢?我们带着疑问一起探索。
runtestprotocol 函数
runtestprotocol
是 pytest 执行测试流程中的一个核心函数,它主要负责调用测试函数的"setup"、"call"和"teardown"钩子函数,并生成对应的测试报告。
runtestprotocol
函数的基本信息
runtestprotocol
函数是 pytest 测试框架中用于执行测试用例的核心函数之一,它位于 pytest 的 runner.py
模块中,定义如下:
python
def runtestprotocol(
item: Item, log: bool = True, nextitem: Optional[Item] = None
) -> List[TestReport]:
hasrequest = hasattr(item, "_request")
其中,该函数的参数包括:
item
: 表示待执行的测试用例对象;log
: 控制是否打印日志,在默认情况下为 True,即打印日志;nextitem
: 表示下一个待执行的测试用例对象,用于实现测试顺序的控制。
函数返回一个包含 pytest.TestReport
对象的列表,每个对象表示执行一个钩子函数的测试结果。
参数解析
在上述参数中,item
是必选参数,表示待执行的测试用例对象。该参数通常由 pytest 测试框架提供,我们无需手动传入该参数。
log
参数是一个布尔值,用于控制是否打印日志。在默认情况下,该参数为 True,即会打印日志。如果你希望在测试过程中关闭日志,可以将该参数设置为 False。
nextitem
参数则表示下一个待执行的测试用例对象,用于实现测试顺序的控制。当 runtestprotocol
函数执行完当前测试用例之后,它会自动执行下一个测试用例。如果希望手动控制下一个待执行的测试用例,可以使用该参数。
源码解读
下面是对代码的解释:
python
def runtestprotocol(
item: Item, log: bool = True, nextitem: Optional[Item] = None
) -> List[TestReport]:
hasrequest = hasattr(item, "_request")
if hasrequest and not item._request: # type: ignore[attr-defined]
# This only happens if the item is re-run, as is done by
# pytest-rerunfailures.
item._initrequest() # type: ignore[attr-defined]
首先,我们声明了 runtestprotocol
函数,并接受三个参数:item
、log
和 nextitem
。其中,item
是一个测试项对象,log
是一个布尔值,表示是否将测试结果记录到日志中,nextitem
表示下一个将要被执行的测试项(可选)。
然后,我们使用 hasattr(item, "_request")
判断 item
对象是否包含 _request
属性。如果包含,则我们检查该属性的值是否为 False
,如果是,则执行 item._initrequest()
方法。这段代码主要是为了处理一种特殊情况:当测试项重新运行(例如通过 pytest-rerunfailures
插件),可能需要重新初始化 _request
属性。
接下来,我们调用 call_and_report
函数执行 setup
钩子函数,并将其结果存储在 rep
变量中:
ini
rep = call_and_report(item, "setup", log)
call_and_report
函数是一个辅助函数,用于执行钩子函数并生成测试报告(TestReport
对象)。在这里,我们调用该函数来执行 setup
钩子函数,并将结果存储在 rep
变量中。
然后,我们将 rep
添加到 reports
列表中:
ini
reports = [rep]
reports
列表用于存储测试过程中生成的所有测试报告。
接下来,我们判断上一个钩子函数是否执行成功。如果成功,则继续执行下一个钩子函数;否则,直接跳过:
css
if rep.passed:
if item.config.getoption("setupshow", False):
show_test_item(item)
if not item.config.getoption("setuponly", False):
reports.append(call_and_report(item, "call", log))
首先,我们使用 rep.passed
来判断上一个钩子函数是否执行成功。如果成功,则判断是否需要在控制台显示该测试项的详细信息,如果需要,则调用 show_test_item
函数显示详细信息。然后,我们再次使用 call_and_report
来执行测试函数并生成测试报告,并将其添加到 reports
列表中。
最后,我们再次使用 call_and_report
函数执行 teardown
钩子函数,并将其结果存储在 rep
变量中:
bash
reports.append(call_and_report(item, "teardown", log, nextitem=nextitem))
然后,我们将 rep
添加到 reports
列表中,并返回该列表作为测试结果。
kotlin
return reports
当所有测试项执行完成后,pytest 会将测试结果打印到控制台,并将其写入日志文件。这样,我们就可以轻松地进行测试并获取测试报告。
最后
总之,runtestprotocol
函数是 pytest 测试框架中非常重要的一个函数,它用于执行测试用例并触发 pytest 钩子函数。当然我们实际场景中通常不会直接调用该函数,这里主要做一个了解,方便查看插件的源码时遇到不知道是干啥的。