[TIP] parametrize a pytest finalizer

Carl Meyer carl at oddbird.net
Wed Apr 9 14:55:48 PDT 2014


Hi Chris,

On 04/09/2014 03:15 PM, Pella,Chris wrote:
> It doesn’t seem possible to pass arguments to a pytest finalizer. I can
> work around it this way, but it seems exceedingly ugly:
> 
>  
> 
> @pytest.fixture(scope = "class")
> def test_fixture(request):
> 
>     global cleanup_parameter
>     cleanup_parameter = "Cleaning up"
> 
>     def cleanup():
>         global cleanup_parameter
>         print cleanup_parameter
> 
>     try:
>         raise
>     except Exception:
>         cleanup_parameter = "Problem in fixture!"
>         cleanup()
> 
>     request.addfinalizer(cleanup)
> 
> Is there a better way?

I would say the better way in modern pytest is to use yield_fixture
rather than request.addfinalizer:

@pytest.yield_fixture(scope='class')
def test_fixture():
    cleanup_parameter = "Cleaning up"
    try:
        raise
    except Exception:
        cleanup_parameter = "Problem in fixture!"
    yield None
    print cleanup_parameter

This gives you flexibility to do whatever you like in cleanup, without
the constraint of "pass a callable to addfinalizer." It also puts the
code in execution order (once you understand that your test runs at the
point of the "yield"), which is nice.

(You'd actually yield whatever you'd have returned from the non-yield
fixture; I used None here to make it equivalent to what you posted).

A couple other notes about the version you posted:

1) Why does it call cleanup() in the except clause, but then still add a
finalizer? That will result in cleanup() being run twice, once before
the test and once after it.

2) There is no need for all those "global" declarations; closures can
access variables in their parent scope with no such declaration. Remove
both global statements and your version should still work the same.

HTH,

Carl



More information about the testing-in-python mailing list