2011-04-14 52 views
5

我有一個python函數,如果通過不良數據被陷入無限循環。我想編寫一個單元測試來確認它能夠優雅地處理錯誤的參數。問題當然是,如果它沒有檢測到錯誤的參數,它將不會返回。針對潛在的無限循環Python單元測試

使用線程爲這種類型的事物編寫測試可以接受嗎?

import threading, unittest 
import mymodule 

class CallSuspectFunctionThread(threading.Thread): 
    def __init__(self, func, *args): 
     self.func = func 
     self.args = args 
     super(CallSuspectFunctionThread, self).__init__() 
    def run(self): 
     self.func(*self.args) 

class TestNoInfiniteLoop(unittest.TestCase): 
    def test_no_infinite_loop(self): 
     myobj = mymodule.MyObject() 
     bad_args = (-1,0) 
     test_thread = CallSuspectFunctionThread(mybj.func_to_test, *bad_args) 
     test_thread.daemon = True 
     test_thread.start() 
     # fail if func doesn't return after 8 seconds 
     for i in range(32): 
      test_thread.join(0.25) 
      if not test_thread.is_alive(): 
       return 
     self.fail("function doesn't return") 

我這個看到的唯一的問題是,如果測試失敗,我堅持這個額外的線程,可能消耗CPU和內存資源,而測試的其餘部分被執行。另一方面,我已經修復了代碼,並且這裏的迴歸可能性非常小,所以我不知道是否包含測試是否重要。

回答

1

你不可能測試這個。如果另一個線程只是「慢」或更差「活鎖」(即等待共享資源)會怎麼樣?你所做的任何假設都可能違背現實。

你不能聳聳肩,說:「它總是使用在1.5秒內終止,但現在它似乎需要一點時間。我會盡力2秒鐘,看它是否通過。」這是不可接受的。

您必須始終證明終止所有循環。所有循環。

如果不這樣做僅僅是一個設計,是如此之差,它應該永遠看不到光明的一天。

如果你不能防止和無限循環,你沒有完成設計。

這不是一個可以測試的東西。

+0

好點。所以這是單元測試的限制。一個**如何證明**終止?就我而言,我試圖確定數學函數的一些簡單屬性。邊緣情況似乎是描述的曲線表現不佳(即不會收斂/沒有局部極端)。我承認我可以採取懶惰的方式;編寫測試並繼續前進然後找出卡住的位置會更容易。 – 2011-04-14 17:23:51

+1

@Aryeh Leib Taurog:這不是「單元測試」的限制。這是所有測試的限制。您無法測試終止。你必須證明它。如果你正在做功能分析,你必須真正做數學來確定什麼約束定義收斂,並拒絕所有不能收斂的初始條件。對不起,但你必須做數學。 – 2011-04-14 17:29:53

+3

並非所有問題都有解決方案。在這種情況下確定一組完整的輸入約束不是一個現實的方法。我的意思是簡單地使用循環計數器來停止計算,一旦它變得太過分了。但是,知道距離太遠並不總是那麼簡單。 – 2011-04-14 19:21:25

1

我想補充的測試,測試的無限循環,因爲如果在測試過程中掛起那麼至少你已經抓住了生產前的錯誤。如果它發生的機會很渺茫(如你所說),那麼我不會擔心一個複雜的測試,但我會測試代碼。

0

對這種情況進行測試是一個好主意。當代碼被測試時,你可以對它的質量更有信心。

至於你創建測試函數的線程,這裏是two ways to kill a thread in python。既然你在一個有無限循環的函數中,你將不得不使用第二種方法來打破循環。

+0

我看到了,但它看起來像是過度殺傷 - 赦免雙關語。正如MichałŠrajer所建議的那樣,如果儘管S. Lott的建議反對它,我仍然可以使用超時修飾器來進行測試。 – 2011-04-14 17:35:31

12

您可以添加超時裝飾器。將測試用例的邏輯與超時機制實現分開很好。這將使您的代碼更易讀,更易於維護。

http://pypi.python.org/pypi/timeout

+1

+1爲簡單起見。我喜歡這個庫使用SIGALRM,這比線程更合適。我注意到,它似乎不能在windows下工作。但這並不直接解決這個問題,這是這種測試的可取之處。 – 2011-04-14 17:12:29

+1

-1:一般來說效果不好。有一天,你的計算機比一般的計算機更忙,並且花費的時間超過了超時時間,而你正在調試整個系統的工作負載,而不是你的應用程序。你不能通過測試來做到這一點。 – 2011-04-14 17:31:03

+4

@ S.Lott:最終用戶不關心你的程序爲什麼掛起 - 它應該儘可能優雅地處理所有情況,包括資源不足的情況。例如,對於具有GUI的應用程序來接管一秒鐘以向用戶操作提供反饋,這是不可接受的。你不需要設置一個超時函數接近函數返回的時間。如果函數通常需要0.01秒才能返回,那麼將函數設置爲10秒即可,如果函數可能需要很長時間並且被認爲是可接受的。 – ArtOfWarfare 2013-08-20 15:22:59

0

其實你不必去捕捉它。只要它在你的測試中,你一定會注意到它是否出錯。

+0

如果您在燈光模式下運行單元測試,則不適用。 – 2012-05-18 21:11:52

0

也許在你的情況下,你可以解決它作爲接受的答案說。對你有好處。 但是,如果不能統治「永久掛起」的情況,有些情況(用於集成和冒煙測試,而不是單元測試)。對於這些情況,我喜歡this timeout解決方案。