在pytest中,我该如何终止灯具拆卸?

时间:2019-12-26 16:33:22

标签: python pytest

我们的pytest环境具有许多固定装置(主要是scope='function'scope='module'),它们的形式如下:

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

我们使用夹具的拆卸阶段(在成品率之后)删除一些专门为测试创建的资源。

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

例如,这是在我们所有测试框架中都重复出现的常见情况:

@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

在这种情况下,如果assert中的条件为True,我希望obj.delete()执行。 但是,如果测试失败,我希望pytest跳过obj.delete()yield之后的其他内容。

谢谢。

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

3 个答案:

答案 0 :(得分:2)

pytest docs中有一个有关如何执行此操作的示例。基本思想是,您需要在挂钩函数中捕获此信息并将其添加到测试item中可用的测试request中,测试request可以通过{{ 1}}固定装置。

对您来说,看起来像这样:

# 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

这表明我们正在到达条件的右分支。

答案 1 :(得分:0)

拆卸的目的是与测试通过还是失败无关。

因此,我建议编写您的拆卸代码,使其足够健壮,无论测试通过还是失败都可以执行,或者将清理添加到测试的末尾,以便在没有前面的情况下被调用分类失败,如果之前没有发生异常

答案 2 :(得分:0)

设置一个类级别标志以指示通过/失败,并在拆解中进行检查。这没有经过测试,但是应该可以给您带来启发:

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

def test_obj_some_field(obj_fixture):
    assert obj_fixture.some_field is True
    this.passed = True