2008-10-12 26 views
9

這是我的問題,我想嘲笑一個類,它在初始化時創建一個線程,並在銷燬時關閉它。我的模擬課沒有理由實際創建和關閉線程。但是,爲了嘲笑一堂課,我繼承了它。當我創建我的模擬類的新實例時,調用基類構造函數,創建線程。當我的模擬對象被銷燬時,基類析構函數被調用,試圖關閉線程。你如何模擬在C++中使用RAII的類

如何模擬一個RAII類而不必處理實際的資源?

回答

12

您改爲製作一個描述類型的接口,並且將真實類和模擬類都從中繼承。所以,如果你有:

class RAIIClass { 
public: 
    RAIIClass(Foo* f); 
    ~RAIIClass(); 
    bool DoOperation(); 

private: 
    ... 
}; 

你會做一個接口,如:

class MockableInterface { 
public: 
    MockableInterface(Foo* f); 
    virtual ~MockableInterface(); 
    virtual bool DoOperation() = 0; 
}; 

,並從那裏。

+0

去哪裏呢? RAII類通常按值使用,而不是在堆上分配。它們主要用於處理堆棧中的堆對象以確定生命週期。 [實施例](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) – zahir 2016-10-20 19:33:50

6

首先,你的課程可能設計得很好,但是它們的測試設計很差,這不一定是不合理的。並非一切都很容易測試。

大概你想使用另一個函數或類,它使用你想模擬的類(否則解決方案是微不足道的)。讓我們稱之爲「用戶」,後者稱爲「模擬」。這裏有一些可能性:

  1. 更改用戶使用嘲笑的抽象版本(你會選擇什麼樣的抽象的使用方法:繼承,回調,模板等...)。
  2. 爲您的測試代碼編譯不同版本的Mocked(例如,在編譯測試時#def輸出RAII代碼)。
  3. 已Mocked接受構造函數標誌來關閉其行爲。我個人會避免這樣做。
  4. 只需要花費分配資源的成本。
  5. 跳過測試。

如果您不能修改用戶或Mocked,最後兩個可能是您唯一的追索權。如果您可以修改用戶,並且您認爲將代碼設計爲可測試很重要,那麼您應該在其他任何人之前探索第一個選項。請注意,在使您的代碼具有通用性/靈活性並保持簡單性之間可以進行權衡,這兩者都是令人欽佩的品質。

0

我使用的一種技術是使用某種形式的裝飾器。你的最終代碼有一個方法,它在堆棧上創建它的實例,然後調用相同的方法,但是在一個指向你的基類的成員上。當該調用返回時,您的方法返回銷燬您創建的實例。

在測試時間,您交換一個模擬,它不會創建任何線程,而只是轉發到您要測試的方法。

class Base{ 
protected: 
    Base* decorated; 
public: 
    virtual void method(void)=0; 
}; 
class Final: public Base{ 
    void method(void) { Thread athread; decorated->method(); } // I expect Final to do something with athread 
}; 
class TestBase: public Base{ 
    void method(void) { decorated->method(); } 
}; 
1

PIMPL方法可能適合你。創建您的Thread類,並帶下一個具體實現。如果將#defines和#ifdefs放在正確的位置,則可以在啓用單元測試時更改實現,這意味着您可以在實際實現和模擬實現之間切換,具體取決於您要實現的目標。