2012-12-20 35 views
8

我正在進行單元測試的第一步,並且遇到了封裝問題。我的類有一些私人成員變量,這些變量對客戶端來說應該是不可見的,但爲了讓我把對象置於我想測試它的狀態,我需要設置這些私有變量。單元測試 - 設置私有成員以獲得期望的對象狀態

說我有一個這樣的代碼:

Class Foo { 

public: 
    int action() ; 
private: 
    int state ; 

} ; 

int Foo::action() 
{ 
    if(this->state == 1) 
    return 1 ; 
    else 
    return 0 ; 
} 

所以現在我想測試Foo::action(),但我需要可以設置Foo::state要能在不同情況下,檢查功能。一種解決方案是測試代碼中的邪惡「define private public」。但是有更優雅的東西嗎?我想強調的是Foo::state是一個不應該被客戶端訪問的變量,所以我不想聲明任何公共setter。

編輯:

我現在認爲,在派生類中擴展我想測試的類測試代碼和包括制定者會工作,提供我改變私有變量,以保護。但這是一種「僅限一代人」的解決方案,仍然感覺像是一種黑客而不是一種正確的方法。

編輯2:

閱讀答案和意見後,我得到了(感謝利芬和AP尤其如此。)我認爲實際的類我想現在來測試(不是我提供的簡單的例子)簡單地做得太多了,我的問題的答案是將其一些邏輯轉移到另一個將由大個子使用的類。

+0

可能的重複[如何測試具有私有方法,字段或內部類的類?](https://stackoverflow.com/questions/34571/how-do-i-test-a-class-that -has-private-methods-fields-or-inner-classes) – Raedwald

回答

3

只有兩個posibilities (重構asside)

  1. 使用公共接口設置狀態。
  2. 如果您無法通過公共界面設置該狀態,則該狀態爲冗餘

選項2不言自明,很可能不適用於您的案例,因此您只需通過您的班級的公共界面設置州。

正如你已經提到的,這是可能的,但它需要很多代碼才能達到正確的狀態。這本身就可以表明你的類正在做的很多,現在是時候將你的類的一部分重構爲更小的可測試類。

Do not test private methods

如果你發現需要測試的私有方法,那麼你就 別的東西是錯誤的。可以說,有一個「上游」問題。你 已經到了這個問題,由於在這個過程中 之前的其他一些錯誤。嘗試隔離什麼,確切地說,並刪除它,而不是彎曲你的測試,在測試中遇到一個脆弱的道路。

Unit testing private members

我建議不進行單元測試私有方法。由於它們是私密的,它們可能會在每個發行版之間以任何可能的(或不可思議的)方式進行更改。如果給定的私有方法是非常關鍵 到你覺得它值得測試用例類的操作, 那麼它可能的時間來重構他們趕出保護或 公共方法

上一個常見的報價是

你不應該觸及陰部

+0

我試圖設置私有成員變量,而不是測試私有方法。此外,雖然國家可以通過公衆意見實現,但不能立即這樣做。我現在正在編寫一個漫遊圖的算法,並且它將在給定節點處的狀態取決於它用於到達的路徑和當前節點值。所以基本上輸入是圖形和起始節點,但就是這樣,而我想測試一些函數在某種可能的狀態下可以正常工作500個節點。 – Puchatek

+1

@Puchatek - 如果您需要跳過這些環節才能讓對象進入所需的狀態,這可能意味着您需要將當前對象重構爲較小的可測試對象。你當前的對象會注入這些較小的對象來完成它的工作。您可能需要閱讀依賴注入。我強烈推薦閱讀Misco Hevery的博客。 –

+0

我會的,謝謝你的提示。 – Puchatek

1

如果你的測試類叫做MyTestClass,然後在類Foo添加MyTestClass的朋友可以訪問我ts私有成員變量。

Class Foo { 
public: 
    int action(); 
private: 
    int state; 

    friend class MyTestClass; 
}; 
+0

我覺得在實際的代碼中不應該有任何測試代碼的痕跡。也許只是一個意見,但它不適合我。 – Puchatek

+0

有時候,你與良好的習慣衝突並完成工作,這是要走的路。 – jopa

1

你應該有一些public LY(或「protected立法院」)訪問機制來改變私有變量state的價值。爲了簡單起見,我們假設它是一種方法Foo::setState(int inState)。在單元測試中使用它來更改狀態,從而測試Foo::action()方法。這可確保將來的任何實施變更不會影響單元測試(除非「API」Foo::setState()發生變化 - 在這種情況下,當然,您必須更改單元測試)。

如果你有這樣一個機制,以改變state,這意味着最終用戶或調用代碼也無法改變它,因此,你不會需要對其進行測試(也許這使得state冗餘,但我不知道這一點)。

如果私有變量通過其他代碼「間接」更改,則必須在單元測試中執行相同的代碼。您始終可以追溯外部可見的任何方法,以便從外部輸入代碼。基本上重點在於,在單元測試中,您必須像在「真實」場景中一樣向代碼提供相同的輸入,然後測試它是否應該如此響應。如下面的評論所述,如果從輸入到正在測試的代碼的「路徑」也是,那麼代碼/測試可能不得不被分解爲更小的模塊或中間點。爲了詳細說明,考慮代碼流程爲:

Input -> A -> B -> C -> Output 
// A, B, C are intermediate execution points which are not "publicly accessible". 

在上述情況下,所有你能做的就是

鑑於Input,檢查是否Output是正確的。

相反,這將是很好,露出中間ABC,至少到單元測試,所以你現在可以把你考不上:

鑑於Input,檢查是否A是正確的。

鑑於A,請檢查B是否正確。

鑑於B,請檢查C是否正確。

鑑於C,請檢查Output是否正確。

正如你可以想象的那樣,如果測試失敗,找出失敗的原因變得更容易,從而修復它。

+0

好吧,如果從輸入到被單元測試的代碼的「路徑」是_too_ long,通常意味着代碼/測試需要被重構爲更小的模塊。或者,也可能是測試需要在「更高級別」應用。從你的描述來看,這聽起來像是你正在低於抽象的抽象層次進行測試。 –

+0

能夠在「中間」點應用測試總是一個好主意。如果你的代碼是這樣編寫的,以便在輸入和狀態之間改變,沒有任何地方可以應用任何測試,那麼你可能需要考慮重構。 –

+1

我個人的意見是,如果僅用於測試,它可以暴露中間點,如果它有助於提高測試質量(從而代碼)。預處理器可以幫助你。在'#ifdef UNIT_TEST ...#endif'中包含「僅測試」代碼,並在單元測試時用'-DUNIT_TEST'編譯。它還確保僅將測試代碼從「真實」可執行文件中排除。 –