深入理解 pytest_collection_modifyitems 钩子函数:优化测试集收集与管理

前言

解锁 pytest_configure(config) 的神奇功能:个性化定制你的测试框架这篇文章中,我们在文章最后,抛出了pytest_collection_modifyitems()钩子函数,那该钩子函数又是啥?怎么用?运行机制?实际运用场景?带着这些疑问,我们一起来探锁pytest_collection_modifyitems()钩子函数。

pytest_collection_modifyitems()是啥?

pytest_collection_modifyitems() 是 Pytest 框架中非常有用的钩子函数之一。

看看源码定义:

python 复制代码
def pytest_collection_modifyitems(
    session: "Session", config: "Config", items: List["Item"]
) -> None:
    """Called after collection has been performed. May filter or re-order
    the items in-place.
​
    :param session: The pytest session object.
    :param config: The pytest config object.
    :param items: List of item objects.
    """

这个钩子函数允许我们在运行测试集之前修改被收集到的测试项(test items),例如重新排序、过滤或添加标记等。

pytest_collection_modifyitems() 函数是在测试集被收集后立即调用的。它接收两个参数,configitemsconfig 参数提供了关于测试运行配置的信息,而 items 参数则是一个列表,包含了所有被收集到的测试项。

pytest_collection_modifyitems()如何使用?

我们配合conftest.py文件进行使用

conftest.py

python 复制代码
def pytest_collection_modifyitems(config, items):
    # 在这里编写需要执行的代码
    pass
​

我们可以在 pytest_collection_modifyitems() 函数内部编写我们的逻辑代码。该函数接收两个参数:configitemsconfig 参数提供了关于测试运行配置的信息,而 items 参数是一个包含所有被收集到的测试项的列表。有同学可能还不是不知道items是啥?这里做一个介绍

参数items是啥?

一组测试用例被称为一个 "item"。pytest 会根据特定的规则 (默认是以 test_ 开头的函数或方法) 自动收集这些测试用例。当 pytest 执行这些测试用例时,它会把它们封装成一个个的 "item",并对它们进行执行和统计结果。

item有哪些属性?

我们说几个常用属性,用来获取用例相关的信息:

  • item.nodeid: 返回一个字符串,表示测试用例的唯一标识符。
  • item.name: 返回一个字符串,表示测试用例的名称。
  • item.parent: 返回一个节点对象,表示测试用例所属的父级节点(模块、类或测试集合)。
  • item.function: 返回一个函数对象,表示测试用例的实际执行函数。
  • item.originalname: 返回一个字符串,表示测试用例的原始名称。
  • item.location: 返回一个元组,包含测试用例所在文件的路径和行号。
  • item.keywords:返回一个包含字符串关键字的集合

我们实际测试一下,看看输出结果,会更加明了

conftest.py中定义该函数,并输出信息,代码如下:

python 复制代码
def pytest_collection_modifyitems(config, items):
    for item in items:
        print(f"item.nodeid  {item.nodeid}")
        print(f"item.name  {item.name}")
        print(f"item.parent  {item.parent}")
        print(f"item.function  {item.function}")
        print(f"item.originalname  {item.originalname}")
        print(f"item.location  {item.location}")

然后我们写一个case进行测试:

test_demo.py

python 复制代码
def test_case():
    assert True

执行case输出信息如下:

php 复制代码
item.nodeid  test_dir/test_demo.py::test_case
item.name  test_case
item.parent  <Module test_demo.py>
item.function  <function test_case at 0x104120940>
item.originalname  test_case
item.location  ('test_dir/test_demo.py', 2, 'test_case')

这样输出结果对应着上面的理论知识看,应该比较容易理解了。

常见使用场景

场景一:

重新排序测试项:通过更改 items 列表的顺序,在运行测试时按照特定的排序规则执行测试项。

python 复制代码
def pytest_collection_modifyitems(config, items):
    # 按照测试项名称字母顺序重新排序
    items.sort(key=lambda x: x.name)

场景二:

过滤测试项:根据特定的规则,从测试集中过滤掉不需要执行的测试项。

arduino 复制代码
def pytest_collection_modifyitems(config, items):
    # 过滤掉带有 'slow' 标记的测试项
    items[:] = [item for item in items if 'slow' not in item.keywords]

场景三:

标记测试项:根据特定的规则,给测试项添加额外的标记。这在动态地为测试项分配标记时非常有用。

scss 复制代码
def pytest_collection_modifyitems(config, items):
    # 给包含 'api' 关键字的测试项添加 'api' 标记
    for item in items:
        if 'api' in item.name:
            item.add_marker(pytest.mark.api)
