我试图了解 conftest.py
文件的用途。
在我的(目前很小的)测试套件中,我在项目根目录中有一个 conftest.py
文件。我用它来定义我注入测试的固定装置。
我有两个问题:
这是 conftest.py 的正确用法吗?它还有其他用途吗?我可以有多个 conftest.py 文件吗?我什么时候想这样做?示例将不胜感激。
更一般地说,您将如何定义 pytest 测试套件中 conftest.py
文件的用途和正确使用?
It seems great. However, I feel the documentation could be better.
conftest.py
,尽管有很多关于使用 conftest 文件执行此操作或执行此操作的参考,但文档中没有任何地方表明 当 pytest 进行测试发现时, 找到所有 conftest.py 文件(在进行测试发现的目录结构中)将在测试收集阶段运行 i>(在运行任何测试之前)。必须通过实验自己弄清楚。
这是 conftest.py 的正确用法吗?
是的。 Fixtures 是 conftest.py
的潜在和常见用途。您将定义的夹具将在您的测试套件中的所有测试之间共享。但是,在根 conftest.py
中定义固定装置可能没有用,如果所有测试都没有使用这些固定装置,它会减慢测试速度。
它还有其他用途吗?
是的,它确实。
夹具:为测试使用的静态数据定义夹具。除非另有说明,否则套件中的所有测试都可以访问此数据。这可能是数据以及将传递给所有测试的模块的助手。
外部插件加载:conftest.py 用于导入外部插件或模块。通过定义以下全局变量,pytest 将加载模块并使其可用于测试。插件通常是在您的项目或测试中可能需要的其他模块中定义的文件。您还可以加载一组预定义的插件,如此处所述。 pytest_plugins = "someapp.someplugin"
Hooks:您可以指定诸如 setup 和 teardown 方法等钩子来改进您的测试。对于一组可用的钩子,请阅读 Hooks 链接。示例:def pytest_runtest_setup(item): """ 在 ``pytest_runtest_call(item) 之前调用。""" #do some stuff`
测试根路径:这是一个隐藏的特性。通过在根路径中定义 conftest.py,您将让 pytest 识别您的应用程序模块,而无需指定 PYTHONPATH。在后台, py.test 通过包含从根路径中找到的所有子模块来修改您的 sys.path。
我可以有多个 conftest.py 文件吗?
是的,如果您的测试结构有些复杂,强烈建议您这样做。 conftest.py
个文件具有目录范围。因此,创建有针对性的夹具和助手是一种很好的做法。
我什么时候想这样做?示例将不胜感激。
几种情况可能适合:
为一组特定的测试创建一组工具或挂钩。
根/mod/conftest.py
def pytest_runtest_setup(item):
print("I am mod")
#do some stuff
test root/mod2/test.py will NOT produce "I am mod"
为某些测试加载一组夹具,但不为其他测试加载。
根/mod/conftest.py
@pytest.fixture()
def fixture():
return "some stuff"
根/mod2/conftest.py
@pytest.fixture()
def fixture():
return "some other stuff"
根/mod2/test.py
def test(fixture):
print(fixture)
将打印“其他一些东西”。
覆盖从根 conftest.py
继承的挂钩。
根/mod/conftest.py
def pytest_runtest_setup(item):
print("I am mod")
#do some stuff
根/conftest.py
def pytest_runtest_setup(item):
print("I am root")
#do some stuff
通过在 root/mod
中运行任何测试,只会打印“I am mod”。
您可以阅读有关 conftest.py
here 的更多信息。
编辑:
如果我需要从不同模块中的多个测试中调用普通的辅助函数怎么办 - 如果我将它们放在 conftest.py 中,它们对我可用吗?或者我应该简单地将它们放在一个 helpers.py 模块中并在我的测试模块中导入和使用它?
您可以使用 conftest.py
来定义您的助手。但是,您应该遵循常规做法。至少在 pytest
中,助手可以用作固定装置。例如,在我的测试中,我有一个模拟 redis 助手,我以这种方式将其注入到我的测试中。
根/助手/redis/redis.py
@pytest.fixture
def mock_redis():
return MockRedis()
根/测试/东西/conftest.py
pytest_plugin="helper.redis.redis"
根/测试/东西/test.py
def test(mock_redis):
print(mock_redis.get('stuff'))
这将是一个测试模块,您可以在测试中自由导入。 注意,如果您的模块 redis
包含更多测试,您可能会将 redis.py
命名为 conftest.py
。但是,由于模棱两可,不鼓励这种做法。
如果您想使用 conftest.py
,您只需将该帮助程序放在您的根目录 conftest.py
中,并在需要时注入它。
根/测试/conftest.py
@pytest.fixture
def mock_redis():
return MockRedis()
根/测试/东西/test.py
def test(mock_redis):
print(mock_redis.get(stuff))
您可以做的另一件事是编写一个可安装的插件。在这种情况下,您的帮助程序可以在任何地方编写,但它需要定义一个入口点以安装在您的和其他潜在的测试框架中。请参阅this。
如果您不想使用固定装置,您当然可以定义一个简单的帮助程序,并在需要的地方使用普通的旧导入。
根/tests/helper/redis.py
class MockRedis():
# stuff
根/测试/东西/test.py
from helper.redis import MockRedis
def test():
print(MockRedis().get(stuff))
但是,在这里您可能会遇到路径问题,因为模块不在测试的子文件夹中。您应该能够通过向您的助手添加 __init__.py
来克服这个问题(未经测试)
根/测试/助手/init.py
from .redis import MockRedis
或者只是将帮助模块添加到您的 PYTHONPATH
。
从广义上讲,conftest.py
是一个本地的每个目录插件。在这里,您定义特定于目录的钩子和固定装置。在我的情况下,有一个包含项目特定测试目录的根目录。一些常见的魔法驻留在“根”conftest.py
。项目特定 - 在他们自己的项目中。除非它们没有被广泛使用,否则在 conftest.py
中存储固定装置时看不到任何不好的东西(在这种情况下,我更喜欢直接在测试文件中定义它们)
我使用 conftest.py 文件来定义我注入测试的夹具,这是 conftest.py 的正确用法吗?
是的,夹具通常用于为多个测试准备好数据。
它还有其他用途吗?
是的,fixture 是 pytest
在实际测试函数之前(有时是之后)运行的函数。夹具中的代码可以做任何你想做的事情。例如,夹具可用于获取数据集以供测试工作,或者夹具也可用于在运行测试之前使系统进入已知状态。
我可以有多个 conftest.py 文件吗?我什么时候想这样做?
首先,可以将夹具放入单独的测试文件中。但是,要在多个测试文件之间共享夹具,您需要在所有测试的中心位置使用 conftest.py
文件。任何测试都可以共享夹具。如果您希望夹具仅由该文件中的测试使用,它们可以放在单独的测试文件中。
其次,是的,您可以在顶层测试目录的子目录中拥有其他 conftest.py
文件。如果您这样做,这些较低级别 conftest.py
文件中定义的固定装置将可用于该目录和子目录中的测试。
最后,将固定装置放在测试根目录的 conftest.py
文件中将使它们在所有测试文件中都可用。
以下是有关使用 conftest.py to share fixtures 的官方文档:
conftest.py:跨多个文件共享fixture conftest.py 文件用作为整个目录提供fixture 的一种方式。 conftest.py 中定义的固件可以被该包中的任何测试使用,而无需导入它们(pytest 将自动发现它们)。您可以拥有多个包含测试的嵌套目录/包,并且每个目录都可以拥有自己的 conftest.py 和自己的设备,添加到父目录中 conftest.py 文件提供的设备上。
test_aaaaa.py
实际上试图在 夹具设置在conftest.py
中完成之前运行。关于为什么会发生这种情况的任何想法?pytest
文档中的conftest.py
页面!