Search code examples
pythonreportpytestpytest-html

Pytest-html custom report


I'm using pytest to run my tests, and using pytest html to generate a report.

i'm trying to show the error\skip message in cases of failure or skip in the report, using the value form call.excinfo.value.
i notice that pytest_runtest_makereport is being called multiple times, for setup, call and teardown, and since call.excinfo.value in setup and teardown is null, it is overwriting the message in the cell, and as a result the error message cell is empty.

so, i tried to update the value with the following condition report.when == "call",
but i'm getting the following error when pytest_html_results_table_row is executing on setup and teardown:

AttributeError: 'TestReport' object has no attribute 'error_message'

here is the code that i tried:

# in conftest.py
@pytest.mark.optionalhook
def pytest_html_results_table_header(cells):
    cells.insert(1, html.th('Error Message'))


@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):
    cells.insert(1, html.td(report.error_message))



@pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.skipped or report.failed:
        report.error_message = str(call.excinfo.value) if call.excinfo else ""
   

is there another way to show the error message in the report for failure\skipped.
p.s: for tests that are passed the value should be an empty string

here is what I expect to achieve: expected result


Solution

  • I've found a way to solve this issue, i hope it will help someone in the future.
    This is a workaround to handle the issue where teardown overwrites the value that was set during call phase.

    basically, after the teardown phase i'm overwriting the value in the error_message with the value that was set during the call phase.

    Note: please take in consideration that this will show only the error message from the call phase

    @pytest.hookimpl(hookwrapper=True)
    def pytest_runtest_makereport(item, call):
        outcome = yield
        report = outcome.get_result()
        report.error_message = ""
    
    
        # set a report attribute for each phase of a call, which can be "setup", "call", "teardown"
        setattr(item, "rep_" + report.when, report)
        report.error_message = str(call.excinfo.value) if call.excinfo else ""
        if report.when == "teardown":
            # retrieving the error messages from the call phase
            report.error_message = item.rep_call.error_message
    
    

    see image attached report