Pytest教程:Fixture详解

什么是 Fixture?

Fixture 是 Pytest 中用于提供测试环境的一种机制。它可以被用来模拟资源,例如数据库连接、临时文件、网络连接等,以及执行一系列的设置和清理操作,从而使测试用例能够在可控的环境下运行。

Fixture 的基本用法

在 Pytest 中,我们可以通过 @pytest.fixture 装饰器定义 fixture。fixture 可以在测试函数中作为参数传递,并在需要时自动执行。以下是一个简单的例子:

python 复制代码
import pytest

@pytest.fixture
def setup():
    print("Performing setup")
    yield
    print("Performing cleanup")

def test_example(setup):
    print("Executing test")
    assert 1 + 1 == 2

在上面的例子中,setup 函数是一个 fixture。在 test_example 测试用例执行前,setup 函数会被调用。当测试用例执行完成后,setup 函数会执行清理操作。

Fixture 的作用域

Fixture 可以具有不同的作用域,以控制其生命周期和共享程度。Pytest 支持四种作用域:function、class、module 和 session。

  • function:默认的作用域,每个测试函数执行前后调用一次 fixture。
  • class:在测试类中所有方法执行前后调用一次 fixture。
  • module:在每个模块(.py 文件)中的所有测试函数执行前后调用一次 fixture。
  • session:在整个测试会话中只调用一次 fixture。
python 复制代码
import pytest

@pytest.fixture(scope="module")
def setup_module():
    print("Module setup")
    yield
    print("Module cleanup")

def test_example1(setup_module):
    print("Executing test 1")
    assert True

def test_example2(setup_module):
    print("Executing test 2")
    assert False

在上面的例子中,setup_module fixture 的作用域被设置为 module,因此在整个模块中的测试函数执行前后只会调用一次。

参数化 Fixture

有时候,我们希望 fixture 能够根据不同的参数化条件提供不同的行为。Pytest 允许 fixture 接受参数,并在测试函数中使用 pytest.mark.parametrize 进行参数化。

python 复制代码
import pytest

@pytest.fixture
def setup(request):
    value = request.param
    print(f"Setup with parameter: {value}")
    yield value * 2
    print("Cleanup")

@pytest.mark.parametrize("setup", [1, 2], indirect=True)
def test_example(setup):
    assert setup == 2 or setup == 4

在上面的例子中,setup fixture 被参数化为 1 和 2,并在测试函数中使用间接参数化 (indirect=True) 进行引用。这样,测试函数会分别以 2 和 4 作为 setup 的值运行。

Fixture 的 Fixture

有时候,我们可能需要在 fixture 中使用其他 fixture。Pytest 允许在 fixture 中注入其他 fixture。

python 复制代码
import pytest

@pytest.fixture
def setup_data():
    return [1, 2, 3]

@pytest.fixture
def setup_complex(setup_data):
    return {"data": setup_data, "count": len(setup_data)}

def test_example(setup_complex):
    assert setup_complex["count"] == 3

在上面的例子中,setup_complex fixture 依赖于 setup_data fixture。Pytest 会自动解析依赖关系,确保在调用 setup_complex 之前,setup_data 先被调用。

Fixture 的延迟加载

有时候,我们可能需要在测试函数中动态地请求 fixture。Pytest 允许在测试函数参数中使用字符串来引用 fixture,从而实现延迟加载。

python 复制代码
import pytest

@pytest.fixture
def setup():
    print("Setup")
    yield 42
    print("Cleanup")

def test_example(setup):
    assert setup == 42

def test_example_with_fixture_string(setup):
    assert setup == 42

在上面的例子中,test_example_with_fixture_string 测试函数直接使用了 setup 字符串来引用 setup fixture。Pytest 会自动解析这种引用并加载相应的 fixture。

Fixture 的 Fixture Finalizer

有时候,我们可能需要在 fixture 的生命周期结束时执行一些额外的清理操作。Pytest 允许使用 request.addfinalizer() 来注册这样的清理函数。

python 复制代码
import pytest

@pytest.fixture
def setup(request):
    print("Setup")
    def teardown():
        print("Teardown")
    request.addfinalizer(teardown)
    yield 42

def test_example(setup):
    assert setup == 42

Fixture实战

python 复制代码
# content of conftest.py

import pytest
from myapp import MyApp  # 假设有一个名为 myapp 的应用,需要进行测试

@pytest.fixture
def app():
    # 在这里创建并返回应用的实例
    return MyApp()

@pytest.fixture
def logged_in_user(app):
    # 模拟已登录的用户,并返回用户对象
    user = app.login("testuser", "password")
    return user

上述示例中,我们创建了两个 Fixture:app 和 logged_in_user。

  • app Fixture 创建了一个 MyApp 的实例,它代表我们的应用。在测试中,可以通过传递 app 参数来获取应用实例,以便进行测试。

  • logged_in_user Fixture 则利用了 app Fixture,模拟了一个已登录的用户,并返回用户对象。这个 Fixture 可以在需要已登录用户的测试中使用。

接下来,我们可以编写测试来使用这些 Fixture:

python 复制代码
# content of test_myapp.py

def test_app_creation(app):
    assert app is not None
    assert app.is_running()  # 假设 MyApp 类有一个检查应用是否正在运行的方法

def test_user_login(logged_in_user):
    assert logged_in_user is not None
    assert logged_in_user.is_logged_in()  # 假设用户对象有一个检查是否已登录的方法

在这个示例中,我们编写了两个测试函数,它们使用了上面定义的 Fixture。通过传递 app 和 logged_in_user 参数,测试函数可以访问预先设置好的资源,使得测试更加简洁和可维护。

相关推荐
1024奇点5 小时前
【仓颉语言入门 · 第二篇 第 22 课】
开发语言·ide·开源
实名上网宋凯宣5 小时前
Django
后端·python·django
Warren2Lynch6 小时前
C4 模型详解:利用 Visual Paradigm 可视化软件架构的综合指南
开发语言·uml
如若1236 小时前
【论文复现】CoFailMed:从零复现医学图像分割的共享失败审计,含完整代码流程与科研绘图
人工智能·python
Knight_AL6 小时前
Java 文件下载时文件名中的 `#` 被转换成 `%23`?一次 URL 编码与解码问题排查
java·开发语言
FRing037 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
by209997 小时前
C++ 智能指针的所有权与使用
开发语言·c++·经验分享
砚底藏山河7 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
视觉小萌新7 小时前
AI Class 3 · Chapter 1:变量、字符串、list/dict、if/for(python学习-小白版-有经验可跳过)
python·学习·list
IT召唤狮8 小时前
开源企业级零信任项目Pangolin,零信任能挡住PHP核弹漏洞吗?
开发语言·开源·php