Pytest和动态夹具模块

时间:2015-04-20 21:53:54

标签: python pytest

我正在使用pytest为可在本地和云端运行的软件编写功能测试。我想创建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(两者都是):

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

/fixtures/__init__.py

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

/test_hybrids/test_hybrids.py

from fixtures.hybrids.foo import my_fixture

def test_hybrid(my_fixture):
   assert my_fixture

最后一个代码块当然不起作用,因为import fixtures.hybrids正在查看文件系统而不是__init__.py的“假”命名空间,这与from fixtures import hybrids不同,这是有效的(但你不能使用灯具,因为名称将涉及点符号)。

我意识到我可以使用pytest_generate_test来动态改变灯具(也许?)但是我真的很讨厌在该函数中手动管理每个灯具......我希望动态导入(如果是x,导入这个,否则导入那个)是标准的Python,不幸的是它与fixtures机制发生了冲突:

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

我还可以在init中依次导入每个fixture函数;更多的腿部工作,但仍然是一个可行的选择来欺骗pytest并获得没有点的夹具名称。

告诉我黑魔法。 :)可以吗?

3 个答案:

答案 0 :(得分:1)

我认为在你的情况下最好定义一个夹具 - environment或其他好名字。

这个fixture可以只是来自os.environ ['KEY']的getter,或者你可以添加自定义命令行参数,如here 然后像here一样使用它 最终用途为here

我想说的是你需要将思维转换为依赖注入:一切都应该是一个固定装置。在您的情况下(以及我的插件中),运行时环境应该是一个夹具,在所有其他依赖于环境的夹具中进行检查。

答案 1 :(得分:0)

你可能在这里遗漏了一些东西:如果你想重新使用这些装置,你需要明确说出来:

from fixtures.hybrids.foo import my_fixture

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

在这种情况下,你可以调整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"]
    ...

如果周围有更好的解决方案我也很感兴趣;)

答案 2 :(得分:0)

我真的不认为在python中有这样做的“好方法”,但仍然可以通过少量的黑客攻击。您可以使用您想要使用的灯具更新子文件夹的sys.path并直接导入灯具。在脏的情况下,它看起来像那样:

用于你的灯具/ __ 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)

并在客户端代码(test_hybrids / test_hybrids.py)中:

import fixtures
from foo import spam

spam()

在其他情况下,您可以使用更复杂的操作从云/本地文件夹中直接将所有模块/包/函数等虚假移动到fixture的__init__.py中。不过,我认为 - 这不值得一试。

还有一件事 - 黑魔法不是最好用的,我建议你使用点缀符号“从Y导入X” - 这是更稳定的解决方案。