​

场景四:

跳过测试项:根据特定的规则,跳过某些测试项的执行

scss 复制代码
def pytest_collection_modifyitems(config, items):
    # 跳过带有 'skip' 标记的测试项
    for item in items:
        if 'skip' in item.keywords:
            item.add_marker(pytest.mark.skip)
​

案例实现

基本知识了解之后,我们实现一个小需求,我们现在拥有一组带smoke标记的case,我们想要在执行全部测试用例时,将smoke标记的测试移到测试集的最前面。我们看看如何实现:

arduino 复制代码
def pytest_collection_modifyitems(config, items):
    smoke_tests = []
    other_tests = []
​
    for item in items:
        if 'smoke' in item.keywords:
            smoke_tests.append(item)
        else:
            other_tests.append(item)
​
    items[:] = smoke_tests + other_tests

代码还是比较简单的,首先创建了两个空列表:smoke_testsother_tests,用于存储带有 smoke 标记和其他标记的测试项。然后我们遍历 items 列表,将带有 smoke 标记的项添加到 smoke_tests 列表中,将其他项添加到 other_tests 列表中。

最后一行的 items[:] = smoke_tests + other_tests 将重新排序后的测试项列表赋值给原始的 items 列表,从而实现了将带有 smoke 标记的测试项移动到最前面。

这样,当我们运行pytest 时,所有带有 smoke 标记的测试项都会首先执行,然后是其他测试项。这在快速执行关键的冒烟测试时非常有用,同时保持了灵活性和维护性。

运行机制

pytest_collection_modifyitems()钩子函数的运行机制如下:

  1. Pytest 运行过程中,当测试集被收集完成后,Pytest 会检测到conftest.py文件中是否定义了 pytest_collection_modifyitems() 函数。
  2. 如果 conftest.py中定义了 pytest_collection_modifyitems() 函数,Pytest 会调用该函数,并传递两个参数:configitems
  3. config 参数是一个对象,包含了当前测试运行的配置信息。我们可以使用它来访问和修改配置选项,或者获取有关测试环境和命令行参数的信息。
  4. items 参数是一个列表,其中包含了所有收集到的测试项对象。每个测试项对象都包含了测试项的相关信息,如名称、路径、函数/方法定义等。
  5. pytest_collection_modifyitems() 函数内部,我们可以根据需要对 items 列表进行修改。例如,重新排序测试项、过滤测试项、添加标记等。
  6. 修改 items 列表后,Pytest 将按更新后的顺序和配置继续执行测试。修改后的 items 列表中包含了经过钩子函数处理后的测试项。

总结来说,pytest_collection_modifyitems() 钩子函数在测试集被收集之后被调用,允许我们对收集到的测试项进行自定义修改。通过使用该钩子函数,我们可以根据特定需求实现对测试项的排序、过滤、标记等操作,从而更好地管理和执行测试。

最后

不得不感叹pytest的强大。各种钩子函数满足你的不同需求场景。回到pytest_collection_modifyitems,我们使用时还是要注意:pytest_collection_modifyitems() 函数中的修改操作会直接影响到测试项的执行顺序和状态。因此,在使用该钩子函数时,请确保了解并测试了修改后的结果,以避免出现意外情况。

相关推荐
Smilejudy34 分钟前
三行五行的 SQL 只存在于教科书和培训班
后端·github
爱上语文39 分钟前
Http 请求协议
网络·后端·网络协议·http
CodeDevMaster41 分钟前
LangChain之检索增强生成RAG
人工智能·python·llm
贝克街的天才1 小时前
据说在代码里拼接查询条件不够优雅?Magic-1.0.2 发布
java·后端·开源
monkey_meng1 小时前
【Rust Iterator 之 fold,map,filter,for_each】
开发语言·后端·rust
运维&陈同学1 小时前
【kafka01】消息队列与微服务之Kafka详解
运维·分布式·后端·微服务·云原生·容器·架构·kafka
Moment1 小时前
毕业半年,终于拥有了两个近 500 star 的开源项目了 🤭🤭🤭
前端·后端·开源
计算机毕设指导61 小时前
基于SpringBoot共享汽车管理系统【附源码】
java·spring boot·后端·mysql·spring·汽车·intellij idea
凡解1 小时前
[自动化测试:实践01]:2:(4-1 )元素定位(selenium)在实际场景中的应用2
自动化测试·python·selenium·测试工具
卖个几把萌1 小时前
【04】Selenium+Python 手动添加Cookie免登录(实例)
python·selenium·测试工具