Mugen
Mugen

Reputation: 9095

Pytest is skipping post yield of contextmanager when assertion fails

I have a custom contextmanager I use (not a fixture) for setup and cleanup of a test:

@contextmanager
def db_content(*args, **kwargs):
    instance = db_insert( ... )

    yield instance

    db_delete(instance)

def test_my_test():
    with db_content( ... ) as instance:
        #  ...
        assert result

The problem is that when the assertion fails, the db_delete() code - meaning the post yield statements, are not being executed.

I can see that if I use a fixture this does work.

@pytest.fixture
def db_instance():
    instance = db_insert( ... )

    yield instance

    db_delete(instance)

def test_my_test(db_instance):
        #  ...
        assert result

However, fixtures are very inflexible. I would like to pass different arguments to my context each test, while using fixtures would force me to define a different fixture for each case.

Upvotes: 1

Views: 764

Answers (1)

Mugen
Mugen

Reputation: 9095

contextlib does not execute the post-yield statements if an exception was thrown. This is by design. To make it work you would have to write:

@contextmanager
def db_content(*args, **kwargs):
    instance = db_insert( ... )

    try:
        yield instance

    finally:
        db_delete(instance)

In my opinion this is counter-intuitive as the try is not on the yield itself.

I took the implementation of contextmanager and made a safe version one that works as I expected, however its an entire code duplication, if anyone has a better workaround I'd love to see it.

Upvotes: 2

Related Questions