Post process html to include teardown in log (#271)
This commit is contained in:
parent
c5a82636be
commit
f2d41db105
|
@ -15,6 +15,10 @@ Release Notes
|
|||
|
||||
* Thanks to `@Zac-HD <https://github.com/Zac-HD>`_ for the fix
|
||||
|
||||
* Post process HTML generation to allow teardown to appear in the HTML output. (`#131 <https://github.com/pytest-dev/pytest-html/issues/131>`_)
|
||||
|
||||
* Thanks to `@iwanb <https://github.com/iwanb>`_ for reporting and `@csm10495 <https://github.com/csm10495>`_ for the fix
|
||||
|
||||
**2.1.1 (2020-03-18)**
|
||||
|
||||
* Fix issue with funcargs causing failures. (`#282 <https://github.com/pytest-dev/pytest-html/issues/282>`_)
|
||||
|
|
|
@ -11,6 +11,7 @@ import time
|
|||
import warnings
|
||||
from base64 import b64decode
|
||||
from base64 import b64encode
|
||||
from collections import defaultdict
|
||||
from collections import OrderedDict
|
||||
from functools import lru_cache
|
||||
from html import escape
|
||||
|
@ -148,6 +149,7 @@ class HTMLReport:
|
|||
self.rerun = 0 if has_rerun else None
|
||||
self.self_contained = config.getoption("self_contained_html")
|
||||
self.config = config
|
||||
self.reports = defaultdict(list)
|
||||
|
||||
class TestResult:
|
||||
def __init__(self, outcome, report, logfile, config):
|
||||
|
@ -279,7 +281,12 @@ class HTMLReport:
|
|||
def append_log_html(self, report, additional_html):
|
||||
log = html.div(class_="log")
|
||||
if report.longrepr:
|
||||
for line in report.longreprtext.splitlines():
|
||||
# longreprtext is only filled out on failure by pytest
|
||||
# otherwise will be None.
|
||||
# Use full_text if longreprtext is None-ish
|
||||
# we added full_text elsewhere in this file.
|
||||
text = report.longreprtext or report.full_text
|
||||
for line in text.splitlines():
|
||||
separator = line.startswith("_ " * 10)
|
||||
if separator:
|
||||
log.append(line[:80])
|
||||
|
@ -620,15 +627,66 @@ class HTMLReport:
|
|||
with open(style_path, "w", encoding="utf-8") as f:
|
||||
f.write(self.style_css)
|
||||
|
||||
def _post_process_reports(self):
|
||||
for test_name, test_reports in self.reports.items():
|
||||
outcome = "passed"
|
||||
wasxfail = False
|
||||
failure_when = None
|
||||
full_text = ""
|
||||
extras = []
|
||||
duration = 0.0
|
||||
|
||||
# in theory the last one should have all logs so we just go
|
||||
# through them all to figure out the outcome, xfail, duration,
|
||||
# extras, and when it swapped from pass
|
||||
for test_report in test_reports:
|
||||
full_text += test_report.longreprtext
|
||||
extras.extend(getattr(test_report, "extra", []))
|
||||
duration += getattr(test_report, "duration", 0.0)
|
||||
|
||||
if (
|
||||
test_report.outcome not in ("passed", "rerun")
|
||||
and outcome == "passed"
|
||||
):
|
||||
outcome = test_report.outcome
|
||||
failure_when = test_report.when
|
||||
|
||||
if hasattr(test_report, "wasxfail"):
|
||||
wasxfail = True
|
||||
|
||||
if test_report.outcome == "rerun":
|
||||
self.append_other(test_report)
|
||||
|
||||
# the following test_report.<X> = settings come at the end of us
|
||||
# looping through all test_reports that make up a single
|
||||
# case.
|
||||
|
||||
# outcome on the right comes from the outcome of the various
|
||||
# test_reports that make up this test case
|
||||
# we are just carrying it over to the final report.
|
||||
test_report.outcome = outcome
|
||||
test_report.when = "call"
|
||||
test_report.nodeid = test_name
|
||||
test_report.longrepr = full_text
|
||||
test_report.extra = extras
|
||||
test_report.duration = duration
|
||||
|
||||
if wasxfail:
|
||||
test_report.wasxfail = True
|
||||
|
||||
if test_report.outcome == "passed":
|
||||
self.append_passed(test_report)
|
||||
elif test_report.outcome == "skipped":
|
||||
self.append_skipped(test_report)
|
||||
elif test_report.outcome == "failed":
|
||||
test_report.when = failure_when
|
||||
self.append_failed(test_report)
|
||||
|
||||
# we don't append other here since the only case supported
|
||||
# for append_other is rerun, which is handled in the loop above
|
||||
|
||||
def pytest_runtest_logreport(self, report):
|
||||
if report.passed:
|
||||
self.append_passed(report)
|
||||
elif report.failed:
|
||||
self.append_failed(report)
|
||||
elif report.skipped:
|
||||
self.append_skipped(report)
|
||||
else:
|
||||
self.append_other(report)
|
||||
self.reports[report.nodeid].append(report)
|
||||
|
||||
def pytest_collectreport(self, report):
|
||||
if report.failed:
|
||||
|
@ -638,6 +696,7 @@ class HTMLReport:
|
|||
self.suite_start_time = time.time()
|
||||
|
||||
def pytest_sessionfinish(self, session):
|
||||
self._post_process_reports()
|
||||
report_content = self._generate_report(session)
|
||||
self._save_report(report_content)
|
||||
|
||||
|
|
|
@ -980,3 +980,59 @@ class TestHTML:
|
|||
result, html = run(testdir)
|
||||
assert result.ret == 0
|
||||
assert len(re.findall(content_report_title, html)) == 1
|
||||
|
||||
def test_setup_and_teardown_in_html(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(scope="function")
|
||||
def setupAndTeardown():
|
||||
print ("this is setup")
|
||||
yield
|
||||
print ("this is teardown")
|
||||
|
||||
def test_setup_and_teardown(setupAndTeardown):
|
||||
print ("this is the test case")
|
||||
"""
|
||||
)
|
||||
result, html = run(testdir)
|
||||
assert result.ret == 0
|
||||
assert_results(html, tests=1, passed=1)
|
||||
assert "this is setup" in html
|
||||
assert "this is teardown" in html
|
||||
assert "this is the test case" in html
|
||||
|
||||
def test_setup_failures_are_errors(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(scope="function")
|
||||
def setup():
|
||||
assert 0, "failure!"
|
||||
|
||||
def test_setup(setup):
|
||||
print ("this is the test case")
|
||||
"""
|
||||
)
|
||||
result, html = run(testdir)
|
||||
assert result.ret == 1
|
||||
assert_results(html, tests=0, passed=0, errors=1)
|
||||
assert "this is the test case" not in html
|
||||
|
||||
def test_teardown_failures_are_errors(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(scope="function")
|
||||
def teardown():
|
||||
yield
|
||||
assert 0, "failure!"
|
||||
|
||||
def test_setup(teardown):
|
||||
print ("this is the test case")
|
||||
"""
|
||||
)
|
||||
result, html = run(testdir)
|
||||
assert result.ret == 1
|
||||
assert_results(html, tests=0, passed=0, errors=1)
|
||||
assert "this is the test case" in html
|
||||
|
|
Loading…
Reference in New Issue