从 pytest 夹具生成的工厂中产生

Yielding from a factory generated by a pytest fixture

下面的 pytest fixture

@pytest.fixture(scope="module")
def gen_factory():
    def _gen_factory(owner: str):

        value = ...

        yield value

        print('gen teardown')

    yield _gen_factory

产生多次使用的工厂。

在测试用例中,我使用该夹具创建两个工厂并使用它们生成一些值:

@pytest.mark.asyncio
def test_case(gen_factory):
    gen1 = gen_factory('owner1')
    gen2 = gen_factory('owner2')
    val1 = next(gen1)
    val2 = next(gen2)

    ...

    next(gen1)
    next(gen2)

发生的情况是 print('gen teardown') 仅被调用一次,然后循环关闭,第二次 next() 调用引发 StopIteration 错误。

我在这里错过了什么?为什么第二次打印没有发生?

如果您不想更改夹具中的任何内容,只需将最后一次调用更改为 next:

next(gen1, None)

使用默认值不会引发 StopIteration

另一种方法是使 _gen_factory 成为上下文管理器:

from contextlib import contextmanager

def gen_factory():
    @contextmanager
    def _gen_factory(owner):
        value = owner
        yield value
        print('gen teardown')
    yield _gen_factory
    

def test_case(genfactory):   
    with genfactory('owner1') as own1, genfactory('owner2') as own2:
        print(own1, own2)

在执行最后一条语句 (next(gen2)) 之前,错误发生在第二次调用 next(gen1) 的(最后一行)行上。迭代器已耗尽。这就是发电机的工作原理。 StopIteration 在生成器的末尾引发,(这就是打印第一个的原因!) 一旦它们没有要迭代的项目,除非传递默认值到内置 next 函数。 StopIteration 使 for 循环停止。尽管生成器可以以不同的方式使用,但它们的使用主要是在 for 循环中迭代。这就是为什么默认情况下会引发此 'exception'。

看到这个tutorial on generators