最近我一直在熟悉 pytest 以及如何使用 conftest.py
来定义在我的测试中自动发现和导入的装置。我很清楚 conftest.py
是如何工作的以及如何使用它,但我不确定为什么这被认为是某些基本场景中的最佳实践。
假设我的测试是这样组织的:
tests/
--test_a.py
--test_b.py
最佳实践,如文档和网络上有关 pytest 的各种文章所建议的那样,将定义一个 conftest.py
文件,其中包含一些要在 test_a 中使用的固定装置。 py
和 test_b.py
。为了更好地组织我的灯具,我可能需要以语义上有意义的方式将它们拆分成单独的文件,例如。 db_session_fixtures.py
、dataframe_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_fixtures
和 dataframe_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_connection
在 conftest.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/