Nose not 运行 add* 来自示例插件的方法

Nose not running add* methods from example plugin

我正在尝试学习一些有关编写 nose 插件的知识,所以我以他们的示例 HtmlOutput 插件为例,并尝试像这样使用它:

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()])

插件似乎真正初始化了,所以我得到了这个输出:

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

我遇到的问题是插件似乎完全跳过了 addFailureaddSuccessaddError 方法,我无法找到它的任何原因跳过它们。

今天做了额外的研究后,我在 nose 仓库中发现了 this GitHub issue

问题是 unittest.defaultTestLoader.loadTestsFromTestCase(MyTest) 实际上 return 一个 TestSuite,并且 suite 选项似乎实际上需要一个测试列表。幸运的是,解决我的问题就像 suite._tests 一样简单,addFailure 等钩子按预期工作。