Python 根据另一个装置的值对值列表进行参数化

Python 根据另一个装置的值对值列表进行参数化,python,pytest,Python,Pytest,我有一个对象,它有一个side参数,用于确定对象的行为。我已经为side的有效值创建了一个fixture,并创建了一个关联的fixture,以生成一个用side的给定值初始化的对象 给我带来麻烦的部分是,我想在各种整数和字符串上测试我的对象的行为(例如为了简化),但我想传递给我的测试的确切值取决于side的值。我目前正在通过创建一个列表字典来实现这一点,并根据端返回整个列表,但这并不理想 固定装置示例: @pytest.fixture(params=['left', 'right']) def

我有一个对象,它有一个
side
参数,用于确定对象的行为。我已经为
side
的有效值创建了一个fixture,并创建了一个关联的fixture,以生成一个用
side
的给定值初始化的对象

给我带来麻烦的部分是,我想在各种整数和字符串上测试我的对象的行为(例如为了简化),但我想传递给我的测试的确切值取决于
side
的值。我目前正在通过创建一个列表字典来实现这一点,并根据
端返回整个列表,但这并不理想

固定装置示例:

@pytest.fixture(params=['left', 'right'])
def side(request):
    return request.param

@pytest.fixture
def my_obj(side):
    # some object that takes `side` as an arg
    return MyObj(side=side)

@pytest.fixture
def my_ints(side):
    # the ints I want to test depending on `side`
    mapping = {'left': [0, 1, 2], 'right': [11, 22]}
    return mapping[side]

@pytest.fixture
def my_strs(side):
    # the strs I want to test depending on `side`
    mapping = {'left': list('ab'), 'right': list('xyz')}
    return mapping[side]
这里的问题是
my_ints
my_strs
返回一个列表,但我想测试列表中的单个值,而不是整个列表。因此,我不得不在测试中使用
for
循环

示例测试:

def test_on_ints(my_obj, my_ints):
    for my_int in my_ints:
        # do the same general thing for each my_int
        assert my_obj.some_meth1(my_int) >= 0

def test_on_strs(my_obj, my_strs):
    for my_str in my_strs:
        # do the same general thing for each my_str
        assert 'foo' in my_obj.some_meth2(my_str)
当然,对于
侧的每个值,这只会运行每个测试一次,并提供以下输出:

test_so_example.py::test_on_ints[left] PASSED
test_so_example.py::test_on_ints[right] PASSED
test_so_example.py::test_on_strs[left] PASSED
test_so_example.py::test_on_strs[right] PASSED
理想情况下,我希望从测试中删除
for
循环,并为列表中的每个值运行测试。基本上产生测试输出,如:

test_so_example.py::test_on_ints[left-0] PASSED
test_so_example.py::test_on_ints[left-1] PASSED
test_so_example.py::test_on_ints[left-2] PASSED
test_so_example.py::test_on_ints[right-11] PASSED
test_so_example.py::test_on_ints[right-22] PASSED
(likewise for test_on_strs)
我想做的事可能吗?是否有一个不同的结构可以使这更容易

实际上,我将在许多测试中使用两个以上的side值,因此我希望避免显式写出所有
(side,int/str/etc)


我已经阅读了一些涉及间接参数化的答案,但没有找到任何可行的方法,也不确定这是否是正确的方法。

这可能不是您想要的,但它提供了您想要的测试输出:

import pytest


class MyObj:
    """Dummy Obj so tests are runnable"""
    def __init__(self, side="left"):
        self.side = side

    def some_meth1(self, i):
        return i

    def some_meth2(self, i):
        return 'foo'


def get_int_data():
    """This could come from a file or anywhere else..."""
    return [('left', 0), ('left', 1), ('left', 2), ('right', 11), ('right', 12)]


@pytest.mark.parametrize('side, my_int', get_int_data())
def test_on_ints(side, my_int):
    assert MyObj(side=side).some_meth1(my_int) >= 0


@pytest.mark.parametrize('side, my_str', [
  ('left', 'ab'),
  ('right', 'xyz'),
])
def test_on_strs(side, my_str):
    assert 'foo' in MyObj(side=side).some_meth2(my_str)
结果:

test_so_example.py::test_on_ints[left-0] PASSED                                                                                                                                        [ 14%]
test_so_example.py::test_on_ints[left-1] PASSED                                                                                                                                        [ 28%]
test_so_example.py::test_on_ints[left-2] PASSED                                                                                                                                        [ 42%]
test_so_example.py::test_on_ints[right-11] PASSED                                                                                                                                      [ 57%]
test_so_example.py::test_on_ints[right-12] PASSED                                                                                                                                      [ 71%]
test_so_example.py::test_on_strs[left-ab] PASSED                                                                                                                                       [ 85%]
test_so_example.py::test_on_strs[right-xyz] PASSED                                                                                                                                     [100%
我读过一些涉及间接参数化的答案,但是 什么都做不到,也不确定这是否正确 接近

如果您希望从一个或多个方面验证所有可能的组合,每个组合都有一个或多个值,并让这些验证在单独的测试中报告通过/失败,则pytest测试生成应该针对您的用例

看起来让人望而生畏,但是看几遍,用一个非常基本的例子练习,然后尝试你需要的,可能看起来像这样:

conftest.py

def pytest_generate_tests(metafunc):
    if set(['value', 'expected']).issubset(set(metafunc.fixturenames)):
        metafunc.parametrize('side', ['left', 'right'])
        metafunc.parametrize('value, expected', [(1, 0), (2, 0), (3, 0)])
测试一些东西

def test_can_use_parameterization(side, value, expected):
    assert value * 0 == expected

test_some_stuff.py::test_can_use_parameterization[left-1-0] ✓                                                                                                                                                                                                                                           17% █▋
 test_some_stuff.py::test_can_use_parameterization[left-2-0] ✓                                                                                                                                                                                                                                           33% ███▍
 test_some_stuff.py::test_can_use_parameterization[left-3-0] ✓                                                                                                                                                                                                                                           50% █████
 test_some_stuff.py::test_can_use_parameterization[right-1-0] ✓                                                                                                                                                                                                                                          67% ██████▋
 test_some_stuff.py::test_can_use_parameterization[right-2-0] ✓                                                                                                                                                                                                                                          83% ████████▍
 test_some_stuff.py::test_can_use_parameterization[right-3-0] ✓                                                                                                                                                                                                                                         100% ██████████

Results (0.04s):
       6 passed

谢谢,但这正是我在问题中所说的我想避免做的。@root明白了,很公平。FWIW,我已经更新了我的答案(请参见
get\u int\u data
change)。我可以理解为什么您不想将其硬编码到decorator中,但可以肯定的是,这些对是在某个地方定义的,您可以加载它们,并轻松地将它们转换为所需的元组格式。