[英]Python 3 unittest: How to extract results of tests?
I am using Python's (3.4.1) unittest
module for my unit tests.我正在使用 Python 的 (3.4.1) unittest
模块进行单元测试。
I load all my testing module files using imports and then run unittest.main():我使用导入加载所有测试模块文件,然后运行 unittest.main():
import unittest
import testing_module1
import testing_module2
# [...]
if __name__ == '__main__':
unittest.main()
This works perfectly for me as it is simple and respect the command line arguments I use to control verbosity or which test(s) to run.这对我来说非常有效,因为它很简单并且尊重我用来控制详细程度或运行哪个测试的命令行参数。
I want to continue to output the same information, but I would like to generate an XML file from the results.我想继续输出相同的信息,但我想从结果中生成一个 XML 文件。 I tried xmlrunner ( https://github.com/xmlrunner/unittest-xml-reporting/ ) but:我试过 xmlrunner ( https://github.com/xmlrunner/unittest-xml-reporting/ ) 但是:
I would like to generate the XML (I don't mind doing it manually) with the format I need but with minimal change to how the tests are run.我想以我需要的格式生成 XML(我不介意手动执行),但对测试的运行方式进行最小的更改。
What are my options?我有哪些选择?
unittest.main()
and parse it.我可以尝试在调用unittest.main()
后提取测试结果并解析它。 The problem here is that unittest.main()
seems to exit when it's done so any code after it is not executed.这里的问题是unittest.main()
似乎在完成后退出,因此它之后的任何代码都没有执行。Any suggestion?有什么建议吗?
Thanks!谢谢!
I ended up writing two new classes, inheriting from unittest.TextTestResult
and unittest.TextTestRunner
.我最终编写了两个新类,继承自unittest.TextTestResult
和unittest.TextTestRunner
。 That way, I could run main like that:这样,我可以像这样运行 main:
unittest.main(testRunner=xmlrunner.XMLTestRunner(...))
I overloaded unittest.TextTestRunner
's __init__
and those from unittest.TextTestResult
:我重载了unittest.TextTestRunner
的__init__
和来自unittest.TextTestResult
那些:
addSuccess()
addError()
addFailure()
addSubTest()
For example:例如:
def addSuccess(self, test):
super().addSuccess(test)
[... store the test into list, dictionary, whatever... ]
Since these add*()
functions are called with the actual test, I can store them in a global list and parse them at the end of my XMLTestRunner.run()
:由于这些add*()
函数是在实际测试中调用的,我可以将它们存储在一个全局列表中,并在我的XMLTestRunner.run()
的末尾解析它们:
def run(self, test):
result = super().run(test)
self.save_xml_report(result)
return result
Note that these functions are normally defined in /usr/lib/python3.4/unittest/runner.py
.请注意,这些函数通常在/usr/lib/python3.4/unittest/runner.py
定义。
Warning note : By using an actual object passed unittest.main()
's testRunner
argument as shown, the command line arguments given when launching python are ignored.警告说明:通过使用实际对象传递unittest.main()
的testRunner
参数,如图所示,启动 python 时给出的命令行参数将被忽略。 For example, increasing verbose level with -v
argument is ignored.例如,使用-v
参数增加详细级别将被忽略。 This is because the TestProgram
class, defined in /usr/lib/python3.4/unittest/main.py
, detects if unittest.main()
was run with testRunner
being a class or an object (see runTests()
near the end of the file).这是因为在/usr/lib/python3.4/unittest/main.py
定义的TestProgram
类检测unittest.main()
是在testRunner
是一个类还是一个对象的情况下运行的(请参阅接近末尾的runTests()
文件)。 If you give just a class like that:如果您只提供这样的课程:
unittest.main(testRunner=xmlrunner.XMLTestRunner)
then command line arguments are parsed.然后解析命令行参数。 But you pass an instantiated object (like I need), runTests()
will just use it as is.但是您传递了一个实例化的对象(就像我需要的那样), runTests()
将按runTests()
使用它。 I thus had to parse arguments myself in my XMLTestRunner.__init__()
:因此,我不得不在我的XMLTestRunner.__init__()
自己解析参数:
# Similar to what /usr/lib/python3.4/unittest/main.py's TestProgram._getParentArgParser() does.
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-v', '--verbose', dest='verbosity',
action='store_const', const=2, default=1, # Add default=1, not present in _getParentArgParser()
help='Verbose output')
parser.add_argument('-q', '--quiet', dest='verbosity',
action='store_const', const=0,
help='Quiet output')
parser.add_argument('-f', '--failfast', dest='failfast',
action='store_true',
help='Stop on first fail or error')
parser.add_argument('-c', '--catch', dest='catchbreak',
action='store_true',
help='Catch ctrl-C and display results so far')
parser.add_argument('-b', '--buffer', dest='buffer',
action='store_true',
help='Buffer stdout and stderr during tests')
How does this work for you.这对你有什么作用。 Capture the output of unittest, which goes to sys.stderr, in a StringIO.在 StringIO 中捕获 unittest 的输出,该输出转到 sys.stderr。 Continue after unittest.main by adding `exit=False'.通过添加`exit = False'在unittest.main之后继续。 Read the captured output and process as you want.根据需要读取捕获的输出和处理。 Proof of concept:概念证明:
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())
This prints (to sys.stdout)这打印(到 sys.stdout)
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Note: contextlib has redirect_stdout, but not redirect_stderr.注意:contextlib 有redirect_stdout,但没有redirect_stderr。 The above is simpler that the contextlib code.上面的内容比 contextlib 代码更简单。 The above assumes that there are no exceptions not caught by unittest.以上假设没有未由 unittest 捕获的异常。 See the contextlib.contextmanager doc for adding try: except: finally.请参阅 contextlib.contextmanager 文档以添加 try: except: finally。 I leave that to you.我把它留给你。
I have faced the same issue with catching FAIL events from unittest lib.我在从 unittest lib 中捕获 FAIL 事件时遇到了同样的问题。 Following big_gie's answer, this code appeared:在 big_gie 的回答之后,出现了这段代码:
File testFileName_1.py
文件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())
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.