Skip to content

Commit c6aa1c9

Browse files
Add support for multiple sub-elements (failure/error/skipped) in testcase
Signed-off-by: Martin Rakovec <martin.rakovec@comap-control.com>
1 parent 2fe4840 commit c6aa1c9

File tree

2 files changed

+152
-54
lines changed

2 files changed

+152
-54
lines changed

junit_xml/__init__.py

+66-35
Original file line numberDiff line numberDiff line change
@@ -192,27 +192,29 @@ def build_xml_doc(self, encoding=None):
192192

193193
# failures
194194
for failure in case.failures:
195-
attrs = {'type': 'failure'}
196-
if failure['message']:
197-
attrs['message'] = decode(failure['message'], encoding)
198-
if failure['type']:
199-
attrs['type'] = decode(failure['type'], encoding)
200-
failure_element = ET.Element("failure", attrs)
201-
if failure['output']:
202-
failure_element.text = decode(failure['output'], encoding)
203-
test_case_element.append(failure_element)
195+
if failure['output'] or failure['message']:
196+
attrs = {'type': 'failure'}
197+
if failure['message']:
198+
attrs['message'] = decode(failure['message'], encoding)
199+
if failure['type']:
200+
attrs['type'] = decode(failure['type'], encoding)
201+
failure_element = ET.Element("failure", attrs)
202+
if failure['output']:
203+
failure_element.text = decode(failure['output'], encoding)
204+
test_case_element.append(failure_element)
204205

205206
# errors
206207
for error in case.errors:
207-
attrs = {'type': 'error'}
208-
if error['message']:
209-
attrs['message'] = decode(error['message'], encoding)
210-
if error['type']:
211-
attrs['type'] = decode(error['type'], encoding)
212-
error_element = ET.Element("error", attrs)
213-
if error['output']:
214-
error_element.text = decode(error['output'], encoding)
215-
test_case_element.append(error_element)
208+
if error['message'] or error['output']:
209+
attrs = {'type': 'error'}
210+
if error['message']:
211+
attrs['message'] = decode(error['message'], encoding)
212+
if error['type']:
213+
attrs['type'] = decode(error['type'], encoding)
214+
error_element = ET.Element("error", attrs)
215+
if error['output']:
216+
error_element.text = decode(error['output'], encoding)
217+
test_case_element.append(error_element)
216218

