在pytest中,conftest.py文件有什么用?

问题:在pytest中,conftest.py文件有什么用?

我最近发现了pytest。好像很棒 但是,我认为文档可能会更好。

我正在尝试了解conftest.py文件的用途。

在我的(目前很小的)测试套件conftest.py中,项目根目录下有一个文件。我用它来定义要注入测试的灯具。

我有两个问题:

  1. 这是正确的用法conftest.py吗?它还有其他用途吗?
  2. 我可以有多个conftest.py文件吗?我什么时候要这么做?示例将被理解。

更一般而言,您如何定义conftest.pypy.test测试套件中的目的和正确使用文件?

I recently discovered pytest. It seems great. However, I feel the documentation could be better.

I’m trying to understand what conftest.py files are meant to be used for.

In my (currently small) test suite I have one conftest.py file at the project root. I use it to define the fixtures that I inject into my tests.

I have two questions:

  1. Is this the correct use of conftest.py? Does it have other uses?
  2. Can I have more than one conftest.py file? When would I want to do that? Examples will be appreciated.

More generally, how would you define the purpose and correct use of conftest.py file(s) in a py.test test suite?


回答 0

这是conftest.py的正确用法吗?

是的。治具是潜在的和普遍的使用conftest.py。您将定义的固定装置将在测试套件中的所有测试之间共享。但是,在根目录中定义固定装置conftest.py可能没有用,如果所有测试未使用此类固定装置,则会减慢测试速度。

它还有其他用途吗?

是的,它确实。

  • 夹具:为测试使用的静态数据定义夹具。除非另有说明,否则套件中的所有测试都可以访问此数据。这可能是数据以及将传递给所有测试的模块帮助程序。

  • 外部插件加载conftest.py用于导入外部插件或模块。通过定义以下全局变量,pytest将加载模块并使它可用于其测试。插件通常是在项目或测试中可能需要的其他模块中定义的文件。您还可以按照此处的说明加载一组预定义的插件。

    pytest_plugins = "someapp.someplugin"

  • 挂钩:您可以指定挂钩(例如设置和拆卸方法)以及更多内容来改善测试。有关一组可用的挂钩,请阅读此处。例:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
  • 测试根路径:这是一个隐藏功能。通过conftest.py在根路径中进行定义,pytest无需指定即可识别应用程序模块PYTHONPATH。py.test在后台sys.path通过包含从根路径找到的所有子模块来修改您的文件。

我可以有多个conftest.py文件吗?

是的,您可以,如果测试结构有些复杂,强烈建议您这样做。conftest.py文件具有目录范围。因此,创建有针对性的装置和助手是一个好习惯。

我什么时候要这么做?示例将不胜感激。

几种情况可能适合:

为一组特定的测试创建一组工具或挂钩

root / 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"

加载一组夹具用于某些测试,但不用于其他测试。

root / mod / conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root / mod2 / conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root / mod2 / test.py

def test(fixture):
    print(fixture)

将打印“其他一些东西”。

覆盖从根继承的钩子conftest.py

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root / conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

通过在内部运行任何测试root/mod,仅打印“ I am mod”。

您可以conftest.py 在此处了解更多信息。

编辑:

如果我需要从不同模块中的多个测试中调用普通的辅助函数,该怎么办-如果将它们放在conftest.py中,它们将对我可用吗?还是我应该将它们放在helpers.py模块中,然后在测试模块中导入并使用它?

您可以conftest.py用来定义您的助手。但是,您应该遵循常规做法。辅助工具至少可以在中用作固定装置pytest。例如,在我的测试中,我有一个模拟的redis帮助器,可以通过这种方式将其注入到我的测试中。

根目录/helper/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.pyconftest.py好像您的模块redis包含更多测试一样。但是,由于模棱两可,不鼓励这种做法。

如果要使用conftest.py,只需将该帮助程序放在根目录中conftest.py,并在需要时将其注入。

root / tests / conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

根/测试/材料/ test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

您可以做的另一件事是编写一个可安装的插件。在那种情况下,您的助手可以写在任何地方,但是需要定义一个要安装在您的和其他潜在测试框架中的入口点。看到这个

如果您不想使用固定装置,则当然可以定义一个简单的帮助器,并在需要的地方使用普通的旧导入。

root /测试/helper/redis.py

class MockRedis():
    # stuff

根/测试/材料/ test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

但是,由于模块不在测试的子文件夹中,因此此处可能存在路径问题。您应该能够克服这(未测试)通过添加__init__.py到您的帮助

root / tests / helper / __ init__.py

from .redis import MockRedis

或者只是将helper模块添加到您的中PYTHONPATH

Is this the correct use of conftest.py?

Yes it is. Fixtures are a potential and common use of conftest.py. The fixtures that you will define will be shared among all tests in your test suite. However, defining fixtures in the root conftest.py might be useless and it would slow down testing if such fixtures are not used by all tests.

Does it have other uses?

