2009-01-13 85 views
80

JUnit只會測試我們課程中公開的那些方法。我如何對那些不是(即私人,受保護)的人進行junit測試?junit和java:測試非公開方法

我可以通過不使用junit來測試它們,但我想知道junit標準方法是什麼。

回答

0

尋找「PrivateAccessor.invoke」。我的代碼從「junitx.util」導入它,但我不知道它來自哪裏。

8

最簡單的解決方案是將JUnit測試放在同一個包(但是不同的目錄)中,併爲方法使用默認(即包私有)可見性。

另一個更復雜的方法是使用反射來訪問私有方法。

+1

這是一個非常實用的解決方案,如果您沒有奢侈的代碼基礎,每個班級都遵守SRP和所有其他設計指南。 – 2009-01-13 21:49:20

76

一個關於單元測試的學派認爲,你應該只能測試公共方法,因爲你應該只是單元測試你的公共API,並且通過這樣做,你應該覆蓋你的非代碼 - 公共方法。你的旅費可能會改變;我發現有時候是這樣,有時候並非如此。

隨着中說,有一對夫婦的方式來測試非公共方法:

  • 可以測試保護和包範圍的方法把你的單元測試在同一個包,因爲他們「類重新測試。這是一個相當普遍的做法。
  • 您可以通過創建受測試類的子類來測試另一個包中的單元測試的受保護方法,該類將覆蓋您想要測試爲公共的方法,並讓這些重寫的方法使用super關鍵字調用原始方法。通常,這個「測試子類」將是JUnit TestCase類中的內部類,用於測試。在我看來,這樣做有點冒失,但我已經做到了。

希望這會有所幫助。

+8

實際上,如果被測試的類位於src/box/of/Cats.java中,那麼在測試項目中將測試類放到src/box/of/CatsTest.java並鏈接項目時,實際上不需要保護子類。這樣你的測試類似乎在同一個包中,因此可以訪問彼此的受保護方法。 – Esko 2009-01-14 07:22:57

+5

爲了訪問受保護的成員,提出了子類化的方法以符合(通常)被接受的將測試和源代碼放在*不同*包中的做法。這使得指定在構建自動化工具的最終分發中排除單元測試變得很容易。 – Dinuk 2009-06-29 23:03:18

+15

@Dinuk:請注意,不要將測試和源代碼放在不同的包中,您也可以將它們放到不同的源代碼樹中。這是如何Maven執行它(src/main/java和src/test/java)。這樣,實現類和測試類可以在同一個包中,但可以輕鬆地分別處理構建/部署。 – sleske 2012-07-30 13:36:52

35

與許多單元測試問題一樣,測試私有方法實際上是一個變相的設計問題。與其試圖做任何棘手的事情來測試私有方法,當我發現自己希望爲私有方法編寫測試時,我花一分鐘時間問自己:「我該如何設計這個,才能通過公共方法徹底測試它?」

如果這不起作用,JUnitX允許測試私有方法,儘管我相信它僅適用於JUnit 3.8。

9

如果您在相對較少的「公共」入口點下埋藏了大量邏輯,則很可能違反了Single Responsibility Principle。如果可能的話,你會想要將代碼重構成多個類,最終導致更多的「公共」方法來測試。

3

我幾乎總是在我的Java項目中使用Spring,因此我的對象是爲依賴注入而構建的。它們往往是在應用程序上下文中組合的相當細緻的公共接口的實現。因此,我很少(如果有的話)需要測試私有方法,因爲這個類本身足夠小以至於它不是問題。

即使我不使用Spring,我傾向於採用將小對象和簡單對象組裝成更大和更大的抽象的相同做法,每種抽象都相對簡單,但由聚合對象變得複雜。

以我的經驗,需要單元測試私有方法是一個指示器,可以(而且應該)簡化您正在測試的內容。

那是,如果你還真覺得有必要:

  • 受保護的方法可以由子類進行測試;
  • 可以通過將單元測試放在同一個包中來測試包私有方法;和
  • 私有方法可以通過提供例如包私有工廠代理方法進行單元測試。不理想,但私人確實意味着私人。
3

您通常不會測試私有方法,因爲它們只能(通常)通過其他公共方法間接進行測試。當您正在測試駕駛並製作私有方法時,他們通常是「提取方法」重構的結果,並且已經通過間接測試。

如果您擔心測試具有大量邏輯的私有方法,那麼您可以做的最聰明的事情是將該代碼移入另一個公共方法的類中。一旦你這樣做了,使用這個代碼的以前的方法可以通過具有由存根或模擬提供的功能來簡化測試。

24

當你編寫JUnit測試時,你必須做一個微妙的思維轉變:「我現在是我自己班的客戶。」這意味着私人是私人的,你只能測試客戶看到的行爲。

