【pytest官方文档】解读- 插件开发之hooks 函数(钩子)

简介: 【pytest官方文档】解读- 插件开发之hooks 函数(钩子)

上一节讲到如何安装和使用第三方插件,用法很简单。接下来解读下如何自己开发pytest插件。


但是,由于一个插件包含一个或多个钩子函数开发而来,所以在具体开发插件之前还需要先学习hooks函数


一、什么是 hooks 函数


简单来说,在 pytest 的代码中,预留出了一些函数供我们修改,以便来改变pytest工作方式,这些函数就是hooks函数,我们可以直接重写函数里的内容。


比如,在 pytest代码路径\Lib\site-packages\_pytest\hookspec.py中,可以看到 pytest 定义好的 hook 规范,方便我们在开发插件的时候参考规范来调用对应的hooks函数。


1268169-20221011203406416-1798893598.png


二、hooks 函数的分类


hooks函数的职责分类来看,大概如下几类:


  • Bootstrapping hooks:引导类钩子,用来调用已经早就注册好的内部插件第三方插件
  • Collection hooks:集合类钩子,pytest 调用集合钩子来收集文件和目录。
  • Test running (runtest) hooks:测试运行相关的钩子,所有与测试运行相关的钩子都接收一个pytest.Item对象。
  • Reporting hooks:与Session 会话相关的钩子。
  • Debugging/Interaction hooks:调试/交互钩子,少有的可以用于特殊的报告或与异常交互的钩子函数。


可供调用的钩子函数有很多,功能也是各式各样的,有兴趣的童鞋可以进一步细看官方文档里的介绍。我们就是要通过不同钩子函数具备的功能,来实现我们自定义的需求。


三、编写 hooks 函数开发本地插件


写一个插件示例。


比如我们平时执行case的时候,一通跑完可能会出现不少失败的case,那通常我可能就会翻控制台的输出来找出哪些case失败了。


但是控制台里输出的信息有很多,于是乎我想直接把测试失败的case信息存到一个本地文件里,我直接打开就可以看到所有失败的case。


先写一个case文件里的建议测试用例:


# content of mytest/tests.py
def test_failed():
    assert False
def test_passed():
    assert True
def test_failed2():
    assert False


然后再同级目录下创建一个conftest文件,之前聊fixture时候就说过,conftest里的内容就是本地插件了。


先直接放上插件代码:


# content of mytest/conftest.py
import pytest
from pathlib import Path
from _pytest.main import Session
from _pytest.nodes import Item
from _pytest.runner import CallInfo
from _pytest.terminal import TerminalReporter
FAILURES_FILE = Path() / "failures.txt"
@pytest.hookimpl()
def pytest_sessionstart(session: Session):
    print("Hello 把苹果咬哭")
    if FAILURES_FILE.exists():
        FAILURES_FILE.unlink()
    FAILURES_FILE.touch()
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item: Item, call: CallInfo):
    outcome = yield
    result = outcome.get_result()
    if result.when == "call" and result.failed:
        try:
            with open(str(FAILURES_FILE), "a") as f:
                f.write(result.nodeid + "\n")
        except Exception as e:
            print("ERROR", e)
            pass


解析


1. 重写钩子函数


首先,关于pathlib模块就是用来做一些路径操作的库,因为我要在本地路径中进行文件相关操作。


def pytest_sessionstart()中做的事情就是先看下本地是否存在这个名字叫failures.txt的文件,有的话就删除,没有就新建。


为啥用pytest_sessionstart这个hook函数,因为通过查看官方API文档里的介绍,发现这个钩子函数是在创建Session对象之后,且在执行收集和进入运行测试循环之前调用,所以很适合用在这里。


所以直接重写这个hook函数来实现我们定义的功能。


2. hook函数中的 firstresult


示例中使用hook函数pytest_runtest_makereport,同样通过查看官方API介绍,它的作用是为测试用例的每个setup运行tearDown阶段创建TestReport。而插件要做的事情,就是要在


用例执行后获取到状态,若是失败就存放到本地txt文件。


当查看hook规范时候,发现一个装饰器参数firstresult=True


1268169-20221013195737846-819755231.png


由于在大多数情况下,调用hook函数可能还会触发调用多个hook,所以最后的结果会是包含所调用钩子函数的非none结果


firstresult=True时,调用钩子函数时只要有第一个返回非none结果,就会将该结果作为整个钩子调用的结果。在这种情况下,将不会调用其余钩子函数。


3. hook函数中的 hookwrapper


回到插件代码本身,也用到了一个参数hookwrapper=True


1268169-20221013200339760-1594545452.png


默认情况下,我们之间重写hook函数来彻底改变它要做的事情,就像插件代码里第一个hook函数pytest_sessionstart一样。


hookwrapper=True时,等于是我们实现了一个hook函数的包装器。钩子包装器是一个生成器函数,它只产生一次。


当 pytest 调用钩子时,首先执行钩子包装器,并像常规钩子一样传递相同的参数。


yield关键字大家都熟悉了,当代码执行到这里的时候会暂停一下,继续执行下一个钩子,并且会把所有的结果或者异常封装成一个result对象返回到yield这里。


钩子包装器本身并不返回结果,只是在实际的钩子实现的外面做一些其他的事情。


