2012-06-16 69 views
1

我嘗試用鼻試驗 但是當我運行下面怪異的行爲的nosetest

import unittest 

class TestSuite(unittest.TestCase): 
    b = [] 

    def setUp(self): 
     self.b.extend([10, 20]) 

    def tearDown(self): 
     self.b = [] 

    def test_case_1(self): 
     self.b.append(30) 
     assert len(self.b) == 3 
     assert self.b == [10, 20, 30] 

    def test_case_2(self): 
     self.b.append(40) 
     assert len(self.b) == 3 
     assert self.b == [10, 20, 40] 

但所有測試用例的測試用例沒有通過

$> nosetest test_module.py 
.F 
====================================================================== 
FAIL: test_case_2 (test_module2.TestSuite) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
File "/Users/knt/test_module2.py", line 19, in test_case_2 
    assert len(self.b) == 3 
AssertionError 

---------------------------------------------------------------------- 
Ran 2 tests in 0.001s 

FAILED (failures=1) 

什麼happend ???我預計運行test_case_1後,tearDown會被調用,所以self.b[]。所以下一個測試用例test_case_2,setUp run和self.b[10, 20]

但實際上,在setUp的值爲self.b is [10, 20, 30]

我不知道爲什麼。我認爲在陳述self.b = []中肯定存在一些問題。

任何相關的指針,我猜? 我仍然沒有弄明白,但我找到了解決這個錯誤的方法。只需將self.b = []更改爲del self.b[:]即可。

任何人都可以幫我找出問題所在? 非常感謝。

回答

2

的問題是在你的類屬性第2行:

b = [] 

即在類TestSuite的屬性。 Nosetests創建類TestSuite的新實例,然後調用setUp。在setUp,修改屬性的類TestSuite

self.b.extend([10, 20]) 

然後,在tearDown,你的第一次測試運行後,將創建一個新的列表,並將其分配到一個新的實例屬性這也恰好被稱爲b

self.b = [] 

這並不是在所有的修改屬性。從此實例訪問self.b的其他嘗試將返回實例屬性,而不是類屬性。

但這並沒有影響,因爲這發生在第二天的事情是,nosetests扔掉的TestSuite當前實例,包括比如你新的空單屬性b您在tearDown已設置。

然後nosetests創建類TestSuite的全新實例,以運行第二次測試。但類TestSuite仍具有b屬性包含[10, 20, 30],因爲它在第一次測試運行時進行了修改。

然後nosetests setUp方法運行,增加了1020TestSuite屬性b。然後你的第二個測試運行,增加了40TestSuite屬性,和你的第二個測試失敗,因爲它發現在屬性六個項目:

[10,20,30,10,20,40] 

之所以del self.b[:]作品是因爲,就像append,del正在修改屬性,而不是創建新的實例屬性。

確保您瞭解類和實例以及類屬性和實例屬性之間的區別,否則只要您使用Python,就會遇到類似的問題。

0

那麼,嘗試更換:

self.b.extend([10,20]) 

self.b = [10,20] 

,也許拋出了類變量,你不需要它,它可能是出現這種情況的原因。

+0

在這種情況下的問題是不通過所有的測試。我想知道單元測試是如何工作的? – kiennt

4

至於我可以告訴這個問題可能與方式unitests作品和類字段在Python如何在這裏工作是一個簡單的測試:

class A: 
    b = [] 
    def reset(self): 
     self.b = [] 

a = A()  
a.b.append(3) # we are actually accessing the class variable here 
print A.b is a.b # True 
print a.b # [3] same here 
a.reset() # We just hid the class variable with our own which points to [] 
print A.b is a.b # False as expected. 
print a.b # [] we think its being clear but rather we are using our own not the class variable 

b = A() 
print b.b # [3] b here is using the class that a previously modified but is no longer pointing to 
print b.b is A.b # True 

# Also note 
c = A() 
d = A() 
print c.b is d.b # True, since both are using the same class variable. 

我覺得單元測試是創建對象多次,每個測試函數,創建對象,觸發設置,訪問類變量,測試運行,調用拆卸它只是隱藏它,創建另一個對象,調用設置,它訪問與前一個對象修改相同的類變量,並且自從其撕裂向下簡單地創建了一個綁定到自己的新實例,隱藏了類的版本。

我們總是使用self聲明__init__中的成員字段。

def __init__(self): 
    self.b = [] 

這樣每個實例都會有它自己的拷貝,雖然我們不能做到這一點,因爲在這裏我們從unittest.TestCase這就是繼承我們爲什麼setUp

import unittest 
class TestSuite(unittest.TestCase): 
    def setUp(self): 
     self.b = [10, 20] 

    def tearDown(self): 
     self.b = [] 

    def test_case_1(self): 
     self.b.append(30) 
     assert len(self.b) == 3 
     assert self.b == [10, 20, 30] 

    def test_case_2(self): 
     self.b.append(40) 
     assert len(self.b) == 3 
     assert self.b == [10, 20, 40] 
+0

謝謝@ samy.vilar,你的關於class字段的解釋很明確 – kiennt

+0

@ kiennt沒有問題,當我開始時我通過相同的事情,另一個獲取人的細微問題是默認參數&引用'def test(key,values = {});值[key] ='test';返回值'有些人認爲每次你調用'test'時,值都會被設置爲'{}',但是不會在函數的初始定義過程中發生一次,並且由於字典被存儲爲參考,所以同一個字典將會使用'每次'你稱這個函數可能或可能不是你想要的,無論哪種方式,我希望你保持/享受python,它是一種偉大的語言...... –

+0

再次感謝,你提到的有關默認論證的問題對我而言仍然是新的。您的幫助對我更清楚地瞭解python非常有用。我也同意你的觀點,python是一門偉大的語言:x – kiennt