Skip to content

Commit

Permalink
junitxml: adjust junitxml output file to comply with JUnit xsd
Browse files Browse the repository at this point in the history
Change XML file structure in the manner that failures in call and errors
in teardown in one test will appear under separate testcase elements in
the XML report.
  • Loading branch information
KKoukiou committed Feb 6, 2017
1 parent 3b47cb4 commit 8287222
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
13 changes: 11 additions & 2 deletions _pytest/junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ def __init__(self, logfile, prefix):
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
self.global_properties = []
self.last_report = None
self.cnt_double_fail_tests = 0

def finalize(self, report):
nodeid = getattr(report, 'nodeid', report)
Expand Down Expand Up @@ -337,6 +339,10 @@ def pytest_runtest_logreport(self, report):
reporter = self._opentestcase(report)
reporter.append_pass(report)
elif report.failed:
if self.last_report and self.last_report.failed:
if self.last_report.when == "call":
self.cnt_double_fail_tests = self.cnt_double_fail_tests + 1
self.finalize(self.last_report)
reporter = self._opentestcase(report)
if report.when == "call":
reporter.append_failure(report)
Expand All @@ -346,8 +352,10 @@ def pytest_runtest_logreport(self, report):
reporter = self._opentestcase(report)
reporter.append_skipped(report)
self.update_testcase_duration(report)
self.last_report = report
if report.when == "teardown":
self.finalize(report)
self.last_report = None

def update_testcase_duration(self, report):
"""accumulates total duration for nodeid from given report and updates
Expand Down Expand Up @@ -380,8 +388,9 @@ def pytest_sessionfinish(self):
suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time

numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped'] + self.stats['error']

numtests = (self.stats['passed'] + self.stats['failure'] +
self.stats['skipped'] + self.stats['error'] -
self.cnt_double_fail_tests)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')

logfile.write(Junit.testsuite(
Expand Down
25 changes: 24 additions & 1 deletion testing/test_junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,29 @@ def test_function(arg):
fnode.assert_attr(message="test teardown failure")
assert "ValueError" in fnode.toxml()

def test_call_failure_teardown_error(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture
def arg():
yield
raise Exception("Teardown Exception")
def test_function(arg):
raise Exception("Call Exception")
""")
result, dom = runandparse(testdir)
assert result.ret
node = dom.find_first_by_tag("testsuite")
node.assert_attr(errors=1, failures=1, tests=1)
first, second = dom.find_by_tag("testcase")
if not first or not second or first == second:
assert 0
fnode = first.find_first_by_tag("failure")
fnode.assert_attr(message="Exception: Call Exception")
snode = second.find_first_by_tag("error")
snode.assert_attr(message="test teardown failure")

def test_skip_contains_name_reason(self, testdir):
testdir.makepyfile("""
import pytest
Expand Down Expand Up @@ -986,4 +1009,4 @@ class Report(BaseReport):

test_case = minidom.parse(str(path)).getElementsByTagName('testcase')[0]

assert (test_case.getAttribute('url') == test_url), "The URL did not get written to the xml"
assert (test_case.getAttribute('url') == test_url), "The URL did not get written to the xml"

0 comments on commit 8287222

Please sign in to comment.