217219
# skippeds
218220
for skipped in case.skipped:
@@ -322,7 +324,7 @@ class TestCase(object):
322324
def __init__(self, name, classname=None, elapsed_sec=None, stdout=None,
323325
stderr=None, assertions=None, timestamp=None, status=None,
324326
category=None, file=None, line=None, log=None, group=None,
325-
url=None):
327+
url=None, allow_multiple_subelements=False):
326328
self.name = name
327329
self.assertions = assertions
328330
self.elapsed_sec = elapsed_sec
@@ -341,40 +343,69 @@ def __init__(self, name, classname=None, elapsed_sec=None, stdout=None,
341343
self.errors = []
342344
self.failures = []
343345
self.skipped = []
346+
self.allow_multiple_subalements = allow_multiple_subelements
344347

345348
def add_error_info(self, message=None, output=None, error_type=None):
346349
"""Adds an error message, output, or both to the test case"""
347-
if message or output or error_type:
348-
error = {}
349-
error['message'] = message
350-
error['output'] = output
351-
error['type'] = error_type
350+
error = {}
351+
error['message'] = message
352+
error['output'] = output
353+
error['type'] = error_type
354+
if self.allow_multiple_subalements:
355+
if message or output:
356+
self.errors.append(error)
357+
elif not len(self.errors):
352358
self.errors.append(error)
359+
else:
360+
if message:
361+
self.errors[0]['message'] = message
362+
if output:
363+
self.errors[0]['output'] = output
364+
if error_type:
365+
self.errors[0]['type'] = error_type
353366

354367
def add_failure_info(self, message=None, output=None, failure_type=None):
355368
"""Adds a failure message, output, or both to the test case"""
356-
if message or output or failure_type:
357-
failure = {}
358-
failure['message'] = message
359-
failure['output'] = output
360-
failure['type'] = failure_type
369+
failure = {}
370+
failure['message'] = message
371+
failure['output'] = output
372+
failure['type'] = failure_type
373+
if self.allow_multiple_subalements:
374+
if message or output:
375+
self.failures.append(failure)
376+
elif not len(self.failures):
361377
self.failures.append(failure)
378+
else:
379+
if message:
380+
self.failures[0]['message'] = message
381+
if output:
382+
self.failures[0]['output'] = output
383+
if failure_type:
384+
self.failures[0]['type'] = failure_type
362385

363386
def add_skipped_info(self, message=None, output=None):
364387
"""Adds a skipped message, output, or both to the test case"""
365-
if message or output:
366-
skipped = {}
367-
skipped['message'] = message
368-
skipped['output'] = output
388+
skipped = {}
389+
skipped['message'] = message
390+
skipped['output'] = output
391+
if self.allow_multiple_subalements:
392+
if message or output:
393+
self.skipped.append(skipped)
394+
elif not len(self.skipped):
369395
self.skipped.append(skipped)
396+
else:
397+
if message:
398+
self.skipped[0]['message'] = message
399+
if output:
400+
self.skipped[0]['output'] = output
370401

371402
def is_failure(self):
372403
"""returns true if this test case is a failure"""
373-
return (len(self.failures) > 0)
404+
return (sum(1 for f in self.failures if f['message'] or f['output']) > 0)
374405

375406
def is_error(self):
376407
"""returns true if this test case is an error"""
377-
return (len(self.errors) > 0)
408+
return (sum(1 for e in self.errors if e['message'] or e['output']) > 0)
378409

379410
def is_skipped(self):
380411
"""returns true if this test case has been skipped"""

test_junit_xml.py

+86-19
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ def test_init_stderr(self):
317317
(ts, tcs) = serialize_and_read(
318318
TestSuite(
319319
'test', [TestCase(name='Test1', classname='some.class.name',
320-
elapsed_sec=123.345, stderr='I am stderr!')]))[0]
320+
elapsed_sec=123.345, stderr='I am stderr!')]))[0]
321321
verify_test_case(
322322
self, tcs[0],
323323
{'name': 'Test1', 'classname': 'some.class.name',
@@ -511,13 +511,59 @@ def test_init_unicode(self):
511511
error_message=decode('Skipped error äöü', 'utf-8'),
512512
error_output=decode('I skippäd with an error!', 'utf-8'))
513513

514+
def test_multiple_errors(self):
515+
"""Tests multiple errors in one test case"""
516+
tc = TestCase('Multiple error', allow_multiple_subelements=True)
517+
tc.add_error_info("First error", "First error message")
518+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
519+
verify_test_case(
520+
self, tcs[0], {'name': 'Multiple error'},
521+
errors=[{"message": "First error", "output": "First error message", "type": "error"}])
522+
tc.add_error_info("Second error", "Second error message")
523+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
524+
verify_test_case(
525+
self, tcs[0], {'name': 'Multiple error'},
526+
errors=[{"message": "First error", "output": "First error message", "type": "error"},
527+
{"message": "Second error", "output": "Second error message", "type": "error"}])
528+
529+
def test_multiple_failures(self):
530+
"""Tests multiple failures in one test case"""
531+
tc = TestCase('Multiple failures', allow_multiple_subelements=True)
532+
tc.add_failure_info("First failure", "First failure message")
533+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
534+
verify_test_case(
535+
self, tcs[0], {'name': 'Multiple failures'},
536+
failures=[{"message": "First failure", "output": "First failure message", "type": "failure"}])
537+
tc.add_failure_info("Second failure", "Second failure message")
538+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
539+
verify_test_case(
540+
self, tcs[0], {'name': 'Multiple failures'},
541+
failures=[{"message": "First failure", "output": "First failure message", "type": "failure"},
542+
{"message": "Second failure", "output": "Second failure message", "type": "failure"}])
543+
544+
def test_multiple_skipped(self):
545+
"""Tests multiple skipped messages in one test case"""
546+
tc = TestCase('Multiple skipped', allow_multiple_subelements=True)
547+
tc.add_skipped_info("First skipped", "First skipped message")
548+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
549+
verify_test_case(
550+
self, tcs[0], {'name': 'Multiple skipped'},
551+
skipped=[{"message": "First skipped", "output": "First skipped message"}])
552+
tc.add_skipped_info("Second skipped", "Second skipped message")
553+
(_, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
554+
verify_test_case(
555+
self, tcs[0], {'name': 'Multiple skipped'},
556+
skipped=[{"message": "First skipped", "output": "First skipped message"},
557+
{"message": "Second skipped", "output": "Second skipped message"}])
558+
514559

515560
def verify_test_case(tc, test_case_element, expected_attributes,
516561
error_message=None, error_output=None, error_type=None,
517562
failure_message=None, failure_output=None,
518563
failure_type=None,
519564
skipped_message=None, skipped_output=None,
520-
stdout=None, stderr=None):
565+
stdout=None, stderr=None,
566+
errors=[], failures=[], skipped=[]):
521567
for k, v in expected_attributes.items():
522568
tc.assertEqual(v, test_case_element.attributes[k].value)
523569

@@ -533,47 +579,68 @@ def verify_test_case(tc, test_case_element, expected_attributes,
533579
stdout, test_case_element.getElementsByTagName(
534580
'system-out')[0].firstChild.nodeValue.strip())
535581

536-
errors = test_case_element.getElementsByTagName('error')
582+
_errors = test_case_element.getElementsByTagName('error')
537583
if error_message or error_output:
538-
tc.assertTrue(len(errors) > 0)
584+
tc.assertTrue(len(_errors) > 0)
585+
elif errors:
586+
tc.assertEqual(len(errors), len(_errors))
539587
else:
540-
tc.assertEqual(0, len(errors))
588+
tc.assertEqual(0, len(_errors))
541589

542590
if error_message:
543591
tc.assertEqual(
544-
error_message, errors[0].attributes['message'].value)
592+
error_message, _errors[0].attributes['message'].value)
545593

