Skip to content

after5cst/python_module_teardown

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python_module_teardown

Example code to show how to do module-level teardown on error (only).

This was originally written to answer a question on StackOverflow:

https://stackoverflow.com/questions/31432013/pytest-skip-module-teardown

The previous answer posted on StackOverflow and link to documentation was helpful but not sufficient for my needs. I needed a module teardown function to execute for each module independently if any test in that module (.py) file failed.

To start with, we need a hook to attach the test function result to the test node. This is taken directly from the pytest docs:

# in conftest.py
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()
    # set a report attribute for each phase of a call, which can
    # be "setup", "call", "teardown"
    var_name = "rep_" + rep.when
    setattr(item, var_name, rep)

After that, we need another hook for the test case to find the module and store itself there, so the module can easily find its test cases. Perhaps there's a better way, but I was unable to find one.

# also in conftest.py
@pytest.fixture(scope="function", autouse=True)
def _testcase_exit(request):
    yield
    parent = request.node.parent
    while not isinstance(parent, pytest.Module):
        parent = parent.parent
    try:
        parent.test_nodes.append(request.node)
    except AttributeError:
        parent.test_nodes = [request.node]

Once we do that, it's nice to have a decorator function to have the module on completion look through its test nodes, find if there are any failures, and then if there were call the function associated with the decorator:

# also also in conftest.py
def module_error_teardown(f):
    @wraps(f)
    @pytest.fixture(scope="module", autouse=True)
    def wrapped(request, *args, **kwargs):
        yield
        try:
            test_nodes = request.node.test_nodes
        except AttributeError:
            test_nodes = []

        something_failed = False
        for x in test_nodes:
            try:
                something_failed |= x.rep_setup.failed
                something_failed |= x.rep_call.failed
                something_failed |= x.rep_teardown.failed
            except AttributeError:
                pass
        if something_failed:
            f(*args, **kwargs)
    return wrapped

Now we have all the necessary framework to work with. Now, a test file with a failing test case is easy to write:

from conftest import module_error_teardown


def test_something_that_fails():
    assert False, "Yes, it failed."


def test_something_else_that_fails():
    assert False, "It failed again."


@module_error_teardown
def _this_gets_called_at_the_end_if_any_test_in_this_file_fails():
    print('')
    print("Here's where we would do module-level cleanup!")

About

Example code to show how to do module teardown on error.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages