Python 3 unittest:如何提取测试结果?

时间:2014-09-25 17:49:53

标签: python unit-testing python-3.x

我正在使用Python的(3.4.1)unittest模块进行单元测试。

我使用导入加载所有测试模块文件,然后运行unittest.main():

import unittest
import testing_module1
import testing_module2
# [...]
if __name__ == '__main__':
    unittest.main()

这对我来说非常合适,因为它很简单并且尊重我用来控制详细程度或运行哪些测试的命令行参数。

我想继续输出相同的信息,但我想从结果中生成XML文件。我尝试了xmlrunner(https://github.com/xmlrunner/unittest-xml-reporting/)但是:

  • 它不会向stdout输出与标准跑步者一样多的信息;
  • 它使用的格式不适合我。

我想用我需要的格式生成XML(我不介意手动操作),但对测试的运行方式的改动很小。

我有什么选择?

  1. 我可以编写自己的TestRunner,但我不想重写所有内容,我只想在实际运行时添加额外的输出,而且代码更改最少。
  2. 我可以继承unittest.TextTestRunner,但我担心添加XML输出需要重写每个方法,首先会失去继承的优势。
  3. 我可以尝试在调用unittest.main()之后提取测试结果并解析它。这里的问题是,unittest.main()似乎在它完成时退出,因此任何代码都不会被执行。
  4. 有什么建议吗?

    谢谢!

3 个答案:

答案 0 :(得分:1)

我最后写了两个新类,继承自unittest.TextTestResultunittest.TextTestRunner。这样,我可以像那样运行main:

unittest.main(testRunner=xmlrunner.XMLTestRunner(...))

我重载了unittest.TextTestRunner的{​​{1}}以及来自__init__的<{1}}:

  • unittest.TextTestResult
  • addSuccess()
  • addError()
  • addFailure()

例如:

addSubTest()

由于这些def addSuccess(self, test): super().addSuccess(test) [... store the test into list, dictionary, whatever... ] 函数是通过实际测试调用的,我可以将它们存储在全局列表中并在add*()的末尾解析它们:

XMLTestRunner.run()

请注意,这些功能通常在def run(self, test): result = super().run(test) self.save_xml_report(result) return result

中定义

警告说明:如图所示,通过使用传递/usr/lib/python3.4/unittest/runner.py的{​​{1}}参数的实际对象,将忽略启动python时给出的命令行参数。例如,忽略使用unittest.main()参数增加详细级别。这是因为testRunner中定义的-v类会检测TestProgram是否在/usr/lib/python3.4/unittest/main.py作为类或对象的情况下运行(请参阅unittest.main()附近的testRunner的文件)。如果你只给这样一个类:

runTests()

然后解析命令行参数。但是你传递了一个实例化的对象(就像我需要的那样),unittest.main(testRunner=xmlrunner.XMLTestRunner) 将只是按原样使用它。因此,我必须在runTests()

中自己解析论点
XMLTestRunner.__init__()

答案 1 :(得分:0)

这对您有何帮助?在StringIO中捕获unittest的输出,该输出转到sys.stderr。在unittest.main之后继续添加`exit = False&#39;。根据需要读取捕获的输出和处理。概念证明:

import contextlib
import io
import sys
import unittest

class Mytest(unittest.TestCase):
    def test_true(self):
        self.assertTrue(True)

@contextlib.contextmanager
def err_to(file):
    old_err = sys.stderr
    sys.stderr = file
    yield
    sys.stderr = old_err

if __name__ == '__main__':
    result = io.StringIO()
    with err_to(result):
        unittest.main(exit=False)
    result.seek(0)
    print(result.read())

打印(到sys.stdout)

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

注意:contextlib有redirect_stdout,但不是redirect_stderr。以上比contextlib代码更简单。以上假设没有单一测试未捕获的例外情况。请参阅contextlib.contextmanager doc以添加try:except:finally。我把它留给你。

答案 2 :(得分:0)

从unittest lib捕获FAIL事件时,我也遇到了同样的问题。遵循big_gie的回答,此代码出现了:

文件testFileName_1.py

import unittest

class TestClassToTestSth(unittest.TestCase):
    def test_One(self):
        self.AssertEqual(True, False, 'Hello world')

import unittest
from io import StringIO
import testFileName_1

def suites():
    return [
        # your testCase classes, for example
        testFileName_1.TestClassToTestSth,
        testFileName_445.TestClassToTestSomethingElse,
    ]

class TextTestResult(unittest.TextTestResult):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.slack = Slack('data-engineering-tests')

    def addFailure(self, test, err):
        super().addFailure(test, err)

        # Whatever you want here
        print(err, test)
        print(self.failures)


class TextTestRunner(unittest.TextTestRunner):
    resultclass = TextTestResult
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


loader = unittest.TestLoader()
suite = unittest.TestSuite()

stream = StringIO()
for test_case in suites():
    suite.addTests(loader.loadTestsFromTestCase(test_case))
runner = TextTestRunner(stream=stream)
result = runner.run(suite)

stream.seek(0)
print(stream.read())