【问题标题】:parameterized test with cartesian product of arguments in pytest在pytest中使用参数的笛卡尔积进行参数化测试
【发布时间】:2014-03-04 12:13:53
【问题描述】:

只是想知道,有没有(更)优雅的方式使用笛卡尔积进行参数化?到目前为止,这是我想出的:

numbers    = [1,2,3,4,5]
vowels     = ['a','e','i','o','u']
consonants = ['x','y','z']

cartesian = [elem for elem in itertools.product(*[numbers,vowels,consonants])]

@pytest.fixture(params=cartesian)
def someparams(request):
  return request.param

def test_something(someparams):
  pass

至少我想在fixture函数中封装数字、元音、辅音和笛卡尔。

【问题讨论】:

    标签: python pytest


    【解决方案1】:

    您可以应用多个parametrize 参数,在这种情况下,它们将生成所有参数的乘积:

    import pytest
    
    numbers = [1,2,3,4,5]
    vowels = ['a','e','i','o','u']
    consonants = ['x','y','z']
    
    
    @pytest.mark.parametrize('number', numbers)
    @pytest.mark.parametrize('vowel', vowels)
    @pytest.mark.parametrize('consonant', consonants)
    def test(number, vowel, consonant):
        pass
    

    【讨论】:

    • 这在 Python 2 中应该可以正常工作。你遇到了什么错误? pytest 5.X 不再支持 Python 2.7 和 Python 3.4,但执行“pip install pytest”这些 Python 版本的用户将获得最后一个兼容版本 4.6.X。
    【解决方案2】:

    我可以想到两种方法来做到这一点。一种使用参数化夹具,另一种使用参数化测试功能。由你决定哪一个更优雅。

    这里是参数化的测试函数:

    import itertools
    import pytest
    
    numbers = [1,2,3,4,5]
    vowels = ['a','e','i','o','u']
    consonants = ['x','y','z']
    
    
    @pytest.mark.parametrize('number,vowel,consonant',
        itertools.product(numbers, vowels, consonants)
    )
    def test(number, vowel, consonant):
        pass
    

    值得注意的是,参数化装饰器的第二个参数可以是可迭代的,而不仅仅是列表。

    以下是通过参数化每个夹具的方法:

    import pytest
    
    numbers = [1,2,3,4,5]
    vowels = ['a','e','i','o','u']
    consonants = ['x','y','z']
    
    
    @pytest.fixture(params=numbers)
    def number(request):
        return request.param
    
    @pytest.fixture(params=vowels)
    def vowel(request):
        return request.param
    
    @pytest.fixture(params=consonants)
    def consonant(request):
        return request.param
    
    
    def test(number, vowel, consonant):
        pass
    

    你的直觉是正确的。通过参数化多个夹具中的每一个,pytest 负责创建所有出现的排列。

    测试输出是相同的。这是一个示例(我使用 -vv 选项运行 py.test):

    test_bar.py:22: test[1-a-x] PASSED
    test_bar.py:22: test[1-a-y] PASSED
    test_bar.py:22: test[1-a-z] PASSED
    test_bar.py:22: test[1-e-x] PASSED
    test_bar.py:22: test[1-e-y] PASSED
    test_bar.py:22: test[1-e-z] PASSED
    test_bar.py:22: test[1-i-x] PASSED
    

    【讨论】:

      【解决方案3】:

      我认为除了一个优雅的解决方案之外,您还应该考虑每个选项所花费的时间以及您必须维护的代码量。

      可能的解决方案

      1. parametrize 与 itertools 一起使用一次(由 Frank T 提供)
      2. 使用 3 个固定装置(由 Frank T 提供)
      3. 使用parametrize 3 次(由 Bruno Oliveira 提供)
      4. 使用 1 个夹具和 itertools(在问题中提供)

      解决方案 1

      @pytest.mark.parametrize('number, vowel, consonant',
                               itertools.product(numbers, vowels, consonants))
      def test(number, vowel, consonant):
          pass
      

      解决方案 2

      @pytest.fixture(params=numbers)
      def number(request): return request.param
      
      @pytest.fixture(params=vowels)
      def vowel(request): return request.param
      
      @pytest.fixture(params=consonants)
      def consonant(request): return request.param
      
      
      def test(number, vowel, consonant):
          pass
      

      解决方案 3

      @pytest.mark.parametrize('number', numbers)
      @pytest.mark.parametrize('vowel', vowels)
      @pytest.mark.parametrize('consonant', consonants)
      def test(number, vowel, consonant):
          pass
      

      解决方案 4

      @pytest.fixture(params=cartesian)
      def someparams(request):
        return request.param
      
      def test_something(someparams):
        pass
      

      谈到优雅,我认为解决方案 3 是最佳选择,因为它维护的代码更少,并且不需要导入 itertools。之后,Solution 1 是最佳选择,因为您不需要将fixture 编写为Solution 4Solution 2解决方案 4 可能比 解决方案 2 更好,因为它需要更少的代码来维护。

      在性能方面,我使用numbers = list(range(100)) 运行每个解决方案,得到以下结果:

      |  Solution  |  Time    | 
      | Solution 1 |  3.91s   |
      | Solution 2 |  3.59s   |
      | Solution 3 |  3.54s   |
      | Solution 4 |  3.09s   |
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-15
        • 1970-01-01
        • 1970-01-01
        • 2017-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多