【问题标题】:pytest fixture of fixturespytest 夹具的夹具
【发布时间】:2016-03-03 16:35:59
【问题描述】:

我目前正在为一个中型库(约 300 个文件)编写测试。 该库中的许多类共享使用 pytest 编码的相同测试方案:

文件 test_for_class_a.py:

import pytest

@pytest.fixture()
def setup_resource_1():
    ...

@pytest.fixture()
def setup_resource_2():
    ...

@pytest.fixture()
def setup_class_a(setup_resource_1, setup_resource_2):
    ...

def test_1_for_class_a(setup_class_a):
    ...

def test_2_for_class_a(setup_class_a):
    ...

class_b、class_c 等存在类似文件...唯一的区别是 setup_resource_1 和 setup_resource_2 的内容。

现在我想重新使用在 test_for_class_a.py、test_for_class_b.py 和 test_for_class_c.py 中定义的夹具 setup_class_a、setup_class_b、setup_class_c 对它们运行测试。

在文件 test_all_class.py 中,这是可行的,但每个测试仅限于一个夹具:

from test_for_class_a import *

@pytest.mark.usefixtures('setup_class_a')      # Fixture was defined in test_for_class_a.py
def test_some_things_on_class_a(request)
    ...

但我正在寻找一种更通用的方法:

from test_for_class_a import *
from test_for_class_b import *   # I can make sure I have no collision here 
from test_for_class_c import *   # I can make sure I have no collision here 

==> @generate_test_for_fixture('setup_class_a', 'setup_class_b', 'setup_class_c') 
def test_some_things_on_all_classes(request)
    ...

有什么办法可以做到这一点吗? 我一直在研究工厂的工厂和抽象的 pytest 工厂,但我正在为 pytest 定义夹具的方式而苦苦挣扎。 有没有办法解决这个问题?

【问题讨论】:

    标签: python pytest


    【解决方案1】:

    我们在工作中遇到了同样的问题,我希望为每个案例只编写一次夹具。所以我写了插件pytest-data 来做到这一点。示例:

    @pytest.fixture
    def resource(request):
        resource_data = get_data(reqeust, 'resource_data', {'some': 'data', 'foo': 'foo'})
        return Resource(resource_data)
    
    @use_data(resource_data={'foo': 'bar'})
    def test_1_for_class_a(resource):
        ...
    
    @use_data(resource_data={'foo': 'baz'})
    def test_2_for_class_a(resource):
        ...
    

    它的优点是你只需编写一次带有一些默认值的夹具。当您只需要该夹具/资源并且您不关心特定设置时,您只需使用它。当您需要测试某些特定属性时,假设要检查该资源是否也可以处理 100 个字符长的值,您可以通过 use_data 装饰器传递它,而不是编写另一个夹具。

    这样您就不必关心冲突,因为一切都只会出现一次。然后,您可以将conftest.py 用于您的所有夹具,而无需导入测试模块。例如,我们对所有灯具进行了单独的深度模块,并全部包含在顶部 conftest.py 中。

    插件文档pytest-data:http://horejsek.github.io/python-pytest-data/

    【讨论】:

      【解决方案2】:

      我发现的一个解决方案是滥用测试用例,如下所示:

      from test_for_class_a import *
      from test_for_class_b import *
      from test_for_class_c import *
      
      list_of_all_fixtures = []
      
      
      # This will force pytest to generate all sub-fixture for class a
      @pytest.mark.usefixtures(setup_class_a)
      def test_register_class_a_fixtures(setup_class_a):
          list_of_fixtures.append(setup_class_a)
      
      
      # This will force pytest to generate all sub-fixture for class b
      @pytest.mark.usefixtures(setup_class_b)
      def test_register_class_b_fixtures(setup_class_b):
          list_of_fixtures.append(setup_class_b)
      
      
      # This will force pytest to generate all sub-fixture for class c
      @pytest.mark.usefixtures(setup_class_c)
      def test_register_class_b_fixtures(setup_class_c):
          list_of_fixtures.append(setup_class_c)
      
      
      # This is the real test to apply on all fixtures
      def test_all_fixtures():
          for my_fixture in list_of_all_fixtures:
              # do something with my_fixture
      

      这隐含地依赖于所有 test_all_fixture 在所有 test_register_class* 之后执行的事实。它显然很脏,但它可以工作......

      【讨论】:

        【解决方案3】:

        我认为,只有pytest_generate_test() (example) 才能为您提供如此强大的自定义功能:

        def pytest_generate_tests(metafunc):
            if 'db' in metafunc.funcargnames:
                metafunc.addcall(param="d1")
                metafunc.addcall(param="d2")
        

        编辑:哎呀,回答了比 python 经验更老的问题,我有 o.O

        【讨论】:

          猜你喜欢
          • 2023-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多