2

I want to save the log files from pytest test runs only when the tests are unsuccessful, this could be from an error in the test fixtures setup or a failure in the tests themselves.

My approach for this is to create a test fixture that copies the logs following a yield, but I can't figure out how to make it conditional on the test outcome.

@pytest.fixture
def setup():
    do_setup()
    yield
    do_teardown()

@pytest.fixture
def get_logs():
    yield
    if not test_success: # how to know this condition?
        copy_log_files()

def test_run(get_logs, setup):
    do_test_stuff()
KyleL
  • 855
  • 6
  • 24

1 Answers1

2

Use pytest_runtest_makereport which as documented:

Called to create a _pytest.reports.TestReport for each of the setup, call and teardown runtest phases of a test item.

and use it along with _Result.get_result() to add the result in the Node/Item.

We can actually just follow the same steps documented in Making test result information available in fixtures

conftest.py

import pytest


@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"

    setattr(item, "rep_" + rep.when, rep)

test_module.py

import pytest


@pytest.fixture
def get_logs(request):
    yield

    if request.node.rep_call.failed:
        # Add code here to cleanup failure scenario
        print("executing test failed")
    elif request.node.rep_call.passed:
        # Add code here to cleanup success scenario
        print("executing test success")


def test_run1(get_logs):
    print("Test 1")
    assert 1


def test_run2(get_logs):
    print("Test 2")
    assert 0


def test_run3(get_logs):
    print("Test 3")
    assert 1


def test_run4(get_logs):
    print("Test 4")
    assert 0

Output

$ pytest -rP test_module.py
test_module.py .F.F                                                                           [100%]

============================================= FAILURES ==============================================
_____________________________________________ test_run2 _____________________________________________

get_logs = None

    def test_run2(get_logs):
        print("Test 2")
>       assert 0
E       assert 0

test_module.py:21: AssertionError
--------------------------------------- Captured stdout call ----------------------------------------
Test 2
------------------------------------- Captured stdout teardown --------------------------------------
executing test failed
_____________________________________________ test_run4 _____________________________________________

get_logs = None

    def test_run4(get_logs):
        print("Test 4")
>       assert 0
E       assert 0

test_module.py:31: AssertionError
--------------------------------------- Captured stdout call ----------------------------------------
Test 4
------------------------------------- Captured stdout teardown --------------------------------------
executing test failed
============================================== PASSES ===============================================
_____________________________________________ test_run1 _____________________________________________
--------------------------------------- Captured stdout call ----------------------------------------
Test 1
------------------------------------- Captured stdout teardown --------------------------------------
executing test success
_____________________________________________ test_run3 _____________________________________________
--------------------------------------- Captured stdout call ----------------------------------------
Test 3
------------------------------------- Captured stdout teardown --------------------------------------
executing test success
==================================== 2 failed, 2 passed in 0.11s ====================================