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

假设我的测试 struct 是这样的:

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

正如关于Web上的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

像往常一样使用固定装置.

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

这个解决方案有没有我忽略的缺点,或者conftest.py带来的其他优势,使其成为设置最热测试套件时的最佳实践?

推荐答案

这并没有太大的区别,这主要是因为人们的喜好.我主要使用conftest.py个来引入您的测试所需的、但不直接使用的修复程序.因此,您可能有一个对数据库执行有用操作的装置,但需要一个数据库连接才能做到这一点.因此,您使db_connection灯具在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

If you didn't make db_connection available in conftest.py个 or directly import it then pytest would fail to find the db_connection fixture when trying to use the new_user fixture. If you directly import db_connection into your test file, then linters will complain that it is an unused import. Worse, some may remove it, and cause your tests to fail. So making the db_connection available in conftest.py个, to me, is the simplest solution.

Overriding Fixtures

The one significant difference is that it is easier to override fixtures using conftest.py个. Say you have a directory layout of:

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

In conftest.py个 you could have:

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'

拥有多个配置测试允许您重写一个装置,同时仍然保持对原始装置的访问.因此,接下来的测试都会奏效.

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'

You can still do this without conftest.py个, but it's a bit more complicated. You'd need to do something like:

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相关问答推荐

使用numpy提取数据块

Pystata:从Python并行运行stata实例

将jit与numpy linSpace函数一起使用时出错

'discord.ext. commanders.cog没有属性监听器'

什么相当于pytorch中的numpy累积ufunc

我们可以为Flask模型中的id字段主键设置默认uuid吗

SQLAlchemy Like ALL ORM analog

提取相关行的最快方法—pandas

在单个对象中解析多个Python数据帧

计算分布的标准差

(Python/Pandas)基于列中非缺失值的子集DataFrame

如何在Python中将超链接添加到PDF中每个页面的顶部?

应用指定的规则构建数组

如何使用pytest在traceback中找到特定的异常

Python如何导入类的实例

将相应的值从第2列合并到第1列(Pandas )

使用Scikit的ValueError-了解

如何在基于时间的数据帧中添加计算值

如何通过特定导入在类中执行Python代码

如何在Python中画一个只能在对角线内裁剪的圆?