测试结果类,用来处理测试用例或测试集执行过程中的所有信息并最终输出,比如代码错误、异常、断言失败、skip等等。所以如果想要增加一些个性化的输出,可以通过或者此类或者基类(TestResult),扩展HTMLTestRunner的大神就是扩展了基类TestResult,增加了一下几个重要的统计属性和重写了基类的一些方法。大家可以去阅读一下它的源码。
本来想讲TextTestResult的,但发现其实TextTestResult也是扩展了TestResult类的,所以干脆就直接分析TestResult了,我们先看一下基类提供了哪些方法可供我们扩展:
def printErrors(self):
"Called by TestRunner after test run"
注释已经说明了,在测试用例执行完成后才应该被TestRuner调用,我们可以看一下这个方法在哪个地方有调用过:
我们可以看到有两处调用了这个方法:第一处我们可以不看,因为并不是unittest框架里面的,我们直接看第二处,可以看到是在TextTestRunner的run方法中有调用,我们看看是在run方法中的什么位置调用了这个方法
class TestRunner(unittest.TextTestRunner):
def run(self, test, skipped):
"Run the given test case or test suite."
# Same as unittest.TextTestRunner.run, except that it reports
# skipped tests.
result = self._makeResult()
startTime = time.time()
test(result)
stopTime = time.time()
timeTaken = stopTime - startTime
result.printErrors()
self.stream.writeln(result.separator2)
run = result.testsRun
if _unavail: #skipped:
从代码我们可以看出,result.printErrors()方法是在test(result)之后才调用的,所以这符合printErrors方法的调用场景了吧
def startTest(self, test):
"Called when the given test is about to be run"
self.testsRun += 1
self._mirrorOutput = False
self._setupStdout()
从注释说明中可以看出,此方法是在用例准备执行之前会被调用一次,我们还是来看一下此方法的调用情况,同样可以看到在TestCase类中的run方法中有调用此方法:
def run(self, result=None):
orig_result = result
if result is None:
result = self.defaultTestResult()
startTestRun = getattr(result, 'startTestRun', None)
if startTestRun is not None:
startTestRun()
self._resultForDoCleanups = result
result.startTest(self)
testMethod = getattr(self, self._testMethodName)
if (getattr(self.__class__, "__unittest_skip__", False) or
#省略下面的代码
...
可以看出确实是在testMethod执行前调用了一次。
def startTestRun(self):
"""Called once before any tests are executed.
See startTest for a method called before each test.
"""
跟startTest类似,用例执行之前调用,依然在TestCase类中的run方法可以看到,沿用上面startTest贴的run部分的代码,可以看到在执行startTest之前会去判断我们传入的result是否为None,如果为None,才会去初始化result,并判断result是否有startTestRun方法,如果有则执行startTestRun方法
def stopTest(self, test):
"""Called when the given test has been run"""
self._restoreStdout()
self._mirrorOutput = False
在用例被执行之后才会执行此方法,依然还是在TestCase类中的run方法可以看到,不过此方法有两处调用,我们来看一下代码:
第一处调用:
def run(self, result=None):
#省略startTestRun和startTest部分代码
...
testMethod = getattr(self, self._testMethodName)
if (getattr(self.__class__, "__unittest_skip__", False) or
getattr(testMethod, "__unittest_skip__", False)):
# If the class or method was skipped.
try:
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
or getattr(testMethod, '__unittest_skip_why__', ''))
self._addSkip(result, skip_why)
finally:
result.stopTest(self)
return
try:
success = False
#省略下方代码
...
我们可以看到,此段代码在分析TestCase类的时候讲过,这是在判断我们编写的TestCase类是否又被skip装饰,或者TestCase类中写的方法是否又被skip装饰,如果有,则会执行一次stopTest方法(result.stopTest(self))
第二处调用:
try:
success = False
try:
self.setUp()
#省略了setUp处理部分
...
else:
try:
testMethod()
#省略了testMethod的处理部分
...
cleanUpSuccess = self.doCleanups()
success = success and cleanUpSuccess
if success:
result.addSuccess(self)
finally:
result.stopTest(self)
if orig_result is None:
stopTestRun = getattr(result, 'stopTestRun', None)
if stopTestRun is not None:
stopTestRun()
我们可以看到,是在用例执行完成后,不管有没有什么错误或异常都会执行一次stopTest方法(result.stopTest(self))
def stopTestRun(self):
"""Called once after all tests are executed.
See stopTest for a method called after each test.
"""
在所有用例执行完成之后被调用。跟startTestRun类似,当我们传入的参数result为None时,才会去执行这个方法
针对上面第2-4个方法我们举一个例子瞧一瞧:
#unittest_prac.py
import unittest
import sys
#扩展TestResult
class MyTestResult(unittest.TestResult):
def startTest(self, test):
print('startTest')
def startTestRun(self):
print('startTestRun')
def stopTest(self, test):
print('stopTest')
def stopTestRun(self):
print('stopTestRun')
class UnitTestCase(unittest.TestCase):
def setUp(self):
print("setup")
def test01(self):
print('test01')
def tearDown(self):
print('teardown')
if __name__ == '__main__':
result = MyTestResult(sys.stdout,'test result',1)
testcase = UnitTestCase('test01')
testcase.run(result) #注意此处我传入了result参数
执行之后我们看一下结果:
C:\software\PythonWorkspace>python unittest_prac.py\
startTest\
setup\
test01\
teardown\
stopTest
我们可以看到确实调用了startTest、stopTest,没有去调用startTestRun、stopTestRun符合我们的分析结果。
这几个方法也都是可以在TestCase类的run方法中找到调用的地方,大家可以自己去试试分析一下
TestResult类分析差不多就到这里了,如果想了解怎么扩展,大家可以去阅读一下TextTestResult类的方法,或者阅读一下扩展HTMLTestRunner大神的代码HTMLTestRunner.py