2011-03-24 113 views
11

假設我有類在C++中模擬對象總是需要虛擬方法或模板嗎?

class Inner { 
    public: 
    void doSomething(); 
}; 

class Outer { 
    public: 
    Outer(Inner *inner); // Dependency injection. 

    void callInner(); 
}; 

正確的單元測試說我應該有測試爲Inner。然後,我應該對Outer進行測試,該測試不使用真實的Inner而是使用MockInner,這樣我將對僅添加Outer的功能執行單元測試,而不是完整的堆棧Outer/Inner

要做到這一點,Googletest似乎暗示轉向Inner成純抽象類(接口)是這樣的:

// Introduced merely for the sake of unit-testing. 
struct InnerInterface { 
    void doSomething() = 0; 
}; 

// Used in production. 
class Inner : public InnerInterface { 
    public: 
    /* override */ void doSomething(); 
}; 

// Used in unit-tests. 
class MockInner : public InnerInterface { 
    public: 
    /* override */ void doSomething(); 
}; 

class Outer { 
    public: 
    Outer(Inner *inner); // Dependency injection. 

    void callInner(); 
}; 

因此,在生產代碼,我會用Outer(new Inner);而在測試中,Outer(new MockInner)

好的。理論上看起來不錯,但是當我在整個代碼中開始使用這個想法時,我發現自己爲每一個瘋狂的課程創建了一個純粹的抽象類。即使您可以忽略由於不必要的虛擬調度而導致的輕微運行時間性能下降,這也是很多鍋爐式輸入。

另一種方法是使用模板,如下:

class Inner { 
    public: 
    void doSomething(); 
}; 

class MockInner { 
    public: 
    void doSomething(); 
}; 

template<class I> 
class Outer { 
    public: 
    Outer(I *inner); 

    void callInner(); 
}; 

// In production, use 
Outer<Inner> obj; 

// In test, use 
Outer<MockInner> test_obj; 

這避免了鍋爐電鍍和不必要的虛擬調度;但是現在我的整個代碼庫都在怪異的頭文件中,這使得無法隱藏源實現(更不用說處理令人沮喪的模板編譯錯誤和長的構建時間)。

這兩種方法,虛擬和模板是進行適當單元測試的唯一方法嗎?有更好的方法來做適當的單元測試嗎?

通過適當的單元測試,我的意思是每個單元測試測試只能由單位推出的功能,但不是單位的依賴性也

回答

5

我不認爲你必須在實踐中嘲笑你的測試類的每個依賴。如果創建,使用或感知很複雜,那麼是的。此外,如果它直接依賴於一些不需要的外部資源,如數據庫,網絡或文件系統。

但是,如果這些都不是問題,IMO可以直接使用它的實例。由於您已經對其進行了單元測試,因此您可以合理確信它按預期工作,並且不會影響更高級別的單元測試。

我個人更喜歡工作單元測試和簡單,乾淨,可維護的設計,以堅持由單元測試純粹主義者設定的一些理想。

每個單元測試只測試該單元引入的功能,但不測試單元的相關性。

使用功能測試功能是兩個完全不同的事情。

+0

我個人更喜歡工作單元測試和簡單,乾淨,可維護的設計,以堅持單元測試純粹主義者設定的一些理想。 我喜歡這個迴應---保持事物的實用性。 – kirakun 2011-03-24 22:46:38

2

我也認爲直接在Inner上使用實例是可以的。 我的問題是嘲笑不屬於我的代碼(通過靜態庫或DLL,有時是第三方提供)的外部對象。 我傾向於用相同的類名重寫模擬的DLL或庫,然後進行不同的鏈接以進行測試。修改外部依賴項的頭文件以添加「虛擬」對我來說似乎是不可接受的。 有沒有人有更好的解決方案?