546-
if error_type and errors:
594+
if error_type and _errors:
547595
tc.assertEqual(
548-
error_type, errors[0].attributes['type'].value)
596+
error_type, _errors[0].attributes['type'].value)
549597

550598
if error_output:
551599
tc.assertEqual(
552-
error_output, errors[0].firstChild.nodeValue.strip())
600+
error_output, _errors[0].firstChild.nodeValue.strip())
601+
602+
for error_exp, error_r in zip(errors, _errors):
603+
tc.assertEqual(error_r.attributes['message'].value, error_exp['message'])
604+
tc.assertEqual(error_r.firstChild.nodeValue.strip(), error_exp['output'])
605+
tc.assertEqual(error_r.attributes['type'].value, error_exp['type'])
553606

554-
failures = test_case_element.getElementsByTagName('failure')
607+
_failures = test_case_element.getElementsByTagName('failure')
555608
if failure_message or failure_output:
556-
tc.assertTrue(len(failures) > 0)
609+
tc.assertTrue(len(_failures) > 0)
610+
elif failures:
611+
tc.assertEqual(len(failures), len(_failures))
557612
else:
558-
tc.assertEqual(0, len(failures))
613+
tc.assertEqual(0, len(_failures))
559614

560615
if failure_message:
561616
tc.assertEqual(
562-
failure_message, failures[0].attributes['message'].value)
617+
failure_message, _failures[0].attributes['message'].value)
563618

564-
if failure_type and failures:
619+
if failure_type and _failures:
565620
tc.assertEqual(
566-
failure_type, failures[0].attributes['type'].value)
621+
failure_type, _failures[0].attributes['type'].value)
567622

568623
if failure_output:
569624
tc.assertEqual(
570-
failure_output, failures[0].firstChild.nodeValue.strip())
625+
failure_output, _failures[0].firstChild.nodeValue.strip())
626+
627+
for failure_exp, failure_r in zip(failures, _failures):
628+
tc.assertEqual(failure_r.attributes['message'].value, failure_exp['message'])
629+
tc.assertEqual(failure_r.firstChild.nodeValue.strip(), failure_exp['output'])
630+
tc.assertEqual(failure_r.attributes['type'].value, failure_exp['type'])
571631

572-
skipped = test_case_element.getElementsByTagName('skipped')
632+
_skipped = test_case_element.getElementsByTagName('skipped')
573633
if skipped_message or skipped_output:
574-
tc.assertTrue(len(skipped) > 0)
634+
tc.assertTrue(len(_skipped) > 0)
635+
elif skipped:
636+
tc.assertEqual(len(skipped), len(_skipped))
575637
else:
576-
tc.assertEqual(0, len(skipped))
638+
tc.assertEqual(0, len(_skipped))
639+
640+
for skipped_exp, skipped_r in zip(skipped, _skipped):
641+
tc.assertEqual(skipped_r.attributes['message'].value, skipped_exp['message'])
642+
tc.assertEqual(skipped_r.firstChild.nodeValue.strip(), skipped_exp['output'])
643+
577644

578645
if __name__ == '__main__':
579646
unittest.main()

0 commit comments

Comments
 (0)