在之前的分享中,我们知道可以使用​​yield​​​或者​​return​​​关键字把​​fixture函数​​​里的值传递给​​test函数​​。

这种方法很实用,比如我在​​fixture函数​​​里向数据库里插入必要的测试数据,那我就可以把相关数据返回给​​test函数​​用来做相关断言查询等操作。

那如果我想把​​test函数​​​(也就是测试用例)中的数据传给​​fixture函数​​使用,要如何实现呢?

直接先贴上一段示例代码:

import pytest


@pytest.fixture
def fixt(request):
marker = request.node.get_closest_marker("fixt_data")
if marker is None:
# Handle missing marker in some way...
data = None
else:
data = marker.args[0]

# Do something with the data
return data


@pytest.mark.fixt_data(42)
def test_fixt(fixt):
assert fixt == 42

一、前置知识

代码中可能有2个知识点,可能有的小伙伴并不熟悉,分别来看下。

1. Mark 标记

什么是​​mark​​标记,干什么用?

​标记​​​可以将元数据应用于测试函数(注意,不能是fixture函数),后续可以通过​​fixture函数​​​或者​​plugins插件​​进行访问。

框架有一些​​内置​​​的marks,也可以支持我们​​自定义​​。

内置的在之前的系列分享中有出现过几个,比如:

  • ​pytest.mark.parametrize​​:参数化
  • ​pytest.mark.skip​​:跳过测试用例
  • ​pytest.mark.skipif​​: 根据条件跳过用例

其他就不展开了,上述提到的分享文章链接会附在文末。

而在上述示例代码中,​​pytest.mark.fixt_data​​​则是属于自定义的mark标记,​​fixt_data​​​我也可以改成​​fixt_pingguo​​也是可以的。

2. request

​request​​​本身也是一个fixture函数,但是很​​特殊​​,用于提供当前正在执行请求的上下文信息。

在上述示例代码中,测试函数​​test_fixt​​​请求了fixture函数​​fixt​​​,那么在这次请求中相关联到的信息就可以在​​request​​​中获得。
比如:

  • ​fixturename​​: 当前这个fixture函数的名称
  • ​module​​: 当前测试函数所在的模块
  • ​scope​​:当前fixture函数作用范围
  • ​node​​​:基于当前测试范围搜集到的底层节点对象,这里又包含了很多信息。
    ...

就不一一展开了,有兴趣的童鞋可以在编辑器里打个断点,查看对应的信息详情。

【pytest官方文档】解读- 如何自定义mark标记,并将测试用例的数据传递给fixture函数_自定义

二、通过自定义mark传递数据

回到示例代码,我们可以先直接执行一下代码。

【pytest官方文档】解读- 如何自定义mark标记,并将测试用例的数据传递给fixture函数_python_02

测试是通过的(warning先忽略,因为没有注册自定义的mark),也就是说​​@pytest.mark.fixt_data(42)​​​中的​​42​​​是成功的带到了​​fixture函数​​中,经过函数中的处理后最后返回出来。

其中的​​get_closest_marker("fixt_data")​​​方法,是返回与名称​​fixt_data​​匹配的第一个mark,从最近的级别到更远的级别,比如从函数到模块级别。

所以在这里,被找到的mark就是我们自定义的这个​​@pytest.mark.fixt_data(42)​​标记了。

1. 自定义mark知识点

标记是使用工厂对象​​pytest.mark​​​动态创建的,用于装饰器,所以我们可以用语法糖​​@​​直接使用即可。

mark对象被创建之后,就被会收集起来,然后可以通过​​fixture​​​或带有​​Node.iter_markers​​的钩子函数访问,可以访问到这个mark对象的属性。

有 2 个属性:

  • ​mark.args​​:这是个元组
  • ​mark.kwargs​​:这是个字典

所以我们可以使用上面的方式来进行传参,比如现在新建一个自定义mark:

@pytest.mark.timeout(10, "slow", method="thread")
def test_function():
...

这里传参实际上就是

mark.args == (10, "slow")
mark.kwargs == {"method": "thread"}

回到最上方的示例代码,在​​fixture函数​​​中就可以使用​​data = marker.args[0]​​​来获取到参数​​42​​。

如果在测试函数上​​同时使用了多个自定义mark​​,那么举例测试函数最近的mark就会被首先迭代。比如:

@pytest.mark.timeout(10, "slow", method="thread")
@pytest.mark.slow
def test_function():
...

结果就是先​​@pytest.mark.slow​​​,然后是​​@pytest.mark.timeout​​。

2. 注册自定义的mark

在运行最上方的示例代码时出现了一个warning,因为我们没有注册自定义的标记导致,现在来进行注册。

新建​​pytest.ini​​配置文件,在里面添加即可:

[pytest]
markers =
fixt_data: pingguo test
fixt_data2

这里冒号​​:​​​后面的描述是可选的,比如​​fixt_data2​​就是没有添加描述。

重新执行下最上方的代码:

platform win32 -- Python 3.9.12, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\PythonCode\my_python, configfile: pytest.ini
collected 1 item

usemarks.py .

============================== 1 passed in 0.00s ==============================

Process finished with exit code 0

注册完成。

pytest合集见​​连接​

--不要用肉体的勤奋,去掩盖思考的懒惰--