Search code examples
pythonpluginspytestteardown

Is pytest_runtest_teardown called for skipped tests while pytest_runtest_setup is not?


I have a plugin that implements the following hooks:

def pytest_runtest_setup(item):
    item.config.bla = Bla()

def pytest_runtest_teardown(item):
    item.config.bla.do_bla()
    item.config.bla = None

All works fine until some tests start to throw AttributeError: 'NoneType' object has no attribute 'do_bla' and indeed, item.config.bla is None

This happens in tests which I marked as

@pytest.mark.skip(reason='bla bla')
def test_bla():
    pass

I tried ipdb-ing the setup hook - but it is not called, while the teardown is. Does it make sense that setups are not called for skip tests while teardowns are?

I can wrap my teardown with try, except but I want to verify the root cause...


Solution

  • The problem seems to be that the pytest_runtest_setup hook is implemented by several components in pytest itself, one being the skipping module (_pytest/skipping.py).
    The implementation does something like this:

    @hookimpl(tryfirst=True)
    def pytest_runtest_setup(item):
        ...
        for skip_info in item.iter_markers(name="skip"):
            item._store[skipped_by_mark_key] = True
            if "reason" in skip_info.kwargs:
                skip(skip_info.kwargs["reason"])
            elif skip_info.args:
                skip(skip_info.args[0])
            else:
                skip("unconditional skip")
    

    e.g. if it finds a skip mark, it raises Skipped() (which is basically all that skip does), which is caught only after any pre-test and test hooks would be executed.

    I don't know if this intentional, but you obviously have to expect this behavior (which you can easily do in your case by catching the AttributeError, as you wrote).