如果方法真的應該是私人的,我會認爲這是一個設計缺陷,只是爲了測試而使其可見。您必須能夠根據客戶的看法來推斷其正確的操作。

自從我最初寫這篇文章以來的三年中,我已經開始用稍微不同的方式來使用Java反射來解決問題。

骯髒的小祕密是,你可以用JUnit測試私有方法,就像使用反射一樣公開的方法。您可以測試自己的內容,並且不會將其公開給客戶。

0

與協議幾乎每一個崗位 - 你或許應該重構,可能無法測試除了通過公私,只是想補充一個不同的方式來思考它...

認爲你的類本身作爲一個「單位」,而不是一個方法。您正在測試這個類,並且它可以維護一個有效的狀態,而不管它是如何調用公共方法的。

調用私有方法可能會破壞封裝並實際上使測試無效。

3

這不是真的發佈爲答案,更多的是我遇到了同樣的問題,而且「如果它需要是私人的,它可能應該被重構」並不適合我。

假設您有某種功能需要以類內部的某種方式分離出來。例如,假設我有這樣的事情:

public class HolderOfSomeStrings{ 

    private List<String> internal_values; 

    public List<String> get() 
    { 
     List<String> out = new ArrayList<String>(); 
     for (String s:internal_values) 
     { 
      out.add(process(s)); 
     } 
     return get; 
    } 

    private static String process(String input) 
    { 
    //do something complicated here that other classes shouldn't be interested in 
    } 

} 

的這裏的一點是,JUnit的強迫我做過程中的公衆,或者至少保護,或者把它放在它自己的實用工具類。但是,如果它是HolderOfSomeStrings的某種內部邏輯,那麼我認爲這是不正確的 - 在我看來,這應該是私人的,並且使它在某種程度上更加可視化了代碼。

5

這裏是「可能不應該這樣做」的方法,其他人都在不停地對你施加壓力。不過,我認爲這當然有可能的原因在於這樣做。以下代碼將訪問專用字段,但私有方法的代碼幾乎相同。

public void testPrivateField() throws InterruptedException { 
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class; 
    try { 
     Field privateField = clazz.getDeclaredField("nameOfPrivateField"); 
     privateField.setAccessible(true); // This is the line 
     // do stuff 
    } catch(NoSuchFieldException nsfe) { 
     nsfe.printStackTrace(); 
     fail(); 
    } catch(IllegalAccessException iae) { 
     iae.printStackTrace(); 
     fail(); 
    } 

} 
2

從安迪·亨特借用一個想法,甚至是你的私有方法必須有一定的副作用,就是你感興趣的,換句話說,它們必須從一些公共方法被調用和執行導致了一個有趣的任務你的對象的狀態改變。測試該狀態的變化。

假設您有公共方法pubMethod和私有方法privMethod。當你調用pubMethod時,它又調用privMethod來執行任務(可能解析一個字符串)。 pubMethod然後使用這個解析的字符串以某種方式設置成員變量的值或影響它自己的返回值。通過觀察對pubMethod的返回值或成員變量(可能通過使用訪問器獲取它們)的期望效果進行測試。

1

您可以使用TestNG代替JUnit,它不關心私有或公共方法。

0

作爲這種分支的一種,我不確定每個人在整個「多語種編程」問題上的下落,但Groovy測試可以在Junit中運行,並忽略整個公共/非公共問題。值得注意的是,它被正式分類爲「bug」,但是當他們試圖修復它時,就會有這樣的風暴,它會像原來一樣被放回原處。

1

使用上面的反射來測試私有方法。 如果我們遵循TDD,我們應該測試私有方法,因爲TDD意味着稍後不會有任何意外。 所以人們不應該等待完成他的公開方法來測試私人。 這有助於在重新分解時進行更精細的迴歸測試。

2

DP4j罐

爲了測試私有方法,我們需要使用反射及其所有答案尖。

現在好了,這個任務在Dp4j jar的幫助下被簡化了。

  • Dp4j會分析您的代碼並自動爲您生成Reflection API代碼。

  • 只需將dp4j.jar添加到您的CLASSPATH即可。

  • Dp4j.jar包含註釋處理程序,它們將查找代碼中使用@Test JUnit註釋進行註釋的方法。

  • Dp4j分析這些方法的代碼,如果它發現您非法訪問私有方法,它將用您使用Java的Reflection API的等效代碼替換您的無效私有方法引用。

獲取更多細節here

0

我絕對@duffymo代碼應該從客戶的觀點進行測試(雖然他說他不幹這樣想的)同意。但是,從這個角度來看,私人與其他人有不同的含義。私有方法的客戶端本身就是類,所以我更喜歡通過外部(公共/包保護)API測試它們。然而,受保護的和受軟件包保護的成員在那裏是爲了外部客戶,因此我使用繼承擁有類或駐留在相同軟件包中的假貨測試它們。