拆卸后 Pytest 断言夹具

Pytest asserting fixture after teardown

我有一个测试,可以制作一个东西,验证它,删除它并确认它已被删除。

def test_thing():
    thing = Thing()  # Simplified, it actually takes many lines to make a thing

    assert thing.exists

    thing.delete()  # Simplified, it also takes a few lines to delete it

    assert thing.deleted

接下来我想做更多的测试都使用这个东西,所以下一步很自然地将这个东西 creation/deletion 移动到一个夹具中

@pytest.fixture
def thing():
    thing = Thing()  # Simplified, it actually takes many lines to make a thing
    yield thing
    thing.delete()  # Simplified, it also takes a few lines to delete it

def test_thing(thing):
    assert thing.exists

def test_thing_again(thing):
    # Do more stuff with thing

...

但现在我失去了我的assert thing.deleted

我觉得这里有一些选择,但 none 令人满意。

  1. 我可以在 fixture 中断言,但据我所知,在 fixture 中放置断言是不好的做法,因为如果它失败,它将导致 ERROR 而不是 FAIL
  2. 我可以保留我的原始测试并创建夹具,但这会导致 creating/deleting 这个东西有很多重复的代码。
  3. 我无法直接调用夹具,因为我得到了一个 Fixture called directly 异常,所以我可以将事物创建移到夹具和测试都使用的生成器中。不过这感觉很笨拙,如果我的 thing 灯具需要使用另一个灯具会怎样?

我最好的选择是什么?有什么我没有想到的吗?

如果你想测试一个“东西”是否被删除,制作一个没有拆解的夹具,在测试中删除它,然后断言它是否被删除。

@pytest.fixture
def thing_create():
    # Perform all the creation steps
    thing = Thing()
    ...

    yield thing


def thing_delete(thing):
    # Perform all the deletion steps
    ...
    thing.delete()  


@pytest.fixture
def thing_all(thing_create):
    yield thing_create
    thing_delete(thing_create)


def test_thing(thing_all):
    assert thing_all.exists


def test_thing_again(thing_create):
    thing_delete(thing_create)
    assert thing_create.deleted

在适当的地方(以减少重复)而不是在你的逻辑只存在一次的地方使用 fixture 如何?

@pytest.fixture
def thing():
    thing = Thing()  # Simplified, it actually takes many lines to make a thing
    yield thing
    thing.delete()  # Simplified, it also takes a few lines to delete it

def test_thing(thing):
    assert thing.exists

def test_thing_does_a_thing(thing):
    expected = "expected"
    assert thing.do_thing() == expected

def test_thing_deletes():
    # just don't use the fixture here
    thing = Thing()
    thing.delete()
    assert thing.deleted

另一种解决方案可能是拥有一个产生上下文管理器的固定装置,这样测试就可以完全控制调用它。

@pytest.fixture
def gen_thing():

    @contextmanager
    def cm():
        thing = Thing()  # Simplified, it actually takes many lines to make a thing
        try:
            yield thing
        finally:
            thing.delete()  # Simplified, it also takes a few lines to delete it

    yield cm


def test_thing(gen_thing):
    with gen_thing() as thing:
        assert thing.exists
    assert thing.deleted


def test_thing_again(gen_thing):
    with gen_thing() as thing:
        # Do more stuff with thing

将上下文管理器创建为闭包意味着它也将具有与固定装置相同的作用域。