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中,但可以肯定的是,这些对是在某个地方定义的,您可以加载它们,并轻松地将它们转换为所需的元组格式。