2011-07-08 54 views
12

目前我的項目是由各種具體的類組成。現在,當我進入單元測試時,看起來我應該爲每個類創建一個接口(有效地將項目中的類數加倍)?我碰巧使用Google Mock作爲嘲弄的框架。見Google Mock CookBook on Interfaces。雖然之前我可能只有類CarEngine,現在我會有抽象類(又名C++接口)CarEngine,然後是實現類CarImplementationEngineImpl或其他什麼。這將允許我剔除CarEngine的依賴。單元測試:編碼到接口?

有思想的兩行我已經在研究這個遇到:

  1. 只使用接口時,你可能有一個以上的 實現給定的抽象的和/或用於需要公共API, ,否則不要不必要地創建接口。

  2. 單元測試存根/嘲笑 經常「其他實施」,所以,是的,你應該創建 intefaces。

單元測試時,我應該爲我的項目中的每個類創建一個接口嗎? (我傾向於創建易於測試的界面)

回答

0

爲項目中的每個類創建接口可能需要也可能不需要。這完全是一個設計決定。我發現它大多不是。通常在n-tier設計中,你希望你抽象出數據訪問和邏輯之間的層。我認爲你應該努力實現這一目標,因爲它有助於測試邏輯,而不需要太多測試所需的基礎設施。像dependency injection and IoC這樣的抽象方法會要求你做這樣的事情,並且可以更容易地測試所說的邏輯。

我會檢查你正在測試的內容,並專注於你認爲最容易出錯的區域。這可以幫助您確定接口是否必要。

1

實施存在知名度兩類測試:黑箱測試和白箱測試

  • 黑箱測試的重點是測試執行通過它們的接口,並驗證調整自己的規範。

  • 白盒測試測試通常可以從外部訪問的關於實現的細粒度細節應該不是。這種測試將驗證實現組件按預期工作。因此,他們的結果是最感興趣的開發人員試圖弄清楚什麼是壞了,或者它們的定義適合需要mantainance

嘲笑成模塊化的架構,但它並不意味着項目中的所有類都需要完全模塊化出去。當一組班級相互瞭解時,可以畫出一些線。它們作爲一個組可以從某些外觀接口類的persesent中呈現給其他模塊。但是,您仍然希望在該模塊中使用白盒測試驅動程序,並瞭解實施細節。因此這種測試是不適合模擬

由此可以看出,你不需要爲所有東西都進行模擬或者接口。只需要實現門面接口的高級設計組件併爲它們創建模擬。它會給你的甜蜜點,其中模擬測試不負有心人恕我直言

話說回來,嘗試使用工具來您的需求,而不是讓工具強迫你改變,你覺得會不會在長期有利運行

+0

感謝您的回答;我正在努力尋找它。你是說白盒測試不適合模擬嗎?我在看單元測試的方式是我試圖測試每個代碼單元。例如,我想測試'Car'和'Car'取決於'Engine'類。爲了確保我正在做一個* unit *測試而不是* integration *測試,我*需要*模擬'Engine'類(以某種方式獲取'Car'來使用我的模擬'Engine') 。否則,我正在測試兩個類並進行集成測試,而不是單元測試。爲了輕鬆模擬'Engine'類,它似乎需要成爲一個接口。 – User

+0

這裏唯一的區別是*單元*代表什麼。通常對於某些人來說,被測試的單元代表一個編譯單元或一個類,但它並不一定如此。例如,如果你創建一個圖類,節點和邊將相互高度耦合,所以它對於單獨測試它們毫無意義。只需將整個圖形模塊包裝在Facade界面中,並且當其他模塊需要與圖形的模擬表示進行交互時,模擬圖形外觀,而不是單個類(作爲適當的外觀,將需要節點的API級別表示,如整數或ID) – lurscher

+0

在我對單元測試的理解中,如果邊緣類使用節點類,那麼當我測試邊緣類時,我想模擬節點類,因爲我不想測試兩個「單元」與此同時。如果邊緣類測試失敗,我不知道它是否是真正失敗的邊緣或節點。我想我會打電話給你談論的集成測試(意識到它只是一定程度的語義)。 – User

5

認爲你有很多選擇。正如你所說,一種選擇是創建接口。假設你有類

class Engine: 
{ 
public: 
    void start(){ }; 
}; 

class Car 
{ 
public: 
    void start() 
    { 
     // do car specific stuff 
     e_.start(); 

private: 
    Engine e; 
}; 

介紹接口 - 你將不得不改變汽車採取的引擎

class Car 
{ 
public: 
    Car(Engine* engine) : 
    e_(engine) 
    {} 

    void start() 
    { 
     // do car specific stuff 
     e_->start(); 

private: 
    Engine *e_; 
}; 

如果你只有一個類型的發動機 - 你突然做了你的車難以使用的對象(誰創建引擎,誰擁有引擎)。汽車有很多零件 - 所以這個問題會不斷增加。

如果你想單獨實現,另一種方法是使用模板。這消除了對接口的需求。

class Car<type EngineType = Engine> 
{ 
public: 
    void start() 
    { 
     // do car specific stuff 
     e_.start(); 

private: 
    EngineType e; 
}; 

在你的嘲笑,然後你可以用專門的引擎打造汽車:

Car<MockEngine> testEngine; 

另外,不同的方法,將方法添加到引擎允許它進行測試,是這樣的:

class Engine: 
{ 
public: 
    void start(); 
    bool hasStarted() const; 
}; 

然後,您可以將檢查方法添加到Car,或者從Car繼承來測試。

class TestCar : public Car 
{ 
public: 
    bool hasEngineStarted() { return e_.hasStarted(); } 
}; 

這將需要將發動機從私人更改爲保護在汽車類。

根據現實世界的情況,將取決於哪種解決方案最好。另外,每個開發人員都會有自己的聖盃,看看他們如何相信代碼應該進行單元測試。我個人的觀點是牢記客戶/客戶。讓我們假設你的客戶(可能是你的團隊中的其他開發人員)將創建汽車並且不關心引擎。因此我不想公開發動機的概念(我的圖書館內部的一個類),所以我可以單元測試這個東西。我會選擇不創建接口並一起測試這兩個類(我給出的第三個選項)。