pyunit中的测试结果包括Pass、Fail、Error三种类型。但是很多时候,仅仅三种类型不足以区分。
比如当某些用例所依赖的外部环境不具备时,或者因为某些已知缺陷导致用例必然失败,等等。
如果执行这些用例,会浪费时间;如果不执行,又难以从测试报告中清晰的看到原因。扩展测试结果的类型能帮助我们解决这个问题。
#add a new method to set user defined result, such as block, investigation...
class UserDefinedResultTestCase(TestCase):
#@see TestCase.fail
def setresult(self, resulttype, msg):
raise self.failureException(UserDefinedResult.sealresult(resulttype, msg))
class UserDefinedResult(TextTestResult):
#seal and unseal user defined result by FAILURE
@staticmethod
def getMsgSepetor():
return ':::'
@staticmethod
def sealresult(resulttype, msg):
return resulttype + UserDefinedResult.getMsgSepetor() + msg
@staticmethod
def unsealresult(err):
if isinstance(err, tuple):
if isinstance(err[1], AssertionError):
errmsg = err[1].message
errinfo = errmsg.split(UserDefinedResult.getMsgSepetor())
if len(errinfo) == 2:
return errinfo
return None
@staticmethod
def unsealresultstr(errmsg):
errmsgbegin = errmsg.find('AssertionError:')
if errmsgbegin > 1:
errinfo = errmsg[errmsgbegin + 16:].strip().split(UserDefinedResult.getMsgSepetor())
if len(errinfo) == 2:
return errinfo
return None
#console output when fail, including user defined fail
#@see TextTestResult.addFailure
def addFailure(self, test, err):
#just use TestResult's addFailure, not TextTestResult's
super(TextTestResult, self).addFailure(test, err)
fulltype = 'FAIL'
shorttype = 'F'
errinfo = UserDefinedResult.unsealresult(err)
if not errinfo == None:
fulltype = errinfo[0]
shorttype = errinfo[0][0:1]
if self.showAll:
self.stream.writeln(fulltype)
elif self.dots:
self.stream.write(shorttype)
self.stream.flush()
#console output of detail info
#@see TextTestResult.printErrors
def printErrors(self):
if self.dots or self.showAll:
self.stream.writeln()
self.printErrorList('ERROR', self.errors)
self.failureDict = {}
for aTest, aFailure in self.failures:
failureinfo = UserDefinedResult.unsealresultstr(aFailure)
if not failureinfo == None:
if failureinfo[0] in self.failureDict.keys():
self.failureDict[failureinfo[0]].append((aTest, aFailure))
else:
self.failureDict[failureinfo[0]] = [(aTest, aFailure)]
else:
if 'FAIL' in self.failureDict.keys():
self.failureDict['FAIL'].append((aTest, aFailure))
else:
self.failureDict['FAIL'] = [(aTest, aFailure)]
for errtype in self.failureDict.keys():
self.printErrorList(errtype, self.failureDict[errtype])
#console output of summary info
#@see TextTestRunner.run
def printFailureSummary(self):
if not self.wasSuccessful():
infos = []
for errtype in self.failureDict.keys():
infos.append('%s=%d' % (errtype, len(self.failureDict[errtype])))
self.stream.writeln(" (%s)" % (", ".join(infos),))
#user defined TestProgram, set TestResult Class, print failure summary
#@see TestProgram.__init__
class UserDefinedTestProgram(TestProgram):
def __init__(self, module='__main__', defaultTest=None, argv=None,
testLoader=loader.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
buffer=None):
mytestRunner = TextTestRunner(resultclass=UserDefinedResult)
super(UserDefinedTestProgram, self).__init__(module, defaultTest, argv,
mytestRunner, testLoader,
False, verbosity, failfast, catchbreak,
buffer)
self.result.printFailureSummary()
if exit:
sys.exit(not self.result.wasSuccessful())
###############################
class testdemo4(UserDefinedResultTestCase):
def testpass(self):
pass
def testFail(self):
self.fail('fail')
def testUserDefinedResult1(self):
self.setresult('DTS', '20140802115316133')
def testUserDefinedResult2(self):
self.setresult('DTS', '--------')
def testUserDefinedResult3(self):
self.setresult('REQ', 'you have to change it')
def testUserDefinedResult4(self):
self.setresult('ENV', 'no device exists')
def testUserDefinedResult5(self):
self.setresult('BLOCK', 'do not run')
def testUserDefinedResult6(self):
self.setresult('BLOCK', 'feature on old version')
###################
if __name__ == '__main__':
UserDefinedTestProgram()
简单起见,我并没有彻底改造TestResult和TestCase,而是在结果输出的地方做了一些扩展,虽然代码有点牵强,但是改动的工作量和影响是比较小的,而且也达到了预期的效果。
执行的结果
FDDREBB.
========================================
FAIL: testFail (__main__.testdemo4)
----------------------------------------
Traceback (most recent call last):
AssertionError: fail
========================================
DTS: testUserDefinedResult1 (__main__.te
----------------------------------------
Traceback (most recent call last):
AssertionError: DTS:::20140802115316133
========================================
DTS: testUserDefinedResult2 (__main__.te
----------------------------------------
Traceback (most recent call last):
AssertionError: DTS:::--------
========================================
REQ: testUserDefinedResult3 (__main__.te
----------------------------------------
Traceback (most recent call last):
AssertionError: REQ:::you have to change
========================================
BLOCK: testUserDefinedResult5 (__main__.
----------------------------------------
Traceback (most recent call last):
AssertionError: BLOCK:::do not run
========================================
BLOCK: testUserDefinedResult6 (__main__.
----------------------------------------
Traceback (most recent call last):
AssertionError: BLOCK:::feature on old v
========================================
ENV: testUserDefinedResult4 (__main__.te
----------------------------------------
Traceback (most recent call last):
AssertionError: ENV:::no device exists
----------------------------------------
Ran 8 tests in 0.002s
FAILED (failures=7)
(FAIL=1, DTS=2, REQ=1, BLOCK=2, ENV=1)
由于之前的账号【ist】注册邮箱一直修改不成功,无奈重新注册一个账号,将之前的帖子搬过来,原帖删除。