Skip to content

Added test suite attributes #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions junit_xml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class TestSuite(object):
"""

def __init__(self, name, test_cases=None, hostname=None, id=None,
package=None, timestamp=None, properties=None):
package=None, timestamp=None, properties=None, file=None,
log=None, url=None, stdout=None, stderr=None):
self.name = name
if not test_cases:
test_cases = []
Expand All @@ -89,10 +90,15 @@ def __init__(self, name, test_cases=None, hostname=None, id=None,
except TypeError:
raise Exception('test_cases must be a list of test cases')
self.test_cases = test_cases
self.timestamp = timestamp
self.hostname = hostname
self.id = id
self.package = package
self.timestamp = timestamp
self.file = file
self.log = log
self.url = url
self.stdout = stdout
self.stderr = stderr
self.properties = properties

def build_xml_doc(self, encoding=None):
Expand All @@ -106,6 +112,11 @@ def build_xml_doc(self, encoding=None):
# build the test suite element
test_suite_attributes = dict()
test_suite_attributes['name'] = decode(self.name, encoding)
if any(c.assertions for c in self.test_cases):
test_suite_attributes['assertions'] = \
str(sum([int(c.assertions) for c in self.test_cases if c.assertions]))
test_suite_attributes['disabled'] = \
str(len([c for c in self.test_cases if not c.is_enabled]))
test_suite_attributes['failures'] = \
str(len([c for c in self.test_cases if c.is_failure()]))
test_suite_attributes['errors'] = \
Expand All @@ -124,6 +135,12 @@ def build_xml_doc(self, encoding=None):
test_suite_attributes['package'] = decode(self.package, encoding)
if self.timestamp:
test_suite_attributes['timestamp'] = decode(self.timestamp, encoding)
if self.timestamp:
test_suite_attributes['file'] = decode(self.file, encoding)
if self.timestamp:
test_suite_attributes['log'] = decode(self.log, encoding)
if self.timestamp:
test_suite_attributes['url'] = decode(self.url, encoding)

xml_element = ET.Element("testsuite", test_suite_attributes)

Expand All @@ -134,12 +151,23 @@ def build_xml_doc(self, encoding=None):
attrs = {'name': decode(k, encoding), 'value': decode(v, encoding)}
ET.SubElement(props_element, "property", attrs)

# add test suite stdout
if self.stdout:
stdout_element = ET.SubElement(xml_element, "system-out")
stdout_element.text = decode(self.stdout, encoding)

# add test suite stderr
if self.stderr:
stderr_element = ET.SubElement(xml_element, "system-err")
stderr_element.text = decode(self.stderr, encoding)

# test cases
for case in self.test_cases:
test_case_attributes = dict()
test_case_attributes['name'] = decode(case.name, encoding)
if case.assertions:
test_case_attributes['assertions'] = decode(case.assertions, encoding)
# Number of assertions in the test case
test_case_attributes['assertions'] = "%d" % case.assertions
if case.elapsed_sec:
test_case_attributes['time'] = "%f" % case.elapsed_sec
if case.timestamp:
Expand Down Expand Up @@ -227,7 +255,7 @@ def to_xml_string(test_suites, prettyprint=True, encoding=None):
attributes = defaultdict(int)
for ts in test_suites:
ts_xml = ts.build_xml_doc(encoding=encoding)
for key in ['failures', 'errors', 'tests']:
for key in ['failures', 'errors', 'tests', 'disabled']:
attributes[key] += int(ts_xml.get(key, 0))
for key in ['time']:
attributes[key] += float(ts_xml.get(key, 0))
Expand Down Expand Up @@ -291,9 +319,10 @@ def _clean_illegal_xml_chars(string_to_clean):
class TestCase(object):
"""A JUnit test case with a result and possibly some stdout or stderr"""

def __init__(self, name, assertions=None, elapsed_sec=None,
timestamp=None, classname=None, status=None, category=None, file=None, line=None,
log=None, group=None, url=None, stdout=None, stderr=None):
def __init__(self, name, classname=None, elapsed_sec=None, stdout=None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you have also changed the signtature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed back the signature as it was in #38 before my #41 (where I broke the signature)
def __init__(self, name, classname=None, elapsed_sec=None, stdout=None, stderr=None):

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, sorry.

stderr=None, assertions=None, timestamp=None, status=None,
category=None, file=None, line=None, log=None, group=None,
url=None):
self.name = name
self.assertions = assertions
self.elapsed_sec = elapsed_sec
Expand All @@ -308,6 +337,7 @@ def __init__(self, name, assertions=None, elapsed_sec=None,
self.stdout = stdout
self.stderr = stderr

self.is_enabled = True
self.error_message = None
self.error_output = None
self.error_type = None
Expand Down
72 changes: 58 additions & 14 deletions test_junit_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ def test_single_suite_no_test_cases(self):

(ts, tcs) = serialize_and_read(
TestSuite(
'test',
[],
name='test',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see after you changed the constructors signature you broke the tests already. That could be someones code :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I just add attributes explicitly in constructor signature. I think I don't break anyone's code :___(

Copy link
Collaborator

@sysradium sysradium Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off course not. My comment was about this: imagine you have a function and a test:

def foo(a, b, c): ...

def test_foo():
     foo('1', '2', [])

If you change the signature of foo to foo(a, c, b) you break the client code and the test as well. And I thought that you realised and fixed that problem just modifying the test:

def test_foo():
     foo(a='1', b='2', c=[])

So the test now works but still there are guys out there whose code broke :) So the failing test was a red flag here. If you did not - then it's fine.

test_cases=[],
hostname='localhost',
id=1,
properties=properties,
Expand All @@ -100,8 +100,8 @@ def test_single_suite_no_test_cases_utf8(self):
timestamp = 1398382805

test_suite = TestSuite(
'äöü',
[],
name='äöü',
test_cases=[],
hostname='löcalhost',
id='äöü',
properties=properties,
Expand Down Expand Up @@ -131,8 +131,8 @@ def test_single_suite_no_test_cases_unicode(self):

(ts, tcs) = serialize_and_read(
TestSuite(
decode('äöü', 'utf-8'),
[],
name=decode('äöü', 'utf-8'),
test_cases=[],
hostname=decode('löcalhost', 'utf-8'),
id=decode('äöü', 'utf-8'),
properties=properties,
Expand Down Expand Up @@ -198,9 +198,8 @@ def test_multiple_suites_to_string(self):
verify_test_case(self, suites[1][1][0], {'name': 'Test2'})

def test_attribute_time(self):
tss = [TestSuite('suite1',
[TestCase('Test1', 'some.class.name', 123.345),
TestCase('Test2', 'some2.class.name', 123.345)]),
tss = [TestSuite('suite1', [TestCase(name='Test1', classname='some.class.name', elapsed_sec=123.345),
TestCase(name='Test2', classname='some2.class.name', elapsed_sec=123.345)]),
TestSuite('suite2', [TestCase('Test2')])]
suites = serialize_and_read(tss)

Expand All @@ -212,21 +211,60 @@ def test_attribute_time(self):
# testcase
self.assertEqual('0', suites[1][0].attributes['time'].value)

def test_attribute_disable(self):
tc = TestCase('Disabled-Test')
tc.is_enabled = False
tss = [TestSuite('suite1', [tc])]
suites = serialize_and_read(tss)

self.assertEqual('1', suites[0][0].attributes['disabled'].value)

def test_stderr(self):
suites = serialize_and_read(
TestSuite(name='test', stderr='I am stderr!',
test_cases=[TestCase(name='Test1')]))[0]
self.assertEqual('I am stderr!',
suites[0].getElementsByTagName('system-err')[0].firstChild.data)

def test_stdout_stderr(self):
suites = serialize_and_read(
TestSuite(name='test', stdout='I am stdout!',
stderr='I am stderr!',
test_cases=[TestCase(name='Test1')]))[0]
self.assertEqual('I am stderr!',
suites[0].getElementsByTagName('system-err')[0].firstChild.data)
self.assertEqual('I am stdout!',
suites[0].getElementsByTagName('system-out')[0].firstChild.data)

def test_no_assertions(self):
suites = serialize_and_read(
TestSuite(name='test',
test_cases=[TestCase(name='Test1')]))[0]
self.assertFalse(suites[0].getElementsByTagName('testcase')[0].hasAttribute('assertions'))

def test_assertions(self):
suites = serialize_and_read(
TestSuite(name='test',
test_cases=[TestCase(name='Test1',
assertions=5)]))[0]
self.assertEquals('5',
suites[0].getElementsByTagName('testcase')[0].attributes['assertions'].value)

# @todo: add more tests for the other attributes and properties

def test_to_xml_string(self):
test_suites = [TestSuite('suite1', [TestCase('Test1')]),
TestSuite('suite2', [TestCase('Test2')])]
test_suites = [TestSuite(name='suite1', test_cases=[TestCase(name='Test1')]),
TestSuite(name='suite2', test_cases=[TestCase(name='Test2')])]
xml_string = TestSuite.to_xml_string(test_suites)
if PY2:
self.assertTrue(isinstance(xml_string, unicode))
expected_xml_string = textwrap.dedent("""
<?xml version="1.0" ?>
<testsuites errors="0" failures="0" tests="2" time="0.0">
\t<testsuite errors="0" failures="0" name="suite1" skipped="0" tests="1" time="0">
<testsuites disabled="0" errors="0" failures="0" tests="2" time="0.0">
\t<testsuite disabled="0" errors="0" failures="0" name="suite1" skipped="0" tests="1" time="0">
\t\t<testcase name="Test1"/>
\t</testsuite>
\t<testsuite errors="0" failures="0" name="suite2" skipped="0" tests="1" time="0">
\t<testsuite disabled="0" errors="0" failures="0" name="suite2" skipped="0" tests="1" time="0">
\t\t<testcase name="Test2"/>
\t</testsuite>
</testsuites>
Expand Down Expand Up @@ -298,6 +336,12 @@ def test_init_stdout_stderr(self):
'time': ("%f" % 123.345)},
stdout='I am stdout!', stderr='I am stderr!')

def test_init_disable(self):
tc = TestCase('Disabled-Test')
tc.is_enabled = False
(ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0]
verify_test_case(self, tcs[0], {'name': 'Disabled-Test'})

def test_init_failure_message(self):
tc = TestCase('Failure-Message')
tc.add_failure_info("failure message")
Expand Down