pytest 如何创建固定装置,或者如何获得我的产量?

问题描述 投票:0回答:1

我有一个生成器功能

def foo():
    resource = setup()
    yield resource
    tidy(resource)

我用它作为固定装置

@pytest.fixture
def foofix():
    yield from foo()

这个效果很好。

我想测试

foo

def test_foo():
    res_gen = foo()
    res = next(res_gen)
    assert res.is_working

但是因为我在

resource
之后立即清理了
yield
,所以不再可以断言它正在工作。那么 pytest 在整理之前如何使用
resource
,我该如何做同样的事情?

python pytest generator pytest-fixtures
1个回答
0
投票

如果你想测试 foo,你只需在代码中调用它,而不是将其包装在 pytest 固定装置中。

夹具对于需要生成一些样板的值很有用,并且,当这些值必须在测试后整理时,以对测试透明的方式 - 以便测试可以消耗该值,并执行不用担心任何样板。

如果您想测试

foo

 生成器本身,而不仅仅是在测试中消耗其值,则将其包装在固定装置中是没有意义的 - 只需将其或其模块导入包含测试函数的模块中,通过调用启动生成器,然后调用 
next
 并捕获 
StopIteration
(如果需要)。

当你跑步时

def test_foo(): res_gen = foo() res = next(res_gen) assert res.is_working
作为普通的 Python 生成器,在 

next

 上调用 
foo()
 后,您将在释放资源之前获得资源:当执行 
yield resource
 时,生成器中的执行会在 
assert res.is_working
 表达式处暂停。只有在随后调用 
next
 时,
tidy(resource)
 线路才会运行。

实际上,这是您设计中的

另一个问题:生成器不会以任何方式确保它们将运行完成(因此完成您的资源) - 在这样的模式中,由调用者调用next

 直到发电机耗尽。

当您将生成器标记为 pytest 固定装置时,情况会有所不同 - 在这种情况下,

pytest 将在第一次产量之后、测试结束后再次运行生成器 - 但这是 pytest“好东西”,因为它使用这种模式来启动和清理固定装置,而不是发电机的自然行为。

与之接近的模式是使用 Python 的

contextlib.contextmanager

 调用来装饰这样的生成器,然后您的生成器将转换为上下文管理器,可以用作 
with
 命令中的表达式。当
with
命令结束时,执行
yield
下面的生成器代码。

所以,也许你想要这个:

from contextlib import contextmanager @contextmanager def foo(): resource = setup() # try/finally block is needed, otherwise, if an exception # occurs where resource is used in a `with` block, # the finalization code is not executed. try: yield resource finally: tidy(resource) def test_foo(): with foo() as res: # here, you have "resource" and it has not terminated assert res.is_working # the end of the "with" block resumes the generator and frees the resource return # even if it is an implicit return.
    
© www.soinside.com 2019 - 2024. All rights reserved.