I want to get information about exceptions raised during test call
phase and add them to report created with pytest-html
plugin. So I created hookwrapper
for pytest_runtest_makereport
:
@hookimpl(hookwrapper=True)
def pytest_runtest_makereport(call):
outcome = yield
result = outcome.get_result()
errors = getattr(result, "errors", [])
if result.when == "call":
if result.failed:
if error := call.excinfo:
errors.append(error.typename)
result.errors = errors
else:
logger.info("PASSED")
To add this information to test report, I'm using:
def pytest_html_results_table_html(report, data):
del data[:]
if errors := getattr(report, "errors", []):
data.append(html.div(", ".join(errors), class_="failed log"))
Unfortunately in pytest_html_results_table_html
there is no errors field in report instance. If I add result.errors = errors
to teardown phase, field appears in report object but it has empty list.
I know there is an extra
field, but pytest-html
adds it straight into report. I want to do something with those values before adding them.
So what I'm missing here? How to pass this value from pytest_runtest_makereport
to pytest_html_results_table_html
?
Example test class that I'm using:
from logging import getLogger
from pytest import fixture
logger = getLogger()
class TestVariousOutputs:
@fixture()
def setup(self):
logger.info("Run setup")
raise RuntimeError("Error raised during setup")
@fixture()
def teardown(self):
logger.info("Run setup")
yield
logger.info("Run teardown")
raise RuntimeError("Error raised during teardown")
def test_pass(self):
logger.info("Run test")
assert True
def test_fail(self):
logger.info("Run test")
assert False, "Assert False"
def test_raise_error(self):
logger.info("Run test")
raise RuntimeError("Error raised during test run")
def test_setup_raise_error(self, setup):
logger.info("Run test")
assert True
def test_teardown_raise_error(self, teardown):
logger.info("Run test")
assert True
def test_teardown_raise_error_and_test_fails(self, teardown):
logger.info("Run test")
assert False, "Assert False but teardown should also fail"
Because this is how pytest-html
is currently implemented: for each test case, it will take the teardown
report and copy selected fields from the call
and setup
reports. Of course, custom fields are simply ignored (duh!). You thus have to intercept this post-processing of pytest
reports and ensure your custom field is present on teardown
report. Example to put into your conftest.py
:
@pytest.hookimpl(tryfirst=True) # run our hookimpl before pytest-html does its own postprocessing
def pytest_sessionfinish(session):
html_report = getattr(session.config, "_html", None)
for rep_setup, rep_call, rep_teardown in html_report.reports.values():
# copy errors field from call report to teardown report
rep_teardown.errors = getattr(rep_call, "errors", [])