From 062b73ee728790b5f551ebfbf4dba14845fbec57 Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Thu, 12 Nov 2015 18:14:54 +1100 Subject: [PATCH 1/2] feat: adds an option that allows prefixing the class name in an xunit report --- AUTHORS | 1 + README.txt | 8 ++++++ nose/plugins/xunit.py | 26 +++++++++++++++-- unit_tests/test_xunit.py | 60 +++++++++++++++++++++++++++++++++++----- 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5414bcda..1c29d09b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -25,3 +25,4 @@ Brendan McCollam Erik Rose Sascha Peilicke Andre Caron +Joscha Feth diff --git a/README.txt b/README.txt index e8650e05..de3b748c 100644 --- a/README.txt +++ b/README.txt @@ -524,6 +524,14 @@ Options Name of the testsuite in the xunit xml, generated by plugin. Default test suite name is nosetests. +--xunit-prefix-with-testsuite-name + + Enables prefixing of the test class name in the xunit xml. + This can be used in a matrixed build to distinguish between failures + in different environments. + If enabled, the testsuite name is used as a prefix. + [NOSE_XUNIT_PREFIX_WITH_TESTSUITE_NAME] + --all-modules Enable plugin AllModules: Collect tests from all python modules. diff --git a/nose/plugins/xunit.py b/nose/plugins/xunit.py index b43ff042..77e68e35 100644 --- a/nose/plugins/xunit.py +++ b/nose/plugins/xunit.py @@ -22,6 +22,12 @@ If you need to change the name of the test suite, you can set the ``--xunit-testsuite-name`` option. +If you need to prefix the name of the tested classes, you can set the +``--xunit-prefix-with-testsuite-name`` option. +This can be used in a matrixed build to distinguish between failures +in different environments. +The testsuite name is used as a prefix. + Here is an abbreviated version of what an XML test report might look like:: @@ -169,6 +175,12 @@ def _quoteattr(self, attr): attr = xml_safe(attr) return saxutils.quoteattr(attr) + def _getCls(self, id): + if self.xunit_prefix_class: + return self._quoteattr('%s.%s' % (self.xunit_testsuite_name, id_split(id)[0])) + else: + return self._quoteattr(id_split(id)[0]) + def options(self, parser, env): """Sets additional command line options.""" Plugin.options(self, parser, env) @@ -187,6 +199,13 @@ def options(self, parser, env): help=("Name of the testsuite in the xunit xml, generated by plugin. " "Default test suite name is nosetests.")) + parser.add_option( + '--xunit-prefix-with-testsuite-name', action='store_true', + dest='xunit_prefix_class', + default=bool(env.get('NOSE_XUNIT_PREFIX_WITH_TESTSUITE_NAME', False)), + help=("Whether to prefix the class name under test with testsuite name. " + "Defaults to false.")) + def configure(self, options, config): """Configures the xunit plugin.""" Plugin.configure(self, options, config) @@ -200,6 +219,7 @@ def configure(self, options, config): self.errorlist = [] self.error_report_file_name = os.path.realpath(options.xunit_file) self.xunit_testsuite_name = options.xunit_testsuite_name + self.xunit_prefix_class = options.xunit_prefix_class def report(self, stream): """Writes an Xunit-formatted XML file @@ -292,7 +312,7 @@ def addError(self, test, err, capt=None): u'' u'<%(type)s type=%(errtype)s message=%(message)s>' u'%(systemout)s%(systemerr)s' % - {'cls': self._quoteattr(id_split(id)[0]), + {'cls': self._getCls(id), 'name': self._quoteattr(id_split(id)[-1]), 'taken': taken, 'type': type, @@ -315,7 +335,7 @@ def addFailure(self, test, err, capt=None, tb_info=None): u'' u'' u'%(systemout)s%(systemerr)s' % - {'cls': self._quoteattr(id_split(id)[0]), + {'cls': self._getCls(id), 'name': self._quoteattr(id_split(id)[-1]), 'taken': taken, 'errtype': self._quoteattr(nice_classname(err[0])), @@ -334,7 +354,7 @@ def addSuccess(self, test, capt=None): self.errorlist.append( '%(systemout)s%(systemerr)s' % - {'cls': self._quoteattr(id_split(id)[0]), + {'cls': self._getCls(id), 'name': self._quoteattr(id_split(id)[-1]), 'taken': taken, 'systemout': self._getCapturedStdout(), diff --git a/unit_tests/test_xunit.py b/unit_tests/test_xunit.py index dd54b8d8..dc255999 100644 --- a/unit_tests/test_xunit.py +++ b/unit_tests/test_xunit.py @@ -95,19 +95,32 @@ def test_tee_works_with_distutils_log(self): class TestXMLOutputWithXML(unittest.TestCase): + def test_prefix_from_environ(self): + parser = optparse.OptionParser() + x = Xunit() + x.add_options(parser, env={'NOSE_XUNIT_PREFIX_WITH_TESTSUITE_NAME': 'true'}) + (options, args) = parser.parse_args([]) + eq_(options.xunit_prefix_class, True) + + def test_prefix_from_opt(self): + parser = optparse.OptionParser() + x = Xunit() + x.add_options(parser, env={}) + (options, args) = parser.parse_args(["--xunit-prefix-with-testsuite-name"]) + eq_(options.xunit_prefix_class, True) + +class BaseTestXMLOutputWithXML(unittest.TestCase): + def configure(self, args): + parser = optparse.OptionParser() + self.x.add_options(parser, env={}) + (options, args) = parser.parse_args(args) + self.x.configure(options, Config()) def setUp(self): self.xmlfile = os.path.abspath( os.path.join(os.path.dirname(__file__), 'support', 'xunit.xml')) - parser = optparse.OptionParser() self.x = Xunit() - self.x.add_options(parser, env={}) - (options, args) = parser.parse_args([ - "--with-xunit", - "--xunit-file=%s" % self.xmlfile - ]) - self.x.configure(options, Config()) try: import xml.etree.ElementTree @@ -128,6 +141,39 @@ class DummyStream: f.close() return data +class TestXMLOutputWithXMLAndPrefix(BaseTestXMLOutputWithXML): + def setUp(self): + super(TestXMLOutputWithXMLAndPrefix, self).setUp() + self.configure([ + "--with-xunit", + "--xunit-file=%s" % self.xmlfile, + "--xunit-prefix-with-testsuite-name" + ]) + + def test_addSuccess(self): + test = mktest() + self.x.beforeTest(test) + self.x.addSuccess(test, (None,None,None)) + + result = self.get_xml_report() + print result + + if self.ET: + tree = self.ET.fromstring(result) + tc = tree.find("testcase") + eq_(tc.attrib['classname'], "nosetests.test_xunit.TC") + else: + # this is a dumb test for 2.4- + assert ' Date: Sun, 29 Nov 2015 00:09:23 +1100 Subject: [PATCH 2/2] test: add test with custom class name prefix --- unit_tests/test_xunit.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/unit_tests/test_xunit.py b/unit_tests/test_xunit.py index dc255999..944d2859 100644 --- a/unit_tests/test_xunit.py +++ b/unit_tests/test_xunit.py @@ -144,13 +144,8 @@ class DummyStream: class TestXMLOutputWithXMLAndPrefix(BaseTestXMLOutputWithXML): def setUp(self): super(TestXMLOutputWithXMLAndPrefix, self).setUp() - self.configure([ - "--with-xunit", - "--xunit-file=%s" % self.xmlfile, - "--xunit-prefix-with-testsuite-name" - ]) - def test_addSuccess(self): + def _assert_testcase_classname(self, expected_classname): test = mktest() self.x.beforeTest(test) self.x.addSuccess(test, (None,None,None)) @@ -161,10 +156,32 @@ def test_addSuccess(self): if self.ET: tree = self.ET.fromstring(result) tc = tree.find("testcase") - eq_(tc.attrib['classname'], "nosetests.test_xunit.TC") + eq_(tc.attrib['classname'], expected_classname) else: # this is a dumb test for 2.4- - assert '