2009-01-08 56 views
8

許多開發人員認爲測試私有方法是一個壞主意。然而,我發現的所有例子都基於私有方法是私有的想法,因爲調用它們可能會破壞內部對象的狀態。但這不僅是隱藏方法的理由。單元測試私有方法:門面模式

讓我們考慮Facade模式。我的班級用戶需要2種公共方法。他們會太大。在我的例子中,他們需要從數據庫的BLOB中加載一些複雜的結構,解析它,填充一些臨時COM對象,運行用戶宏來驗證和修改這些對象,並將修改後的對象序列化爲XML。單個metod的相當大的功能:-)這兩個公共方法都需要這些操作。所以,我創建了大約10個私有方法,並且有2個公有方法調用它們。其實,我的私人方法不一定是私人的,他們不會破壞實例的內部狀態。但是,當我不慣於測試私有方法,我有以下問題:

  1. 發佈它們意味着用戶的複雜性(他們有他們不需要選擇)
  2. 我無法想象TDD風格對於這樣一個大型的公共方法,當你要編寫500多行代碼只是爲了返回一些東西(甚至不是真正的結果)。
  3. 從數據庫中檢索這些方法的數據,並且測試數據庫相關的功能要困難得多。

當我測試的私有方法:

  1. 我不發表會混淆用戶的詳細信息。公共接口包括2種方法。
  2. 我可以工作在TDD風格(一步一步寫小方法)。
  3. 我可以使用測試數據覆蓋大部分類的功能,無需連接數據庫。

可能有人形容,我究竟做錯了什麼?我應該使用什麼樣的設計來獲得相同的獎金,而不要測試私人方法?

更新:在我看來,我已經提取了一切,我能夠到另一個類。所以,我無法想象我可以提取什麼。從數據庫加載由ORM層執行,解析流,序列化爲XML,運行宏 - 一切都由獨立類完成。這個類包含相當複雜的數據結構,搜索和轉換的例程,並且需要所有提到的實用程序。所以,我認爲別的東西是不能提取的;否則,它的責任(關於數據結構的知識)將在類之間分配。

所以,最好的方法來解決我看到現在分成兩個對象(門面本身和實物,與私有方法成爲大衆)和移動真實物體的某個地方,沒有人會嘗試找到它。在我的情況下(Delphi)它將是一個獨立的單元,在其他語言中它可能是一個單獨的名稱空間。其他類似的選項是2接口,感謝您的想法。

+1

它看起來像已經有一個相同的問題,這一個:http://stackoverflow.com/questions/250692/how-do-you- unit-test-private-methods – 2009-01-08 20:22:28

+0

不是它的笨蛋 – krosenvold 2009-01-08 20:39:17

回答

17

我認爲你把太多的責任(實現)放在門面。我通常會認爲這是其他類中實際實現的前端。

因此,在你的門面私有方法很可能是在一個或多個其他類的公共方法。然後你可以在那裏測試它們。

3

私有方法用於封裝具有您正試圖測試類外沒有任何意義的一些行爲。您不應該測試私有方法,因爲只有同一類的公共或受保護方法纔會調用私有方法。

這可能只是因爲你的班級非常複雜,並且需要花費大量精力來測試它。但是,我建議你尋找抽象概念,你可以將它們分解到自己的課堂中。這些類將具有較小的項目範圍和測試複雜性。

2

我不熟悉您的要求&設計,但似乎您的設計是程序性的而不是面向對象的。即你有2個公共方法和許多私人方法。如果你將課堂分解爲每個對象都有其角色的對象,那麼測試每個「小」類將會更容易。另外,你可以設置「helpers」對象的訪問級別爲package(默認在Java中,我知道在C#中有類似的訪問級別),這樣你不會在API中公開它們,但你可以單獨測試它們他們是單位)。

4

難道有人會說,我的 做錯了什麼?

也許什麼都沒有?

如果我想測試一個方法,我使它成爲默認(包)範圍並測試它。

你已經提到了另一個很好的解決方案:用你的兩種方法創建一個接口。您的客戶訪問這兩種方法,其他方法的可見性無關緊要。

2

也許如果你需要時間,並從Miško看 Clean Code Tech talks。他非常瞭解如何編寫代碼以便進行測試。

2

這是一個有點爭議的話題......大多數TDD持有人認爲,重構你的方法更簡單的單元測試實際上使你的設計更好。我認爲這通常是正確的,但公共API的私有方法的具體情況絕對是一個例外。所以,是的,你應該測試私人方法,不,你不應該公開。

如果你在Java中工作,這是我寫的,這將有助於你在一個類測試靜態私有方法的實用程序方法:

import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import junit.framework.Assert; 

public static Object invokeStaticPrivateMethod(Class<?> clazz, String methodName, Object... params) { 
    Assert.assertNotNull(clazz); 
    Assert.assertNotNull(methodName); 
    Assert.assertNotNull(params); 

    // find requested method 
    final Method methods[] = clazz.getDeclaredMethods(); 
    for (int i = 0; i < methods.length; ++i) { 
     if (methodName.equals(methods[i].getName())) { 
      try { 
       // this line makes testing private methods possible :) 
       methods[i].setAccessible(true); 
       return methods[i].invoke(clazz, params); 
      } catch (IllegalArgumentException ex) { 
       // maybe method is overloaded - try finding another method with the same name 
       continue; 
      } catch (IllegalAccessException ex) { 
       Assert.fail("IllegalAccessException accessing method '" + methodName + "'"); 
      } catch (InvocationTargetException ex) { 
       // this makes finding out where test failed a bit easier by 
       // purging unnecessary stack trace 
       if (ex.getCause() instanceof RuntimeException) { 
        throw (RuntimeException) ex.getCause(); 
       } else { 
        throw new InvocationException(ex.getCause()); 
       } 
      } 
     } 
    } 

    Assert.fail("method '" + methodName + "' not found"); 
    return null; 
} 

這很可能對非靜態方法被改寫爲好,但這些討厭的私人方法通常是私人的,所以我從不需要這些。 :)

2

假設你有8個私人方法和2個公共方法。如果你可以獨立執行一個私有方法,即而不調用任何其他方法,並且沒有狀態損壞副作用,那麼單元測試就是這個方法是有意義的。但在那種情況下,不需要該方法是私人的!

在C#我會做保護的,而不是私人這樣的方法,並且將它們公開爲公衆在子類中進行測試

給你的情況下,它可能會更有意義的檢驗的方法進行公開,並讓用戶有一個真正的外觀只有他們需要的兩種公共方法,他們的界面