Yes it does.

  • Fixtures: Define fixtures for static data used by tests. This data can be accessed by all tests in the suite unless specified otherwise. This could be data as well as helpers of modules which will be passed to all tests.

  • External plugin loading: conftest.py is used to import external plugins or modules. By defining the following global variable, pytest will load the module and make it available for its test. Plugins are generally files defined in your project or other modules which might be needed in your tests. You can also load a set of predefined plugins as explained here.

    pytest_plugins = "someapp.someplugin"

  • Hooks: You can specify hooks such as setup and teardown methods and much more to improve your tests. For a set of available hooks, read here. Example:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
    
  • Test root path: This is a bit of a hidden feature. By defining conftest.py in your root path, you will have pytest recognizing your application modules without specifying PYTHONPATH. In the background, py.test modifies your sys.path by including all submodules which are found from the root path.

Can I have more than one conftest.py file?

Yes you can and it is strongly recommended if your test structure is somewhat complex. conftest.py files have directory scope. Therefore, creating targeted fixtures and helpers is good practice.

When would I want to do that? Examples will be appreciated.

Several cases could fit:

Creating a set of tools or hooks for a particular group of tests.

root/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"

Loading a set of fixtures for some tests but not for others.

root/mod/conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root/mod2/conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root/mod2/test.py

def test(fixture):
    print(fixture)

Will print “some other stuff”.

Overriding hooks inherited from the root conftest.py.

root/mod/conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root/conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

By running any test inside root/mod, only “I am mod” is printed.

You can read more about conftest.py here.

EDIT:

What if I need plain-old helper functions to be called from a number of tests in different modules – will they be available to me if I put them in a conftest.py? Or should I simply put them in a helpers.py module and import and use it in my test modules?

You can use conftest.py to define your helpers. However, you should follow common practice. Helpers can be used as fixtures at least in pytest. For example in my tests I have a mock redis helper which I inject into my tests this way.

root/helper/redis/redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/conftest.py

pytest_plugin="helper.redis.redis"

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

This will be a test module that you can freely import in your tests. NOTE that you could potentially name redis.py as conftest.py if your module redis contains more tests. However, that practice is discouraged because of ambiguity.

If you want to use conftest.py, you can simply put that helper in your root conftest.py and inject it when needed.

root/tests/conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

Another thing you can do is to write an installable plugin. In that case your helper can be written anywhere but it needs to define an entry point to be installed in your and other potential test frameworks. See this.

If you don’t want to use fixtures, you could of course define a simple helper and just use the plain old import wherever it is needed.

root/tests/helper/redis.py

class MockRedis():
    # stuff

root/tests/stuff/test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

However, here you might have problems with the path since the module is not in a child folder of the test. You should be able to overcome this (not tested) by adding an __init__.py to your helper

root/tests/helper/__init__.py

from .redis import MockRedis

Or simply adding the helper module to your PYTHONPATH.


回答 1

广义上,conftest.py是一个本地的按目录的插件。在这里,您可以定义目录特定的挂钩和固定装置。在我的情况下,有一个根目录,其中包含项目特定的测试目录。某些常见的魔术位于“ root” conftest.py中。特定于项目-在自己的项目中。除非将夹具广泛使用(在这种情况下,我宁愿直接在测试文件中定义它们),否则在conftest.py中存储夹具时不会发现任何不良情况。

In a wide meaning conftest.py is a local per-directory plugin. Here you define directory-specific hooks and fixtures. In my case a have a root directory containing project specific tests directories. Some common magic is stationed in ‘root’ conftest.py. Project specific – in their own ones. Can’t see anything bad in storing fixtures in conftest.py unless they are not used widely (In that case I prefer to define them in test files directly)


回答 2

我使用该conftest.py文件来定义要注入测试中的灯具,这是否正确使用conftest.py

是的,通常使用固定装置来准备好用于多个测试的数据。

它还有其他用途吗?

是的,灯具是pytest在实际测试功能之前(有时是之后)运行的功能。灯具中的代码可以执行您想要的任何操作。例如,夹具可以用于获取要进行测试的数据集,或者夹具也可以用于在运行测试之前使系统进入已知状态。

我可以有多个conftest.py文件吗?我什么时候要这么做?

首先,可以将灯具放入单独的测试文件中。但是,要在多个测试文件之间共享灯具,您需要在conftest.py所有测试的中央位置使用文件。灯具可以通过任何测试共享。如果您希望夹具仅由该文件中的测试使用,则可以将它们放在单独的测试文件中。

其次,可以,您可以conftest.py在top tests目录的子目录中拥有其他文件。如果这样做,这些较低级别conftest.py文件中定义的固定装置将可用于该目录和子目录中的测试。

最后,将固定装置放在conftest.py文件中的测试根目录下将使它们在所有测试文件中均可用。

I use the conftest.py file to define the fixtures that I inject into my tests, is this the correct use of conftest.py?

Yes, a fixture is usually used to get data ready for multiple tests.

Does it have other uses?

Yes, a fixture is a function that is run by pytest before, and sometimes after, the actual test functions. The code in the fixture can do whatever you want it to. For instance, a fixture can be used to get a data set for the tests to work on, or a fixture can also be used to get a system into a known state before running a test.

Can I have more than one conftest.py file? When would I want to do that?

First, it is possible to put fixtures into individual test files. However, to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. Fixtures can be shared by any test. They can be put in individual test files if you want the fixture to only be used by tests in that file.

Second, yes, you can have other conftest.py files in subdirectories of the top tests directory. If you do, fixtures defined in these lower-level conftest.py files will be available to tests in that directory and subdirectories.

Finally, putting fixtures in the conftest.py file at the test root will make them available in all test files.