認爲你有很多選擇。正如你所說,一種選擇是創建接口。假設你有類
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(); }
};
這將需要將發動機從私人更改爲保護在汽車類。
根據現實世界的情況,將取決於哪種解決方案最好。另外,每個開發人員都會有自己的聖盃,看看他們如何相信代碼應該進行單元測試。我個人的觀點是牢記客戶/客戶。讓我們假設你的客戶(可能是你的團隊中的其他開發人員)將創建汽車並且不關心引擎。因此我不想公開發動機的概念(我的圖書館內部的一個類),所以我可以單元測試這個東西。我會選擇不創建接口並一起測試這兩個類(我給出的第三個選項)。
感謝您的回答;我正在努力尋找它。你是說白盒測試不適合模擬嗎?我在看單元測試的方式是我試圖測試每個代碼單元。例如,我想測試'Car'和'Car'取決於'Engine'類。爲了確保我正在做一個* unit *測試而不是* integration *測試,我*需要*模擬'Engine'類(以某種方式獲取'Car'來使用我的模擬'Engine') 。否則,我正在測試兩個類並進行集成測試,而不是單元測試。爲了輕鬆模擬'Engine'類,它似乎需要成爲一個接口。 – User
這裏唯一的區別是*單元*代表什麼。通常對於某些人來說,被測試的單元代表一個編譯單元或一個類,但它並不一定如此。例如,如果你創建一個圖類,節點和邊將相互高度耦合,所以它對於單獨測試它們毫無意義。只需將整個圖形模塊包裝在Facade界面中,並且當其他模塊需要與圖形的模擬表示進行交互時,模擬圖形外觀,而不是單個類(作爲適當的外觀,將需要節點的API級別表示,如整數或ID) – lurscher
在我對單元測試的理解中,如果邊緣類使用節點類,那麼當我測試邊緣類時,我想模擬節點類,因爲我不想測試兩個「單元」與此同時。如果邊緣類測試失敗,我不知道它是否是真正失敗的邊緣或節點。我想我會打電話給你談論的集成測試(意識到它只是一定程度的語義)。 – User