pytest框架进阶自学系列 | 在不同的层级上重写fixture

书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》

一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客


在大型测试中,可能需要在本地覆盖项目级别的fixture,以增加可读性和可维护性。也就是说可以通过在不同层级中重写fixture改变fixture中原始的内容。思路是重写同名(文件或函数)的方法。使用时优先调用重写后的fixture。

在文件夹(conftest.py)层级重写fixture

下一层的conftest.py中的fixtures可以覆盖和访问上一级的fixture,实现过程:

  1. 在不同层次建立文件夹

创建tests文件夹,以及子文件夹subfolder。在tests文件夹下面创建conftest.py文件和test_something.py两个文件,在subfolder下同样创建这两个文件。

  1. 各个文件的代码

在tests文件夹下的conftest.py文件的fixture中创建username方法。

import pytest

@pytest.fixture
def username():
    return 'username'

在tests文件夹下的test_something.py文件中创建test_username方法,通过断言判断传入的是否为username方法所返回的username字符串。

def test_username(username):
    assert username == 'username'

在subfolder文件夹下的conftest.py文件的fixture中创建的username方法中调用username,也就是通过这个参数调用上一级conftest.py中username的fixture。通过同名的方法重写上一级的同名fixture方法。

import pytest

@pytest.fixture
def username(username):
    return 'overridden-' + username

在subfolder文件夹下test_something.py文件的test_username测试方法中调用username,这时调用本地的方法,也就是subfolder文件夹下的conftest.py文件的username方法中重写上一级的usename方法,并通过断言测试是否是本地重写的结果。

def test_username(username):
    assert username == 'overridden-username'

执行结果如下:

D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2022.1.3\plugins\python-ce\helpers\pycharm\_jb_pytest_runner.py" --path D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests
Testing started at 11:53 ...
Launching pytest with arguments D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\tests

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 2 items

test_something.py::test_username PASSED                                  [ 50%]
subfolder\test_something.py::test_username PASSED                        [100%]

============================== 2 passed in 0.02s ==============================

Process finished with exit code 0

子文件夹(subfolder)中conftest.py文件的fixture覆盖了上层文件夹中同名的fixture。

子文件夹(subfolder)中conftest.py文件的fixture可以轻松地访问上层文件夹中同名的fixture。

在模块层级重写fixture

在conftest.py及自己同一文件下的fixture具有相同名字,由在同一文件中的fixture函数重写了conftest.py中的fixture函数。也就是说模块(文件中)中的fixture覆盖了conftest.py中同名的fixture。模块(文件)中的fixture可以轻松地访问conftest.py中同名的fixture。

代码如下:

// test/test_something.py
import pytest

@pytest.fixture
def username(username):
    return 'overridden-' + username

def test_username(username):
    assert username == 'overridden-username'
    
// test/test_something_else.py
import pytest

@pytest.fixture
def username(username):
    return 'overridden-else-' + username

def test_username(username):
    assert username == 'overridden-else-username'
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2022.1.3\plugins\python-ce\helpers\pycharm\_jb_pytest_runner.py" --path D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests
Testing started at 12:02 ...
Launching pytest with arguments D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\tests

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items

test_something.py::test_username PASSED                                  [ 33%]
test_something_else.py::test_username PASSED                             [ 66%]
subfolder\test_something.py::test_username PASSED                        [100%]

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

Process finished with exit code 0

在用例参数中重写fixture

常用的使用场景:fixture通常作为测试数据的初始化,而参数化是调用这些数据实现一个测试方法使用不同数据执行多次的效果。在fixture与参数parametrize组合在一起时,在fixture中读到的原始值可以被用例的参数parametrize所覆盖,这可体现数据层级的灵活性。

代码如下:

// test/conftest.py
import pytest


@pytest.fixture
def username():
    return 'username'


@pytest.fixture
def other_username(username):
    return 'other-' + username
    
// test/test_something.py
import pytest

@pytest.mark.parametrize('username', ['directly-overridden-username'])
def test_username(username):
    assert username == 'directly-overridden-username'
    
@pytest.mark.parametrize('username', ['directly-overridden-username-other'])
def test_username_other(username, other_username):
    assert other_username == 'other-directly-overridden-username-other'
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2022.1.3\plugins\python-ce\helpers\pycharm\_jb_pytest_runner.py" --path D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests
Testing started at 12:09 ...
Launching pytest with arguments D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\tests

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 4 items

test_something.py::test_username[directly-overridden-username] PASSED    [ 25%]
test_something.py::test_username_other[directly-overridden-username-other] PASSED [ 50%]
test_something_else.py::test_username PASSED                             [ 75%]
subfolder\test_something.py::test_username PASSED                        [100%]

============================== 4 passed in 0.03s ==============================

Process finished with exit code 0

fixture的值被用例的参数所覆盖,尽管用例test_username_other没有使用username,但是other_username使用了username,所以也同样受到了影响。

