failfast是TestResult的一个属性,缺省为False,
作用: 如果failfast为True,一旦测试集中有测试案例failed或发生error立即终止当前整个测试执行,跳过剩下所有测试案例
各种用法: 实现“短路测试”,设置failfast为True
unittest.main(failfast=True)
unittest.TextTestRunner(failfast=True)
设置TestResult控制,eg:unittest.TextResult.failfast=True
unittest.TestCase().run(result)
unittest.TestSuite测试集实例ts,ts.run(result)
override自己的测试案例类的run(self,testresult=None)方法,关键是设置testresult.failfast=True
局限:unittest缺省实现控制整个测试集的“短路测试”,不能很好的满足基于测试案例类层面的控制
问题场景:eg: 同一测试集下N个相对独立的业务流程测试案例testcaseclass(每个包含n个步骤即testmethod)
期望每个testcaseclass对应的独立流程一旦有一个步骤failed终止执行该流程测试案例剩下步骤
每个testcaseclass的测试执行失败不影响其它同一测试集下的业务流程测试
希望统一业务流程测试集testsuite加载、执行、不是单独一个一个testcasecalss的加载、运行
问题方案:个人发现unittest目前的实现不能很好的解决5.中问题场景,实现初步验证ok方案如下:
直接override的unittest.suite模块TestSuite的run(self,result)方法(修改几行代码)
修改点如下:#注释对应部分为个人添加修改部分
def run(self, result, debug=False):
topLevel = False
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True
for index, test in enumerate(self):
# if result.shouldStop:
# break
if _isnotsuite(test):
self._tearDownPreviousClass(test, result)
self._handleModuleFixture(test, result)
self._handleClassSetUp(test, result)
# result._previousTestClass = test.__class__
# begin: failfast on the testcaselevel
if result._previousTestClass == test.__class__:
if result.shouldStop:
continue
else:
result._previousTestClass = test.__class__
if result.shouldStop:
result.shouldStop = False
# end
if (getattr(test.__class__, '_classSetupFailed', False) or
getattr(result, '_moduleSetUpFailed', False)):
continue
if not debug:
test(result)
else:
test.debug()
if self._cleanup:
self._removeTestAtIndex(index)
if topLevel:
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
return result
最好附上测试代码及执行结果:
# encoding: utf-8
'''
Created on 2015年9月10日
@author: laughlast
'''
import os
import unittest2
class BaseDemo(unittest2.TestCase):
'''BaseDemo: all ok except test_step_2'''
def test_step_1(self):
'''base'''
assert True
def test_step_2(self):
'''base'''
self.fail('fail()')
def test_step_3(self):
'''base'''
assert True
def test_step_4(self):
'''base'''
assert True
def test_step_5(self):
'''base'''
assert True
def test_step_6(self):
'''base'''
assert True
def test_step_7(self):
'''base'''
assert True
def test_step_8(self):
'''base'''
assert True
def test_step_9(self):
'''base'''
assert True
class BizDemo1(BaseDemo):
'''BizDemo1'''
pass
class BizDemo2(BaseDemo):
'''BizDemo2'''
pass
class BizDemo3(BaseDemo):
'''BizDemo3'''
pass
class BizDemo4(BaseDemo):
'''BizDemo4'''
pass
class BizDemo5(BaseDemo):
'''BizDemo5'''
pass
class BizDemo6(BaseDemo):
'''BizDemo6'''
pass
class BizDemo7(BaseDemo):
'''BizDemo7'''
def test_step_2(self):
'''BizDemo7 all ok'''
assert True
if __name__ == "__main__":
tloader = unittest2.defaultTestLoader
tpath = os.path.split(os.path.realpath(__file__))[0]
tsuite = tloader.discover(tpath, 'test_failfast*.py')
trunner = unittest2.TextTestRunner(failfast=True)
tresult = trunner.run(tsuite)
print tresult
assert tresult.testsRun == 2 * 7 + 9 \
and len(tresult.failures) == 7
执行结果参考:
----------------------------------------------------------------------
Ran 23 tests in 0.000s
FAILED (failures=7)