Pytest----fixture高级应用

简介: Pytest----fixture高级应用

【原文链接】

一、通过request获取模块及文件中的属性

通过request可以获取到测试文件及模块的属性值,这样就可以动态的对测试文件做一些操作或者控制,本质上就是pytest中的反射

如下在test_demo.py的代码,其中定义了一个变量name

name="张无忌"

def test_func3():
    assert 10==9

如下为conftest.py代码,这里定义了一个module级的fixture,在这个fixture中可以获取到用例中的name属性,在实际应用中,比如就可以通过获取到的属性值进一步做一些判断或者处理等等,这里仅仅打印出来展示一下

import pytest

@pytest.fixture(autouse=True,scope="module")
def session_fixture(request):
    name=getattr(request.module,"name",None)
    print(name)

执行结果如下:

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 1 item                                                                                                                                                        

test_demo.py 张无忌
F

=============================================================================== FAILURES ===============================================================================
______________________________________________________________________________ test_func3 ______________________________________________________________________________

    def test_func3():
>       assert 10==9
E       assert 10 == 9

test_demo.py:6: AssertionError
======================================================================= short test summary info ========================================================================
FAILED test_demo.py::test_func3 - assert 10 == 9
========================================================================== 1 failed in 0.10s ===========================================================================

二、通过marker向fixture传值

可以通过request.node获取到marker对象,进而获取到其参数,从而可以向fixture传递此参数,具体示例如下

test_demo.py代码如下:

import pytest


@pytest.fixture()
def fixt(request):
    marker = request.node.get_closest_marker("fixt_data")
    if marker is None:
        data = None
    else:
        data = marker.args[0]
    return data


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

执行结果为:

$ pytest
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 1 item                                                                                                                                                        

test_demo.py .                                                                                                                                                    [100%]

=========================================================================== warnings summary ===========================================================================
test_demo.py:14
  D:\src\blog\tests\test_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.fixt_data - is this a typo?  You can register custom marks to avoid this warning - for
 details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.fixt_data(42)

-- Docs: https://docs.pytest.org/en/stable/warnings.html
===================================================================== 1 passed, 1 warning in 0.02s =====================================================================

三、将工厂函数作为fixture

可以将工厂函数作为fixture,这样可以在测试用例中很方便的构造一些对象,如下

test_demo.py代码如下:

import pytest

@pytest.fixture
def make_customer_record():
    def _make_customer_record(name):
        return {"name": name, "orders": []}

    return _make_customer_record


def test_customer_records(make_customer_record):
    customer_1 = make_customer_record("Lisa")
    customer_2 = make_customer_record("Mike")
    customer_3 = make_customer_record("Meredith")
    print("\n")
    print(customer_1)
    print(customer_2)
    print(customer_3)

执行结果如下:

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 1 item                                                                                                                                                        

test_demo.py

{'name': 'Lisa', 'orders': []}
{'name': 'Mike', 'orders': []}
{'name': 'Meredith', 'orders': []}
.

========================================================================== 1 passed in 0.03s ===========================================================================

四、支持参数化的fixture

定义fixture中使用params,然后再fixture中通过request.param抛出,则可以实现参数化的过程,即params中有几个参数就可以执行几个测试用例,亦即数据驱动,如下params中有三个参数,虽然这里只有一个测试函数,但从执行结果可以看出仍然显示是执行了三个测试用例

test_demo.py代码如下

import pytest

@pytest.fixture(scope="function",params=[1,2,3])
def f1(request):
    print("in f1 fixture ...")
    yield request.param

def test_func(f1):
    print(f1)
    assert f1>0

执行结果如下

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

test_demo.py in f1 fixture ...
1
.in f1 fixture ...
2
.in f1 fixture ...
3
.

========================================================================== 3 passed in 0.04s ===========================================================================

五、参数化的fixture指定用例id

如下,通过pytest --collect-only 查看id

test_demo.py的代码如下:

import pytest

@pytest.fixture(scope="function",params=[1,2,3])
def f1(request):
    print("in f1 fixture ...")
    yield request.param

def test_func(f1):
    print(f1)
    assert f1 > 0

执行结果如下

$ pytest --collect-only
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

<Module test_demo.py>
  <Function test_func[1]>
  <Function test_func[2]>
  <Function test_func[3]>

====================================================================== 3 tests collected in 0.02s ======================================================================

修改test_demo.py代码如下,通过ids设置用例id

import pytest

@pytest.fixture(scope="function",params=[1,2,3],ids=["test_01", "test_02","test_03"])
def f1(request):
    print("in f1 fixture ...")
    yield request.param

def test_func(f1):
    print(f1)
    assert f1 > 0

然后执行,结果如下,可以发现,此时用例id已经发生了变化

$ pytest --collect-only
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