参数化的fixture可重写非参数化的fixture,反之亦然

参数化的fixture和非参数化的fixture同样可以相互覆盖。其实就是在使用fixture的文件中建立同名的fixture方法重写上一级conftest.py文件中的同名方法。无论重写前的方法带参数,还是重写后的方法带参数都是可以的。也就是可以实现原来执行多次的方法通过重写为非参数化的fixture而变成只执行一次。同样可以实现原来只执行一次的方法通过重写为参数化的fixture而变成执行多次的参数化fixture。

代码如下:

import pytest

@pytest.fixture
def username():
    return 'username'

@pytest.fixture
def other_username(username):
    return 'other-' + username

@pytest.fixture(params=['one', 'two', 'three'])
def parametrized_username(request):
    return request.param

@pytest.fixture
def non_parametrized_username(request):
    return 'username'

在test_something1.py文件中,创建与conftest.py文件中同名的parametrized_username方法,重写为只返回一个值的非参数化方法。创建与conftest.py文件中同名的non_parametrized_username方法,添加参数及返回值,重写为参数化的方法。

import pytest

@pytest.fixture
def parametrized_username():
    return 'overridden-username'

@pytest.fixture(params=['one', 'two', 'three'])
def non_parametrized_username(request):
    return request.param

def test_username(parametrized_username):
    assert parametrized_username == 'overridden-username'
    
def test_parametrized_username(non_parametrized_username):
    assert non_parametrized_username in ['one', 'two', 'three']

在test_something_else1.py方法中不需要重写,正常调用conftest.py文件中的fixture。

def test_username(parametrized_username):
    assert parametrized_username in ['one','two','three']
    
def test_username1(non_parametrized_username):
    assert non_parametrized_username == 'username'
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2022.1.3\plugins\python-ce\helpers\pycharm\_jb_pytest_runner.py" --path D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests
Testing started at 12:19 ...
Launching pytest with arguments D:/SynologyDrive/CodeLearning/WIN/pytest-book/src/chapter-3/tests in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\tests

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 12 items

test_something.py::test_username[directly-overridden-username] PASSED    [  8%]
test_something.py::test_username_other[directly-overridden-username-other] PASSED [ 16%]
test_something1.py::test_username PASSED                                 [ 25%]
test_something1.py::test_parametrized_username[one] PASSED               [ 33%]
test_something1.py::test_parametrized_username[two] PASSED               [ 41%]
test_something1.py::test_parametrized_username[three] PASSED             [ 50%]
test_something_else.py::test_username PASSED                             [ 58%]
test_something_else1.py::test_username[one] PASSED                       [ 66%]
test_something_else1.py::test_username[two] PASSED                       [ 75%]
test_something_else1.py::test_username[three] PASSED                     [ 83%]
test_something_else1.py::test_username1 PASSED                           [ 91%]
subfolder\test_something.py::test_username PASSED                        [100%]

============================= 12 passed in 0.07s ==============================

Process finished with exit code 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`pytest`是一个强大的Python测试框架,它支持模块化的测试编写和执行,并且允许你通过`fixture`(也称为“固定设置”)来组织和复用测试代码中的共享资源。`fixture`是一种预定义的函数,用于在测试运行之前初始化环境、创建测试数据或设置其他测试依赖。 默认情况下,`pytest`会在每次测试中自动调用fixture,但有时候你可能需要在某些条件下控制fixture的行为,例如重新执行fixture,或者在一个测试套件中只运行一次。这时候,你可以选择重写fixture的行为。 **重写fixture**通常涉及以下几个步骤: 1. **标记fixture**: 使用`@pytest.fixture(scope="function" / "class" / "module")`标记,指定fixture的作用范围。`function`是默认值,表示每个测试函数使用独立的实例;`class`或`module`则使fixture在整个类或模块内的所有测试中保持不变。 2. **自定义fixture函数**: 可以创建一个新的fixture函数,它会覆盖默认的行为。例如,你可能想要在某些情况下返回模拟数据,而不是实际依赖。 ```python import pytest @pytest.fixture def custom_fixture(): # 这里可以定义一个函数,根据需求动态生成数据或设置 if some_condition: yield simulated_data else: # 或者在其他条件下返回不同内容 yield real_data ``` 3. **使用`yield`**: 为了使fixture具有可变状态,你需要使用`yield`关键字。这样做的好处是可以在`yield`内部执行操作,然后在下次请求时继续。 4. **参数化fixture**: 如果同一个fixture需要提供多个版本,可以使用`parametrize`装饰器对fixture进行参数化。 ```python @pytest.mark.parametrize("data", [simulated_data, real_data], indirect=True) def test_function(custom_fixture, data): # 测试使用custom_fixture提供的数据 ... ``` **相关问题--:** 1. pytestfixture作用范围有哪些? 2. 如何在pytest中创建具有可变状态的fixture? 3. 如何通过参数化来复用fixture并测试多种情况?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值