python - 使用 conftest.py 与从专用模块导入固定装置

标签 python pytest

最近我一直在熟悉 pytest 以及如何使用 conftest.py 来定义在我的测试中自动发现和导入的装置。我很清楚 conftest.py 是如何工作的以及如何使用它,但我不确定为什么这被认为是某些基本场景中的最佳实践。

假设我的测试是这样组织的:

tests/
--test_a.py
--test_b.py

最佳实践,如文档和网络上有关 pytest 的各种文章所建议的那样,将定义一个 conftest.py 文件,其中包含一些要在 test_a 中使用的固定装置。 pytest_b.py。为了更好地组织我的灯具,我可能需要以语义上有意义的方式将它们拆分成单独的文件,例如。 db_session_fixtures.pydataframe_fixtures.py,然后将它们作为插件导入到conftest.py中。

tests/
--test_a.py
--test_b.py
--conftest.py
--db_session_fixtures.py
--dataframe_fixtures.py

conftest.py 中我会:

import pytest
    
pytest_plugins = ["db_session_fixtures", "dataframe_fixtures"]

而且我将能够在我的测试用例中无缝地使用 db_session_fixturesdataframe_fixtures,而无需任何额外的代码。

虽然这很方便,但我觉得它可能会影响可读性。例如,如果我不使用如上所述的 conftest.py,我可能会写在 test_a.py

from .dataframe_fixtures import my_dataframe_fixture

def test_case_a(my_dataframe_fixture):
   #some tests

并像往常一样使用固定装置。

缺点是它需要我导入 fixture ,但显式导入提高了我的测试用例的可读性,让我一目了然地知道 fixture 来自哪里,就像任何其他 python 模块一样。

关于此解决方案或 conftest.py 带来的其他优势,我是否忽略了缺点,使其成为设置 pytest 测试套件时的最佳实践?

最佳答案

差别不大,主要取决于偏好。我主要使用 conftest.py 来引入所需的修复程序,但您的测试不会直接使用这些修复程序。所以你可能有一个 fixture 可以对数据库做一些有用的事情,但需要一个数据库连接才能做到这一点。因此,您使 db_connection fixture 在 conftest.py 中可用,然后您的测试只需执行如下操作:

conftest.py

from tests.database_fixtures import db_connection

__all__ = ['db_connection']

tests/database_fixtures.py

import pytest

@pytest.fixture
def db_connection():
    ...

@pytest.fixture
def new_user(db_connection):
    ...

test/test_user.py

from tests.database_fixtures import new_user

def test_user(new_user):
    assert new_user.id > 0  # or whatever the test needs to do

如果您没有在 conftest.py 中提供 db_connection 或直接导入它,那么 pytest 将无法找到 db_connection fixture尝试使用 new_user fixture 。如果您直接将 db_connection 导入到您的测试文件中,那么 linters 会提示这是一个未使用的导入。更糟糕的是,有些人可能会删除它,并导致您的测试失败。因此,对我来说,使 db_connectionconftest.py 中可用是最简单的解决方案。

覆盖装置

一个重要的区别是使用 conftest.py 更容易覆盖固定装置。假设您的目录布局为:

./
├─ conftest.py
└─ tests/
   ├─ test_foo.py
   └─ bar/
      ├─ conftest.py
      └─ test_foobar.py

conftest.py 中你可以:

import pytest

@pytest.fixture
def some_value():
    return 'foo'

然后在 tests/bar/conftest.py 中你可以:

import pytest

@pytest.fixture
def some_value(some_value):
    return some_value + 'bar'

拥有多个 conftests 允许您覆盖一个 fixture ,同时仍然保持对原始 fixture 的访问。所以下面的测试都可以。

tests/test_foo.py

def test_foo(some_value):
    assert some_value == 'foo'

tests/bar/test_foobar.py

def test_foobar(some_value):
    assert some_value == 'foobar'

您仍然可以在没有 conftest.py 的情况下执行此操作,但它有点复杂。您需要执行以下操作:

import pytest

# in this scenario we would have something like:
#   mv contest.py tests/custom_fixtures.py
from tests.custom_fixtures import some_value as original_some_value

@pytest.fixture
def some_value(original_some_value):
    return original_some_value + 'bar'

def test_foobar(some_value):
    assert some_value == 'foobar'

关于python - 使用 conftest.py 与从专用模块导入固定装置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73191533/

相关文章:

python - 每个训练周期后进行评估

python - jinja2 的 vim 语法突出显示?

python - 使用 Python 将复杂参数解析为 shell 脚本

python - 仍然不知道如何模拟导入的库 - Python

python - PyTest Tornado : 'SimpleAsyncHTTPClient' is not iterable

python - Pandas -按累积时间段分组

python - 将 Pandas DataFrame 与多名称列一起使用

python - 运行时警告: coroutine was never awaited in tests

python - 如何在 pytest 中为模拟函数提供不同的返回值?

python - 连接到测试数据库的 Pytest 错误