<Module test_demo.py>
  <Function test_func[test_01]>
  <Function test_func[test_02]>
  <Function test_func[test_03]>

====================================================================== 3 tests collected in 0.03s ======================================================================

六、参数化的fixture指定某个参数使用skip标记

在使用参数的fixture的时候,对一些被测数值也可以像普通的测试函数一样使用skip标记跳过,如下

test_demo.py代码如下:

import pytest

@pytest.fixture(scope="function",params=[1,2,pytest.param(3,marks=pytest.mark.skip)])
def f1(request):
    print("in f1 fixture ...")
    yield request.param

def test_func(f1):
    print(f1)
    assert f1 > 0

执行结果如下:

$ pytest -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('G:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

test_demo.py::test_func[1] PASSED                                                                                                                                 [ 33%]
test_demo.py::test_func[2] PASSED                                                                                                                                 [ 66%]
test_demo.py::test_func[3] SKIPPED (unconditional skip)                                                                                                           [100%]

===================================================================== 2 passed, 1 skipped in 0.19s =====================================================================

七、调用参数化的fixture可以实现两组数据的全排列组合测试数据

当在一个测试函数中调用两个参数化的fixture,可以实现两组参数的全排列组合,比如一个参数列表为[1,2],另一个fixture的参数列表为[10,20],当一个测试函数同时调用此两个fixture时,则会产生[(1,10),(1,20),(2,10),(2,20)]这样四组被测数据

test_demo.py代码如下

import pytest

@pytest.fixture(scope="function",params=[1,2])
def f1(request):
    print("in f1 fixture setup...")
    yield request.param
    print("in f1 fixture teardown...")

@pytest.fixture(scope="function",params=[10,20])
def f2(request):
    print("in f2 fixture setup...")
    yield request.param
    print("in f2 fixture teardown...")

def test_func(f1,f2):
    assert f2<f1

执行结果如下:

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 4 items                                                                                                                                                       

test_demo.py in f1 fixture setup...
in f2 fixture setup...
Fin f2 fixture teardown...
in f1 fixture teardown...
in f1 fixture setup...
in f2 fixture setup...
Fin f2 fixture teardown...
in f1 fixture teardown...
in f1 fixture setup...
in f2 fixture setup...
Fin f2 fixture teardown...
in f1 fixture teardown...
in f1 fixture setup...
in f2 fixture setup...
Fin f2 fixture teardown...
in f1 fixture teardown...


=============================================================================== FAILURES ===============================================================================
___________________________________________________________________________ test_func[1-10] ____________________________________________________________________________

f1 = 1, f2 = 10

    def test_func(f1,f2):
>       assert f2<f1
E       assert 10 < 1

test_demo.py:16: AssertionError
___________________________________________________________________________ test_func[1-20] ____________________________________________________________________________

f1 = 1, f2 = 20

    def test_func(f1,f2):
>       assert f2<f1
E       assert 20 < 1

test_demo.py:16: AssertionError
___________________________________________________________________________ test_func[2-10] ____________________________________________________________________________

f1 = 2, f2 = 10

    def test_func(f1,f2):
>       assert f2<f1
E       assert 10 < 2

test_demo.py:16: AssertionError
___________________________________________________________________________ test_func[2-20] ____________________________________________________________________________

f1 = 2, f2 = 20

    def test_func(f1,f2):
>       assert f2<f1
E       assert 20 < 2

test_demo.py:16: AssertionError
======================================================================= short test summary info ========================================================================
FAILED test_demo.py::test_func[1-10] - assert 10 < 1
FAILED test_demo.py::test_func[1-20] - assert 20 < 1
FAILED test_demo.py::test_func[2-10] - assert 10 < 2
FAILED test_demo.py::test_func[2-20] - assert 20 < 2
========================================================================== 4 failed in 0.20s ===========================================================================

八、通过usefixtures为一个测试类调用fixture

假如想为一个测试类中的每个测试函数都去调用一个fixture,则此时可以在类上通过使用@pytest.mark.usefixtures()来指定

test_demo.py代码如下:

import pytest

@pytest.fixture(scope="function")
def f1():
    print("in f1 fixture setup...")
    yield
    print("in f1 fixture teardown...")

@pytest.mark.usefixtures("f1")
class TestDemo(object):
    def test_01(self):
        print("in test_01...")
        assert 1==1

    def test_02(self):
        print("in test_02...")
        assert 1==1

    def test_03(self):
        print("in test_03...")
        assert 1==1

执行结果如下:

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

test_demo.py in f1 fixture setup...
in test_01...
.in f1 fixture teardown...
in f1 fixture setup...
in test_02...
.in f1 fixture teardown...
in f1 fixture setup...
in test_03...
.in f1 fixture teardown...


========================================================================== 3 passed in 0.04s ===========================================================================

九、fixture覆盖重写

fixture的覆盖重写简单点来说就是遵循就近原则,即在测试函数中总是调用离测试函数最近的fixture,具体点说就是:

(1)内层的conftest.py中的fixture会覆盖外层conftest.py中的fixture

(2)测试模块中的fixture会覆盖conftest.py中的同名fixture

(3)参数化中的参数会覆盖测试模块中的同名fixture

目录结构如下:

tests
  |----conftest.py
  |----demo
        |----__init__.py
        |----conftest.py
        |----test_demo.py

其中tests/conftest.py内容如下:

import pytest

@pytest.fixture()
def conf_fixture():
    print("in outer conftest.py fixture...")

tests/demo/conftest.py内容如下:

import pytest

@pytest.fixture()
def conf_fixture():
    print("in inner conftest.py fixture...")

@pytest.fixture()
def module_fixture():
    print("in conftest.py module fixture...")

test_demo.py内容如下:

import pytest

@pytest.fixture()
def module_fixture():
    print("in test_demo.py module fixture...")


@pytest.fixture()
def f1():
    return "hello f1 function"

def test_01(conf_fixture):
    print("in test_01...")

def test_02(module_fixture):
    print("in test_02")

@pytest.mark.parametrize("f1",["hello zhangwuji"])
def test_03(f1):
    print("in test_03...")
    print(f1)

执行结果如下,可以看出,test_01函数调用了内层的conftest.py中的fixture,test_02函数调用了test_demo.py中的fixture,test_03函数则直接调用了参数化的参数,即均遵循就近原则

$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items                                                                                                                                                       

demo\test_demo.py in inner conftest.py fixture...
in test_01...
.in test_demo.py module fixture...
in test_02
.in test_03...
hello zhangwuji
.

========================================================================== 3 passed in 0.07s ===========================================================================
目录
相关文章
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
3957 2
|
Kubernetes 关系型数据库 MySQL
k8s教程(基础篇)-入门及案例
k8s教程(基础篇)-入门及案例
4801 0
|
JSON API 数据格式
实时获取小红书笔记详情的API使用与解析
小红书是一个以分享消费经验、生活方式为主的社交平台,拥有大量的用户和内容。为了更好地了解用户在小红书上的行为和内容,许多开发者选择使用小红书开放平台提供的API接口。本文将介绍如何通过小红书笔记详情API实现实时数据获取,并给出相应的代码示例。
|
7月前
|
人工智能 数据可视化 测试技术
Apifox与Apipost对比,2025年功能对比与选项建议
Apifox 和 Apipost 作为国内 API 一体化协作平台的佼佼者,都在不断进化,力求为用户提供更全面的解决方案。本文将聚焦“2025 版”,基于两款工具截至 2024 年末至 2025 年中旬的预期功能和行业发展趋势,进行一次全方位、深度的功能对比,旨在为开发者、测试工程师、产品经理及技术决策者在选型时提供有价值的参考。
2101 123
|
JSON 测试技术 数据格式
Playwright 测试报告器
Playwright 测试报告器
477 4
|
Linux iOS开发 MacOS
【MCP教程系列】阿里云百炼MCP全面配置指南:涵盖NPX、UVX、SSE及Streamable HTTP
本文详细介绍如何在阿里云百炼平台及Windows、Linux、MacOS系统中正确配置MCP服务的JSON文件。内容涵盖三种MCP服务配置:npx(基于Stdio)、uvx(Python工具运行)和SSE(服务器发送事件)。同时解析Streamable HTTP作为新一代传输方案的优势与应用,帮助用户掌握每个参数的具体用途及使用方法,解决配置过程中可能遇到的问题,提供完整示例和扩展信息以优化设置体验。
3773 11
|
C# 开发工具 git
ScreenToGif:一款开源免费且好用的录屏转Gif软件
ScreenToGif:一款开源免费且好用的录屏转Gif软件
743 1
|
开发工具
如何修改Vscode查看源代码管理版本变动文件的查看方式
这篇文章介绍了如何在VSCode中通过源代码管理插件修改查看源代码版本变动文件的方式,提供了树形视图和列表视图两种查看方法,并说明了如何通过设置选项来切换查看方式,帮助用户根据自己的喜好更高效地查看和管理代码变动。
如何修改Vscode查看源代码管理版本变动文件的查看方式
|
XML Java 测试技术
TestNG 与 JUnit 测试框架:哪个更好?
【2月更文挑战第16天】
837 1
TestNG 与 JUnit 测试框架:哪个更好?
|
Linux iOS开发 MacOS
[已解决]ModuleNotFoundError: No module named ‘tqdm‘
[已解决]ModuleNotFoundError: No module named ‘tqdm‘