从 pytest fixture 返回多个对象

Returning multiple objects from a pytest fixture

我正在通过测试一个简单的事件发射器实现来学习如何使用 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 用于测试

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

为了测试事件发射器,我需要检查事件传播后监听器的内部状态是否发生了变化。因此,我需要两个对象,我想知道是否有更好的方法来做到这一点(也许使用两个固定装置而不是一个固定装置)因为这对我来说有点难看。

在这种情况下,您可能需要两个灯具。

您可以尝试 @pytest.yield_fixture,例如:

@pytest.yield_fixture
def event():
    ...
    yield <event_properties>

@pytest.yield_fixture
def listener(event):
    ...
    yield <listener_properties>

注意:现在已弃用https://docs.pytest.org/en/latest/yieldfixture.html

通常为了避免tuples和美化你的代码,你可以将它们重新组合成一个单元作为一个class,这已经为你完成了,使用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

您应该在变量中使用名为 iterable unpacking 的 Python 功能。

def test_emitter(event):
    lstr, ee = event # unpacking
    ee.emit("event")
    assert lstr.result == 7

基本上,您将 event[0] 分配给 lstr,将 event[1] 分配给 ee。使用此功能是避免使用索引的一种非常优雅的方式。

丢弃

如果你打算在多个测试中使用你的夹具,并且你不需要在每个测试中使用所有值,如果你对使用它们不感兴趣,你也可以丢弃 iterable 的一些元素,如下所示:

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

理论上,你不是字面上的丢弃值,而是in Python_,so-called“我不在乎”,用于忽略特定值。

如果买不起就随便, you can now "unpack" a tuple or list fixture into other fixtures using my pytest-cases plugin as explained in this answer.

对于您的示例,它看起来像:

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