Search code examples
pythonnose

Nose not running add* methods from example plugin


I'm trying to learn a bit about writing nose plugins, so I've taken their example HtmlOutput plugin and I'm trying to use it like so:

import unittest
import nose
import sys
import traceback

class MyTest(unittest.TestCase):
    def test_failing(self):
        self.assertTrue(False)

    def test_passing(self):
        self.assertTrue(True)

class HtmlOutput(nose.plugins.Plugin):
    """Output test results as ugly, unstyled html.
    """
    name = 'html-output'
    score = 2 # run late

    def __init__(self):
        super(HtmlOutput, self).__init__()
        self.html = [ '<html><head>',
                      '<title>Test output</title>',
                      '</head><body>' ]

    def configure(self, options, conf):
        super(HtmlOutput, self).configure(options, conf)

        self.enabled = True

    def addSuccess(self, test):
        self.html.append('<span>ok</span>')

    def addError(self, test, err):
        err = self.formatErr(err)
        self.html.append('<span>ERROR</span>')
        self.html.append('<pre>%s</pre>' % err)

    def addFailure(self, test, err):
        err = self.formatErr(err)
        self.html.append('<span>FAIL</span>')
        self.html.append('<pre>%s</pre>' % err)

    def finalize(self, result):
        self.html.append('<div>')
        self.html.append("Ran %d test%s" %
                         (result.testsRun, result.testsRun != 1 and "s" or ""))
        self.html.append('</div>')
        self.html.append('<div>')
        if not result.wasSuccessful():
            self.html.extend(['<span>FAILED ( ',
                              'failures=%d ' % len(result.failures),
                              'errors=%d' % len(result.errors),
                              ')</span>'])
        else:
            self.html.append('OK')
        self.html.append('</div></body></html>')
        # print >> sys.stderr, self.html
        for l in self.html:
            self.stream.writeln(l)

    def formatErr(self, err):
        exctype, value, tb = err
        return ''.join(traceback.format_exception(exctype, value, tb))

    def setOutputStream(self, stream):
        # grab for own use
        self.stream = stream
        # return dummy stream
        class dummy:
            def write(self, *arg):
                pass
            def writeln(self, *arg):
                pass
        d = dummy()
        return d

    def startContext(self, ctx):
        try:
            n = ctx.__name__
        except AttributeError:
            n = str(ctx).replace('<', '').replace('>', '')
        self.html.extend(['<fieldset>', '<legend>', n, '</legend>'])
        try:
            path = ctx.__file__.replace('.pyc', '.py')
            self.html.extend(['<div>', path, '</div>'])
        except AttributeError:
            pass

    def stopContext(self, ctx):
        self.html.append('</fieldset>')

    def startTest(self, test):
        self.html.extend([ '<div><span>',
                           test.shortDescription() or str(test),
                           '</span>' ])

    def stopTest(self, test):
        self.html.append('</div>')

suite  = unittest.defaultTestLoader.loadTestsFromTestCase(MyTest)
nose.run(argv=['-s'],
         suite=suite,
         addplugins=[HtmlOutput()])

The plugin appears to actually initialize so I get this output:

<html><head>
<title>Test output</title>
</head><body>
<div>
Ran 2 tests
</div>
<div>
<span>FAILED (
failures=1
errors=0
)</span>
</div></body></html>

The problem I'm having is the plugin appears to totally skip over the addFailure, addSuccess, and addError methods completely and I cannot track down any reason for it to be skipping them.


Solution

  • After doing additional research today, I came across this GitHub issue in the nose repo.

    The problem was unittest.defaultTestLoader.loadTestsFromTestCase(MyTest) does in fact return a TestSuite, and suite option appears to actually want a list of tests. Fortunately, fixing my problem was as easy as doing suite._tests and the addFailure et al hooks work as expected.