Skip to content

Cannot test for correct cancellation handling behaviour inside a fixture #117

Open
@basak

Description

@basak

A pattern I've been using is handing a class instance a nursery when it is constructed, so that it can then run background tasks. Things that are working well:

  • This pattern.
  • In one case a background task performing cleanup by handling trio.Cancelled.
  • I have a fixture that constructs a specialised instance of such a class for test purposes, and am testing a bunch of class functionality effectively.

However, I'm struggling to write a test that uses the fixture, cancels the nursery given to the class instances, and asserts that the cleanup was performed correctly. The docs say:

So what happens if the yield gets cancelled?

First, pytest-trio assumes that something has gone wrong and there’s no point in continuing the test. If the top-level test function is running, then it cancels it.

...and this is what is biting me here. Here's a reduced example:

import pytest
import pytest_trio
import trio


class Foo:
    def __init__(self, nursery):
        self.nursery = nursery
        self.handled_cancellation = False
        nursery.start_soon(self.foo)

    async def foo(self):
        try:
            await trio.sleep_forever()
        except trio.Cancelled:
            self.handled_cancellation = True


@pytest_trio.trio_fixture
async def foo_fixture():
    async with trio.open_nursery() as nursery:
        yield Foo(nursery)

@pytest.mark.trio
async def test_foo(foo_fixture):
    foo_fixture.nursery.cancel_scope.cancel()

    # This line seems to cause the test to terminate
    await trio.testing.wait_all_tasks_blocked()

    assert foo_fixture.handled_cancellation  # this assert never runs
    assert False  # if the test passes then this line definitely never ran

I notice that in newer releases, the test fails with RuntimeError: <fixture 'foo_fixture'> cancelled the test but didn't raise an error which is nice (in older releases the test silently passes).

The behaviour is therefore as documented, but that leads me to the problem: how do I test cancellation behaviour in an object created by a fixture?

Workaround: I can copy the fixture code into a test. But this seems ugly as it requires duplication, precluding code reuse.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions