2008-11-23 88 views
13

有沒有什麼好的方法來單元測試析構函數?就像說我有一類這樣的(人爲)例如:單元測試析構函數?

class X 
{ 
private: 
    int *x; 

public: 
    X() 
    { 
     x = new int; 
    } 

    ~X() 
    { 
     delete x; 
    } 

    int *getX() {return x;} 
    const int *getX() const {return x;} 
}; 

有沒有什麼好辦法的單元測試是爲了確保X不會搞亂我的HPP文件使用#ifdef來測試或破壞了封裝被刪除?我看到的主要問題是很難判斷x是否真的被刪除,特別是因爲在調用析構函數時對象超出了範圍。

回答

7

依賴注入可能有些事要說。除了在構造函數中創建一個對象(在這種情況下是一個int,但是在非構思的情況下更可能是用戶定義的類型),該對象作爲參數傳遞給構造函數。如果稍後創建了對象,那麼工廠將傳遞給構造函數X.

然後,當您進行單元測試時,將傳入一個模擬對象(或創建模擬對象的模擬工廠)和析構函數記錄它被調用的事實。如果不是,則測試失敗。

當然你不能嘲笑(或者替換)一個內建類型,所以在這種特定情況下它沒有好,但如果你有一個接口定義對象/工廠,然後就可以了。如其他人所說,檢查單元測試中的內存泄漏通常可以在更高級別上完成。但是這隻會調用一個析構函數,它並不能證明調用了析構函數。所以它不會在x成員類型的析構函數上捕獲缺少的「虛擬」聲明(同樣,如果它只是一個int,則不相關)。

0

某些編譯器會在調試模式下用已知模式覆蓋已刪除的內存,以幫助檢測對懸掛指針的訪問。我知道Visual C++曾經使用過0xDD,但我暫時還沒有用過它。

在您的測試情況下,可以存儲x的拷貝,讓它走出去的範圍,並確保* X == 0xDDDDDDDD:

void testDestructor() 
{ 
    int *my_x; 
    { 
     X object; 
     my_x = object.getX(); 
    } 
    CPPUNIT_ASSERT(*my_x == 0xDDDDDDDD); 
} 
+0

我鄙視這個想法。您無法啓用單元測試代碼並啓用了優化功能,或者您的編譯器發生了變化 – 2008-11-23 02:42:32

+0

我同意由於您提到的原因,它不是很好,並且您無法訪問專用指針。一般來說,我認爲你的想法(和onebyone的)更好,但使用模擬對象並不總是微不足道的。 – 2008-11-23 03:53:23

1

我傾向於去了「通過任何手段必要「的測試方法。如果它需要測試,我願意泄漏抽象,破解封裝,並且破解......因爲測試過的代碼比漂亮的代碼更好。我會經常將打破此類方法的名稱命名爲VaildateForTesting或OverrideForTesting,以清楚說明封裝的違反僅適用於測試。

我不知道有任何其他方式在C++中執行此操作,而不是將析構函數調用爲單例來註冊它已被銷燬。我已經想出了一種在C#中使用弱引用來做類似於這種事情的方法(我沒有用這種方法違反封裝或抽象)。我沒有足夠的創造力來推論C++的類比,但你可能是。如果有幫助,很好,如果不是,對不起。

http://houseofbilz.com/archive/2008/11/11/writing-tests-to-catch-memory-leaks-in-.net.aspx

0

不是一個平臺無關的建議,但在過去,我撥打的電話進行單元測試期間,CRT的堆檢查功能,以驗證是否有在試驗結束時沒有分配更多的內存(或也許是一整套測試)比開始。你也許可以用你的平臺的儀器做類似的事情,檢查手柄的數量等。

1

在這個例子中,定義和測量你自己的全局新建和刪除。

爲了避免#ifdefs,我做了測試類的朋友。您可以根據需要設置/保存/獲取狀態以驗證呼叫結果。

+0

這個想法沒有規模。如果你需要測試很多不同的類,你會實現不同版本的全局新建和刪除嗎?如果你是已經替換:: new和:: delete的單元測試代碼呢? – 2008-11-23 02:45:01

+0

我不知道你在想什麼稻草人。儀器旨在回答像「仍然分配X」這樣的問題嗎?其他答案利用平臺特定版本的內存儀器,而不是自己控制。 – 2008-11-23 05:21:48

5

我認爲你的問題是你目前的例子不可測試。既然你想知道x是否被刪除,你真的需要能夠用模擬代替x。這可能是一個int的OTT,但我想你的真實例子中有一些其他的類。爲了使它可檢驗的,在X構造函數需要,要求實現int接口的對象:

template<class T> 
class X 
{ 
    T *x; 
    public: 
    X(T* inx) 
    : x(inx) 
    { 
    } 

    // etc 
}; 

現在它變得簡單的價值嘲笑爲x,和模擬可以處理檢查正確的破壞。

請不要關注那些說你應該破解封裝或者使用可怕的黑客來獲得可測試代碼的人。儘管測試代碼比未經測試的代碼更好,但可測試代碼是最好的代碼,它總是會導致代碼更加清晰,黑客少,耦合度低。

1

這與問問題的人沒有關係,但可能對閱讀此內容的人有幫助。在面試中我被問到了類似的問題。

假設記憶是有限的,你可以試試這個方法:

  1. ,直到分配失敗,內存不足的消息(運行任何有關試驗的析構函數之前)分配內存並節省了內存的大小在運行測試之前可用。
  2. 運行測試(調用構造函數並根據需要在新實例上執行一些操作)。
  3. 運行析構函數。
  4. 再次運行分配部分(如步驟1) 如果您可以在運行測試之前分配完全相同的內存,則析構函數將正常工作。

這種方法適用(合理),當你有小的有限的記憶,否則似乎不合理,至少對我的意見。