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>