2012-03-22 88 views
10

由於具有多個輸出流,因此執行具有多個獨立故障模式的單個測試時出現了這個問題。我還想顯示在所有這些模式下聲明數據的結果,而不管哪一個首先失敗。除了使用套件來表示單個測試之外,Python的unittest沒有這樣的功能,這是不可接受的,因爲我的單個測試總是需要作爲一個單元運行;它只是沒有捕捉到事物的本質。如何在單個Python單元測試中處理多個斷言?

一個實際的例子是測試一個也會生成日誌的對象。你想斷言它的方法的輸出,但你也想斷言日誌輸出。這兩個輸出需要不同的測試,可以整齊地表示爲兩個股票斷言表達式,但是您也不希望一個人在測試中隱藏另一個可能的失敗。所以你真的需要同時測試兩者。

我拼湊了這個有用的小部件來解決我的問題。

def logFailures(fnList): 
    failurelog = [] 
    for fn in fnList: 
     try: 
      fn() 
     except AssertionError as e: 
      failurelog.append("\nFailure %d: %s" % (len(failurelog)+1,str(e))) 

    if len(failurelog) != 0: 
     raise AssertionError(
      "%d failures within test.\n %s" % (len(failurelog),"\n".join(failurelog)) 
     ) 

這是使用像這樣:

def test__myTest(): 
    # do some work here 
    logFailures([ 
     lambda: assert_(False,"This test failed."), 
     lambda: assert_(False,"This test also failed."), 
    ]) 

結果是logFailures()將引發一個包含日誌列表中的方法中提出的所有斷言的一個例外。

問題:雖然這樣做,我仍然想知道是否有更好的方法來處理這個問題,除了需要去創建嵌套測試等等的長度?

+3

「您也不希望某人在測試中隱藏另一方的可能失敗」。如果你想測試兩種不同的東西,請做兩個不同的測試! – 2012-03-22 19:43:51

+1

「你也不希望一個人在測試中隱藏另一個人的可能失敗」。是的 - 我想要:這些都是單元測試。如果一個測試失敗,請更正失敗並重新運行測試。 – 2012-03-22 20:52:20

回答

12

我不同意應該爲每個斷言寫一個測試方法的主流觀點。有些情況下你想用一種測試方法檢查多個事物。這裏是我如何做到這一點的回答:

# Works with unittest in Python 2.7 
class ExpectingTestCase(unittest.TestCase): 
    def run(self, result=None): 
     self._result = result 
     self._num_expectations = 0 
     super(ExpectingTestCase, self).run(result) 

    def _fail(self, failure): 
     try: 
      raise failure 
     except failure.__class__: 
      self._result.addFailure(self, sys.exc_info()) 

    def expect_true(self, a, msg): 
     if not a: 
      self._fail(self.failureException(msg)) 
     self._num_expectations += 1 

    def expect_equal(self, a, b, msg=''): 
     if a != b: 
      msg = '({}) Expected {} to equal {}. '.format(self._num_expectations, a, b) + msg 
      self._fail(self.failureException(msg)) 
     self._num_expectations += 1 

這裏有一些情況下,我認爲這是非常有用的,而不是冒險:

1)當你想測試不同的數據集的代碼。這裏我們有一個add()函數,我想用一些示例輸入來測試它。爲3個數據集編寫3種測試方法意味着重複自己,這是不好的。特別是如果呼叫更精細:

class MyTest(ExpectingTestCase): 
    def test_multiple_inputs(self): 
     for a, b, expect in ([1,1,2], [0,0,0], [2,2,4]): 
      self.expect_equal(expect, add(a,b), 'inputs: {} {}'.format(a,b)) 

2)當你想檢查一個函數的多個輸出。我想檢查每個輸出,但我不希望第一次失敗掩蓋其他兩個。

class MyTest(ExpectingTestCase): 
    def test_things_with_no_side_effects(self): 
     a, b, c = myfunc() 
     self.expect_equal('first value', a) 
     self.expect_equal('second value', b) 
     self.expect_equal('third value', c) 

3)用沉重的設置成本測試東西。測試必須快速運行或人們停止使用它們。一些測試需要數據庫或網絡連接,這會花費很長時間,這會降低測試速度。如果你正在測試數據庫連接本身,那麼你可能需要加快速度。但是如果你正在測試一些不相關的東西,我們想爲一整套檢查做一次緩慢的設置。

+1

默認情況下,應該在單元測試框架中存在這樣的東西。有人知道有這種功能嗎? – pmos 2014-10-22 23:48:01

10

這對我來說就像過度工程。或者:

  • 在一個測試用例中使用兩個斷言。如果第一個斷言失敗,那麼這是真的,你不知道第二個斷言是否通過。但是,無論如何你要修復代碼,所以修復它,然後你會發現第二個斷言是否通過。

  • 寫兩個測試,一個檢查每個條件。如果您擔心測試中重複的代碼,請將大部分代碼放入您從測試中調用的幫助程序方法中。

3

與使用子測試,執行不會第一次失敗之後停止 https://docs.python.org/3/library/unittest.html#subtests

下面是例如具有兩個故障斷言:

class TestMultipleAsserts(unittest.TestCase): 

    def test_multipleasserts(self): 
     with self.subTest(): 
      self.assertEqual(1, 0) 
     with self.subTest(): 
      self.assertEqual(2, 0) 

輸出將是:

====================================================================== 
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "./test.py", line 9, in test_multipleasserts 
    self.assertEqual(1, 0) 
AssertionError: 1 != 0 

====================================================================== 
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "./test.py", line 11, in test_multipleasserts 
    self.assertEqual(2, 0) 
AssertionError: 2 != 0 

---------------------------------------------------------------------- 
Ran 1 test in 0.000s 

FAILED (failures=2) 

您可以輕鬆打包子測試如下

class MyTestCase(unittest.TestCase): 
    def expectEqual(self, first, second, msg=None): 
     with self.subTest(): 
      self.assertEqual(first, second, msg) 

class TestMA(MyTestCase): 
    def test_ma(self): 
     self.expectEqual(3, 0) 
     self.expectEqual(4, 0) 
相關問題