首页 > 解决方案 > 在 pytest 中,如何中止夹具拆卸?

问题描述

我们的 pytest 环境有很多固定装置(主要是scope='function'scope='module'),它们正在执行以下形式的操作:

@pytest.yield_fixture(scope='function')
def some_fixture():
    ... some object initialization ...
    yield some_object
    ... teardown ...

我们使用fixture的teardown阶段(在yield之后)删除一些专门为测试创建的资源。

但是,如果测试失败,我不希望执行拆解,因此我们将拥有仍然存在的资源以进行进一步调试。

例如,这是一个在我们所有测试框架中重复的常见场景:

@pytest.yield_fixture(scope='function')
def obj_fixture():
    obj = SomeObj.create()
    yield obj
    obj.delete()

def test_obj_some_field(obj_fixture):
    assert obj_fixture.some_field is True

在这种情况下,如果 中的条件assertTrue我想要obj.delete()执行。obj.delete()但是,如果测试失败,我希望pytest跳过yield.

谢谢你。

编辑 我希望在不更改夹具和测试代码的情况下完成该过程,我更喜欢自动过程而不是在我们的整个测试代码库中进行此重构。

标签: pythonpytest

解决方案


pytest 文档中有一个关于如何执行此操作的示例。基本思想是,您需要在钩子函数中捕获此信息并将其添加到 testitem中,这在 test 上request可用,可通过 fixture 提供给 fixture/tests request

对你来说,它看起来像这样:

# conftest.py

import pytest

@pytest.hookimpl(tryfirst = True, hookwrapper = True)
def pytest_runtest_makereport(item, call):
    # execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # set a report attribute for each phase of a call, which can
    # be "setup", "call", "teardown"

    setattr(item, "rep_" + rep.when, rep)
# test_obj.py

import pytest


@pytest.fixture()
def obj(request):
    obj = 'obj'
    yield obj

    # setup succeeded, but the test itself ("call") failed
    if request.node.rep_setup.passed and request.node.rep_call.failed:
        print(' dont kill obj here')
    else:
        print(' kill obj here')


def test_obj(obj):
    assert obj == 'obj'
    assert False # force the test to fail

如果你运行它pytest -s(不让 pytest 捕获来自固定装置的输出),你会看到像这样的输出

foobar.py::test_obj FAILED dont kill obj here

这表明我们正在命中条件的正确分支。


推荐阅读