我们的插件功能其实也并不是要修改这个钩子本身测试报告的内容,所以就直接通过hookwrapper=True将我们的pytest_runtest_makereport写成一个包装好的钩子。


接下来就是具体功能的代码,判断当用例测试结果是fail,就写到本地文件中。


运行


运行一下测试用例,看下我们插件的执行情况。


1268169-20221013202739195-1666297591.png


查看下failures.txt内容,结果正确。


1268169-20221013202818487-1815708584.png


四、钩子函数排序/调用示例


存在这样的情况,对于同一个钩子规范,可能会存在多个实现。这种情况下可以使用参数tryfirsttrylast来影响钩子的调用顺序。


# Plugin 1
@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(items):
    # 尽可能早的执行
    ...
# Plugin 2
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(items):
    # 尽可能晚的执行
    ...
# Plugin 3
@pytest.hookimpl(hookwrapper=True)
def pytest_collection_modifyitems(items):
    # 会在上面的 tryfirst 之前执行
    outcome = yield
    # 在执行所有非钩子包装器之后执行


具体执行顺序如下:


  1. Plugin3pytest_collection_modifyitems一直调用到yield,因为它是一个钩子包装器。
  2. Plugin1pytest_collection_modifyitems被调用,因为它被标记为tryfirst=True
  3. Plugin2pytest_collection_modifyitems被调用,因为它被标记为trylast=True(但即使没有这个标记,它也会在Plugin1之后)。
  4. Plugin3pytest_collection_modifyitems继续在yield执行代码,yield接收一个Result实例。


关于hook本篇先到此,剩下的内容另起篇幅了。


最后,闻道有先后,文章有遗漏,欢迎交流。

相关文章
|
SQL 关系型数据库 MySQL
六、SQL多表查询
六、SQL多表查询
507 0
|
5月前
|
JSON 监控 API
拼多多API接口的应用场景介绍
拼多多API是开放平台提供的标准化接口,基于RESTful架构,支持商品管理、订单处理、用户分析与数据报表等自动化场景。通过OAuth 2.0认证和JSON数据格式,助力开发者高效集成电商功能,提升运营效率与业务洞察力。(238字)
972 2
|
6月前
|
人工智能 JSON 测试技术
Dify入门实战:5分钟搭建你的第一个AI测试用例生成器
本文教你利用Dify平台,结合大模型API,5分钟内搭建一个无需编程基础的AI测试用例生成器。通过配置提示词、连接AI模型,实现自动输出覆盖正常、异常及边界场景的结构化测试用例,提升测试效率与质量,并支持集成到CI/CD流程,助力智能化测试落地。
|
Web App开发 测试技术 持续交付
自动化测试的利器:Selenium与Python的完美结合
【9月更文挑战第21天】在软件开发的世界里,测试是确保产品质量的关键步骤。随着敏捷开发和持续集成的流行,自动化测试工具变得尤为重要。本文将介绍如何使用Selenium和Python进行高效的自动化测试,不仅提供代码示例,还深入探讨如何设计测试用例、选择正确的测试框架、以及如何整合到CI/CD流程中。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧。
346 3
|
jenkins 测试技术 Shell
利用Apipost轻松实现用户充值系统的API自动化测试
API在现代软件开发中扮演着连接不同系统与模块的关键角色,其测试的重要性日益凸显。传统API测试面临效率低、覆盖率不足及难以融入自动化工作流等问题。Apipost提供了一站式API自动化测试解决方案,支持零代码拖拽编排、全场景覆盖,并可无缝集成CI/CD流程。通过可视化界面,研发与测试人员可基于同一数据源协作,大幅提升效率。同时,Apipost支持动态数据提取、性能压测等功能,满足复杂测试需求。文档还以用户充值系统为例,详细介绍了从创建测试用例到生成报告的全流程,帮助用户快速上手并提升测试质量。
1141 18
|
存储 测试技术 API
pytest接口自动化测试框架搭建
通过上述步骤,我们成功搭建了一个基于 `pytest`的接口自动化测试框架。这个框架具备良好的扩展性和可维护性,能够高效地管理和执行API测试。通过封装HTTP请求逻辑、使用 `conftest.py`定义共享资源和前置条件,并利用 `pytest.ini`进行配置管理,可以大幅提高测试的自动化程度和执行效率。希望本文能为您的测试工作提供实用的指导和帮助。
2043 15
|
Java 开发工具 git
【Python】已解决:ERROR: No matching distribution found for JPype1
【Python】已解决:ERROR: No matching distribution found for JPype1
1241 0
|
测试技术 API Apache
使用 Apache JMeter 吞吐量控制器的详细指南
Apache JMeter是开源的负载和性能测试工具,其吞吐量控制器用于控制采样器执行频率以达到特定吞吐量。要使用它,首先启动JMeter,创建测试计划,添加线程组和逻辑控制器。配置吞吐量控制器的参数,如总执行次数或百分比,并添加HTTP请求采样器。例如,创建两个控制器,一个设定执行次数,另一个设定执行百分比。通过监听器如汇总报告和查看结果树来分析测试结果,从而模拟不同负载并识别性能瓶颈。吞吐量控制器是实现复杂测试场景的关键组件。
|
SQL 分布式计算 API
Apache Hudi从零到一:深入研究读取流程和查询类型(二)
Apache Hudi从零到一:深入研究读取流程和查询类型(二)
490 1