简体   繁体   English

Pytest 和动态夹具模块

[英]Pytest and Dynamic fixture modules

I am writing functional tests using pytest for a software that can run locally and in the cloud.我正在使用 pytest 为可以在本地和云中运行的软件编写功能测试。 I want to create 2 modules, each with the same module/fixture names, and have pytest load one or the other depending if I'm running tests locally or in the cloud:我想创建 2 个模块,每个模块都具有相同的模块/夹具名称,并让 pytest 加载一个或另一个,具体取决于我是在本地还是在云中运行测试:

/fixtures
/fixtures/__init__.py
/fixtures/local_hybrids
/fixtures/local_hybrids/__init__.py
/fixtures/local_hybrids/foo.py
/fixtures/cloud_hybrids
/fixtures/cloud_hybrids/__init__.py
/fixtures/cloud_hybrids/foo.py
/test_hybrids/test_hybrids.py

foo.py (both of them): foo.py (两者):

import pytest
@pytest.fixture()
def my_fixture():
    return True

/fixtures/__init__.py : /fixtures/__init__.py

if True:
    import local_hybrids as hybrids
else:
    import cloud_hybrids as hybrids

/test_hybrids/test_hybrids.py : /test_hybrids/test_hybrids.py

from fixtures.hybrids.foo import my_fixture

def test_hybrid(my_fixture):
   assert my_fixture

The last code block doesn't work of course, because import fixtures.hybrids is looking at the file system instead of __init__.py 's "fake" namespace, which isn't like from fixtures import hybrids , which works (but then you cannot use the fixtures as the names would involve dot notation).最后一个代码块当然不起作用,因为import fixtures.hybrids正在查看文件系统而不是__init__.py的“假”命名空间,这不像from fixtures import hybrids那样有效(但是你不能使用固定装置,因为名称会涉及点符号)。

I realize that I could play with pytest_generate_test to alter the fixture dynamically (maybe?) but I'd really hate managing each fixture manually from within that function... I was hoping the dynamic import (if x, import this, else import that) was standard Python, unfortunately it clashes with the fixtures mechanism:我意识到我可以使用 pytest_generate_test 来动态更改夹具(也许?)但我真的很讨厌从 function 中手动管理每个夹具......我希望动态导入(如果 x,导入这个,否则导入那个) 是标准的 Python,不幸的是它与夹具机制发生冲突:

import fixtures
def test(fixtures.hybrids.my_fixture):  # of course it doesn't work :)
    ...

I could also import each fixture function one after the other in init;我还可以在 init 中一个接一个地导入每个夹具 function; more legwork, but still a viable option to fool pytest and get fixture names without dots.更多的跑腿工作,但仍然是欺骗 pytest 并获得不带点的夹具名称的可行选择。

Show me the black magic.给我看看黑魔法。 :) Can it be done? :) 可以吗?

I think in your case it's better to define a fixture - environment or other nice name.我认为在您的情况下,最好定义一个夹具 - environment或其他好听的名称。

This fixture can be just a getter from os.environ['KEY'] or you can add custom command line argument like here then use it like here and the final use is here .这个夹具可以只是来自 os.environ['KEY'] 的 getter,或者您可以像这里一样添加自定义命令行参数,然后像这里一样使用它,最终使用在这里

What im trying to tell is that you need to switch thinking into dependency injection: everything should be a fixture.我想说的是,您需要将思维转变为依赖注入:一切都应该是固定的。 In your case (and in my plugin as well), runtime environment should be a fixture, which is checked in all other fixtures which depend on the environment.在您的情况下(以及在我的插件中),运行时环境应该是一个夹具,它在所有其他依赖于环境的夹具中进行检查。

You might be missing something here: If you want to re-use those fixtures you need to say it explicitly:你可能在这里遗漏了一些东西:如果你想重用这些固定装置,你需要明确地说出来:

from fixtures.hybrids.foo import my_fixture

@pytest.mark.usefixtures('my_fixture')
def test_hybrid(my_fixture):
    assert my_fixture

In that case you could tweak pytest as following:在这种情况下,您可以调整 pytest 如下:

from local_hybrids import local_hybrids_fixture
from cloud_hybrids import cloud_hybrids_fixture

fixtures_to_test = {
    "local":None,
    "cloud":None
}

@pytest.mark.usefixtures("local_hybrids_fixture")
def test_add_local_fixture(local_hybrids_fixture):
    fixtures_to_test["local"] = local_hybrids_fixture

@pytest.mark.usefixtures("cloud_hybrids_fixture")
def test_add_local_fixture(cloud_hybrids_fixture):
    fixtures_to_test["cloud"] = cloud_hybrids_fixture

def test_on_fixtures():
    if cloud_enabled:
        fixture = fixtures_to_test["cloud"]
    else:
        fixture = fixtures_to_test["local"]
    ...

If there are better solutions around I am also interested;)如果周围有更好的解决方案我也很感兴趣;)

I don't really think there is a "good way" of doing that in python, but still it is possible with a little amount of hacking.我真的不认为在 python 中有一个“好方法”,但仍然可以通过少量的黑客攻击。 You can update sys.path for the subfolder with fixtures you would like to use and import fixtures directly.您可以使用要使用的装置更新子文件夹的 sys.path 并直接导入装置。 In dirty case it look's like that:在肮脏的情况下,它看起来像这样:

for your fixtures/__init__.py:对于您的灯具/__init__.py:

if True:
    import local as hybrids
else:
    import cloud as hybrids

def update_path(module):
    from sys import path
    from os.path import join, pardir, abspath
    mod_dir = abspath(join(module.__file__, pardir))
    path.insert(0, mod_dir)

update_path(hybrids)

and in the client code (test_hybrids/test_hybrids.py):并在客户端代码(test_hybrids/test_hybrids.py)中:

import fixtures
from foo import spam

spam()

In other cases you can use much more complex actions to perform a fake-move of all modules/packages/functions etc from your cloud/local folder directly into the fixture's __init__.py.在其他情况下,您可以使用更复杂的操作将所有模块/包/功能等从您的云/本地文件夹直接假移动到夹具的 __init__.py 中。 Still, I think - it does not worth a try.不过,我认为 - 它不值得一试。

One more thing - black magic is not the best thing to use, I would recommend you to use a dotted notation with "import X from Y" - this is much more stable solution.还有一件事——黑魔法不是最好的使用方法,我建议你使用带有“从 Y 导入 X”的点表示法——这是更稳定的解决方案。

Use the pytest plugins feature and put your fixtures in separate modules.使用 pytest 插件功能并将您的灯具放在单独的模块中。 Then at runtime select which plug-in you'll be drawing from via a command line argument or an environment variable.然后在运行时 select 您将通过命令行参数或环境变量从哪个插件中绘制。 It needs to be something global because you need to place different pytest_plugins list assignments based on the global value.它需要是全局的,因为您需要根据全局值放置不同的pytest_plugins列表分配。

Take a look at the section Conditional Plugins from this repo https://github.com/jxramos/pytest_behavior/tree/main/conditional_plugins看看这个仓库中的条件插件部分https://github.com/jxramos/pytest_behavior/tree/main/conditional_plugins

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM