【问题标题】:Pytest and Dynamic fixture modulesPytest 和动态夹具模块
【发布时间】:2015-06-27 20:11:45
【问题描述】:

我正在使用 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,导入这个, else import that) 是标准的 Python,不幸的是它与固定装置机制发生冲突:

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

我也可以在init中一个接一个地导入每个fixture函数;更多的跑腿工作,但仍然是欺骗 pytest 并获得不带点的夹具名称的可行选择。

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

【问题讨论】:

    标签: python pytest


    【解决方案1】:

    我认为在您的情况下,最好定义一个夹具 - environment 或其他好听的名称。

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

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

    【讨论】:

    • 是的,我知道这种可能性(我最初就是这样做的),但如果我正在运行本地测试,我真的想避免导入云模块,反之亦然。因此,一个接一个地导入每个符号(我原始帖子中的“更多跑腿”建议)对我来说更理想。感谢您抽出宝贵时间回答;)
    【解决方案2】:

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

    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"]
        ...
    

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

    【讨论】:

      【解决方案3】:

      我真的不认为在 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()
      

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

      还有一件事——黑魔法不是最好用的东西,我建议你使用带有“从 Y 导入 X”的点表示法——这是更稳定的解决方案。

      【讨论】:

        猜你喜欢
        • 2021-06-16
        • 2020-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-26
        相关资源
        最近更新 更多