#python #pytest
#python #pytest
Вопрос:
Я использую pytest
косвенную параметризацию для параметризации восходящего устройства. Для меня это работает нормально.
Однако теперь я застрял, когда вышестоящие приборы имеют одинаковые имена аргументов, и я хочу передать им разные значения.
Как я могу использовать косвенную параметризацию, когда аргументы вышестоящего устройства, подлежащего параметризации, имеют одинаковые имена?
Пример кода
import pytest
class Config:
"""This is a configuration object."""
def __init__(self, a: int, b: int):
self._a = a
self._b = b
@pytest.fixture
def config(a: int, b: int) -> Config:
return Config(a, b)
class Foo:
def __init__(self, config: Config):
"""This does some behavior."""
self.config = config
@pytest.fixture
def foo(config: Config) -> Foo:
return Foo(config)
class Bar:
def __init__(self, config: Config):
"""This does some other behavior."""
self.config = config
@pytest.fixture
def bar(config: Config) -> Bar:
return Bar(config)
class TestFooBarTogether:
@pytest.mark.parametrize("a, b", [(1, 2)])
def test_foo_alone(self, foo: Foo) -> None:
"""This works fine, since parameters get passed all the way to Config."""
@pytest.mark.parametrize("a, b", [(1, 2)])
def test_together(self, foo: Foo, bar: Bar) -> None:
"""Test interactions between the two objects.
This does not work, because both foo and bar get the same config.
How can I pass a different config to foo and bar, even though Config
takes both an "a" and a "b"?
I would like to pass `foo` something like (1, 2) and
`bar` something like (3, 4).
"""
Ответ №1:
Обновить
Я не вижу возможности использовать config
прибор при параметризации обоих foo
и bar
с разными Config
экземплярами. Отбрасывая config
прибор и параметризируя foo
и bar
косвенно, я прихожу к:
@pytest.fixture
def foo(request) -> Foo:
return Foo(Config(*request.param))
@pytest.fixture
def bar(request) -> Bar:
return Bar(Config(*request.param))
@pytest.mark.parametrize("foo", [(1, 2)], indirect=True)
def test_foo_alone(foo: Foo) -> None:
assert foo.config._a == 1
assert foo.config._b == 2
@pytest.mark.parametrize("foo,bar", [((1, 2), (3, 4))], indirect=True)
def test_together(foo: Foo, bar: Bar) -> None:
assert foo.config._a == 1
assert foo.config._b == 2
assert bar.config._a == 3
assert bar.config._b == 4
Ожидаемое config
a
и b
параметризованное приспособление здесь все равно не подойдет, IMO, поскольку ожидается, что оно будет вызываться new при каждом вызове, как обычная функция — вместо этого я просто использую Config
конструктор.
Оригинальный ответ
Вы всегда можете переключиться на заводские приспособления, если вам нужно обеспечить новые объекты при каждом config
вызове. Аргументы a
и b
инкапсулируются на заводе, поэтому не должно быть необходимости перетаскивать их.
ConfigFactory = Callable[[], Config]
@pytest.fixture
def config(a: int, b: int) -> ConfigFactory:
return lambda: Config(a, b)
@pytest.fixture
def foo(config: ConfigFactory) -> Foo:
return Foo(config())
@pytest.fixture
def bar(config: ConfigFactory) -> Bar:
return Bar(config())
@pytest.mark.parametrize("a, b", [(1, 2)])
def test_together(foo: Foo, bar: Bar) -> None:
assert not foo.config == bar.config
Комментарии:
1. Спасибо @hoefling, это почти работает. Однако это еще не совсем так. Я хочу предоставить что-то вроде
(1, 2)
tofoo
и(3, 4)
tobar
. Другими словами, фабрика успешно создает экземпляры разныхConfig
объектов (мы на полпути), но у них одинаковые переданные аргументы (a, b
) . Есть ли какой-нибудь способ передать другой набор значений вbar
‘sConfigFactory
? Я также обновил вопрос, чтобы сделать это более понятным.2. @IntrastellarExplorer Теперь я понимаю, что вы имеете в виду — действительно, это сложно. Я обновил ответ предложением по измененным требованиям, но он
config
полностью устраняет приспособление. Может быть, кто-то придумает лучшее предложение.3. Еще раз спасибо @hoefling. Я закончил тем, что делал подобное несколько недель назад после публикации этого вопроса. Однако я не думал об использовании этого
request.param
метода.
Ответ №2:
Возможным обходным путем может быть создание нового приспособления, которое принимает две разные конфигурации. Вот два способа сделать это.
@pytest.fixture
def foo_or_bar(config1: Config, config2: Config):
return Foo(config1), Bar(config2)
class TestFooBarTogether:
@pytest.mark.parametrize("a, b", [(1, 2)])
def test_foo_alone(self, foo: Foo) -> None:
"""This works fine, since parameters get passed all the way to Config."""
@pytest.mark.parametrize("config1, config2", [(Config(1, 2), Config(3, 4))])
def test_together(self, foo_or_bar) -> None:
"""Test interactions between the two objects.
"""
(foo, bar) = foo_or_bar
assert foo.config._a == 1
assert foo.config._b == 2
assert bar.config._a == 3
assert bar.config._b == 4
Или
@pytest.fixture
def foo_or_bar(a_b, c_d):
(a, b) = a_b
(c,d) = c_d
config1 = Config(a,b)
config2 = Config(c,d)
return Foo(config1), Bar(config2)
class TestFooBarTogether:
@pytest.mark.parametrize("a, b", [(1, 2)])
def test_foo_alone(self, foo: Foo) -> None:
"""This works fine, since parameters get passed all the way to Config."""
assert isinstance(foo.config, Config)
@pytest.mark.parametrize("a_b, c_d", [((1, 2), (3, 4))])
def test_together(self, foo_or_bar) -> None:
"""Test interactions between the two objects.
This does not work, because both foo and bar get the same config.
How can I pass a different config to foo and bar, even though Config
takes both an "a" and a "b"??
"""
(foo, bar) = foo_or_bar
assert foo.config._a == 1
assert foo.config._b == 2
assert bar.config._a == 3
assert bar.config._b == 4