最近我已经熟悉了pytest,以及如何使用conftest.py来定义在我的测试中自动发现和导入的夹具。我非常清楚conftest.py是如何工作的,以及如何使用它,但我不知道为什么在一些基本场景中这被认为是最佳实践。
假设我的测试是以这种方式构造的:
tests/
--test_a.py
--test_b.py最好的实践,正如文档和关于web上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像往常一样使用固定装置。
缺点是,它要求我导入夹具,但显式导入提高了测试用例的可读性,让我像其他python模块一样,一目了然地知道夹具来自何处。
我是否忽略了这个解决方案或conftest.py带来的其他优点,使其成为设置pytest测试套件的最佳实践?
发布于 2022-08-02 09:43:20
没有太大的差别,这主要是因为偏好。我主要使用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如果您没有使db_connection在conftest.py中可用或直接导入它,那么pytest在尝试使用new_user夹具时将无法找到db_connection夹具。如果您直接将db_connection导入您的测试文件中,那么指针将抱怨它是一个未使用的导入。更糟糕的是,有些人可能会删除它,并导致您的测试失败。因此,让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'有了多个自白,您可以覆盖一个固定设备,同时仍然保持对原始夹具的访问。所以接下来的测试都会有效。
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'发布于 2022-08-02 08:22:35
对我来说没有根本的区别,从执行的角度来看,无论代码组织如何,结果都是一样的。
pytest --setup-show
# test_a.py
# SETUP F f_a
# test_a.py::test_a (fixtures used: f_a).
# TEARDOWN F f_a因此,这只是一个代码组织的问题,它应该适合您组织代码的方式。
conftest.py文件。对于更大的代码库,使用避免在测试模块或conftest.py中显式导入补丁的一个变体可能是坚持一个约定(这里假设固定组件由fixture_启动,但它可以是其他所有东西),并在conftest.py中动态导入它。
pytest_plugins = [
fixture.replace("/", ".").replace(".py", "")
for fixture in glob(
"**/fixture_*.py",
recursive=True
)
]https://stackoverflow.com/questions/73191533
复制相似问题