Возврат нескольких объектов из прибора Pytest
Я учусь тому, как использовать pytest, тестируя простую реализацию генератора событий.
В основном это выглядит так
class EventEmitter():
def __init__(self):
...
def subscribe(self, event_map):
# adds listeners to provided in event_map events
def emit(self, event, *args):
# emits event with given args
Для удобства я создал класс Listener
который используется в тестах
class Listener():
def __init__(self):
...
def operation(self):
# actual listener
В настоящее время тест выглядит следующим образом
@pytest.fixture
def event():
ee = EventEmitter()
lstr = Listener()
ee.subscribe({"event" : [lstr.operation]})
return lstr, ee
def test_emitter(event):
lstr = event[0]
ee = event[1]
ee.emit("event")
assert lstr.result == 7 # for example
Чтобы протестировать источник событий, мне нужно проверить, изменилось ли внутреннее состояние слушателя после распространения события. Таким образом, мне нужны два объекта, и мне интересно, есть ли лучший способ сделать это (возможно, использовать два прибора вместо одного как-нибудь), потому что это выглядит мне некрасиво.
Ответы
Ответ 1
Да! Вероятно, вам понадобится два светильника в этом случае.
вы можете попробовать @pytest.yield_fixture
как:
@pytest.yield_fixture
def event():
...
yield <event_properties>
@pytest.yield_fixture
def listener(event):
...
yield <listener_properties>
Ответ 2
Обычно, чтобы избежать tuples
и украшать ваш код, вы можете объединить их вместе с одним модулем в качестве класса, который был сделан для вас, используя collections.namedtuple
:
import collections
EventListener = collections.namedtuple('EventListener', 'event listener')
Теперь измените ваше приспособление:
@pytest.fixture
def event_listener():
e = EventListener(EventEmitter(), Listener())
e.event.subscribe({'event' : [e.listener.operation]})
return e
Теперь измените свой тест:
def test_emitter(event_listener):
event_listener.event.emit('event')
assert event_listener.listener.result == 7
Ответ 3
Вы должны использовать функцию Python, называемую итеративную распаковку в переменные.
def test_emitter(event):
lstr, ee = event # unpacking
ee.emit("event")
assert lstr.result == 7
По сути, вы назначаете event[0]
для lstr
, а event[1]
для ee
. Использование этой функции - очень элегантный способ избежать использования индексов.
Отбросив
Если вы собираетесь использовать свой прибор в множественных тестах и вам не нужны все значения в каждом тесте, вы также можете отказаться от некоторых элементов итерируемого, если вы не заинтересованы в их использовании следующим образом:
l = ['a', 'b', 'c', 'd']
a, b, c, d = l # unpacking all elements
a, _, c, d = l # discarding b
a, _, _, d = l # python 2: discard b and c
a, *_, d = l # python 3: discard b and c
a, _, _, _ = l # python2: discard, b, c and d
a, *_ = l # python3: discard b, c, and d
Теоретически, вы буквально не отбрасываете значения, но в Python _
, так называемое "мне все равно", используется для игнорирования определенных значений.
Ответ 4
Если вы не можете позволить себе легко разделить свое устройство кортежа на два независимых устройства, теперь вы можете "распаковать" устройство кортежа или списка в другие устройства, используя мой pytest-cases
как описано в этом ответе.
Для вашего примера это будет выглядеть так:
from pytest_cases import pytest_fixture_plus
@pytest_fixture_plus(unpack_into="lstr,ee")
def event():
ee = EventEmitter()
lstr = Listener()
ee.subscribe({"event" : [lstr.operation]})
return lstr, ee
def test_emitter(lstr, ee):
ee.emit("event")
assert lstr.result == 7 # for example