failfast是TestResult的一个属性,缺省为False,python
做用: 若是failfast为True,一旦测试集中有测试案例failed或发生error当即终止当前整个测试执行,跳过剩下全部测试案例ide
各类用法: 实现“短路测试”,设置failfast为True测试
unittest.main(failfast=True)ui
unittest.TextTestRunner(failfast=True)debug
设置TestResult控制,eg:unittest.TextResult.failfast=Truecode
unittest.TestCase().run(result)utf-8
unittest.TestSuite测试集实例ts,ts.run(result)rem
override本身的测试案例类的run(self,testresult=None)方法,关键是设置testresult.failfast=True
get
局限:unittest缺省实现控制整个测试集的“短路测试”,不能很好的知足基于测试案例类层面的控制it
问题场景: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)
<unittest2.runner.TextTestResult run=23 errors=0 failures=7>