2012-12-29 18 views
39

如果一個類,包含了一些靜態方法,以確保沒有人錯誤地初始化這個類的一個實例,我做了一個私人的構造函數:如何在Java應用程序中測試私有構造函數?

private Utils() { 
} 

現在..怎麼會這樣進行測試,鑑於構造函數不能被看到?這可以根據測試覆蓋嗎?

+1

使用Reflection API – MrSmith42

+0

這可能會怎樣?你有一個例子嗎? – JAM

+2

你沒有。您使用私有構造函數測試該方法。 –

回答

62

使用反射,你可以調用私有構造:

Constructor<Util> c = Utils.class.getDeclaredConstructor(); 
c.setAccessible(true); 
Utils u = c.newInstance(); // Hello sailor 

但是,你甚至可以使這不可能的:

private Utils() { 
    throw new UnsupportedOperationException(); 
} 

通過構造函數中拋出異常,你會阻止所有的嘗試。


我會讓類本身final過,只是「因爲」:

public final class Utils { 
    private Utils() { 
     throw new UnsupportedOperationException(); 
    } 
} 
+7

+1。關於例外的優秀點! – JAM

+1

不錯。另外,如果添加了異常,實際上也可能有爲此進行單元測試的原因。 – msandiford

+3

我發現UnsupportedOperationException比IllegalStateException更合適。 –

6
@Test 
public// 
void privateConstructorTest() throws Exception { 
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors(); 
    // check that all constructors are 'private': 
    for (final Constructor<?> constructor : constructors) { 
     Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers())); 
    }   
    // call the private constructor: 
    constructors[0].setAccessible(true); 
    constructors[0].newInstance((Object[]) null); 
} 
+0

我用它將我的工具類的測試覆蓋率提高到100% – MrSmith42

+1

這非常酷!謝謝 – JAM

+2

@ MrSmith42將測試覆蓋率接近100%不是一件好事。測試覆蓋率是一個指標。任何旨在改進度量而不是實現實際結果的行爲都會降低度量標準。畢竟你可以讓所有的代碼覆蓋測試,這些測試可以運行所有的方法,但不會驗證結果。所以,只有在你有充足的理由這樣做時,直接調用它才能測試私有構造函數。 (如果可能,最好間接測試 - 通過調用工廠方法或類似方法)。 無論如何,知道如何做到這一點,如果需要的話是好的:-) –

19

測試代碼的意圖..總是:)

例如:如果該點構造函數是不可見的,那麼你需要測試的是這個事實,沒有別的。

使用反射API查詢構造函數並驗證它們是否具有私有屬性集。

我會做這樣的事情:

@Test() 
public void testPrivateConstructors() { 
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors(); 
    for (Constructor<?> constructor : constructors) { 
     assertTrue(Modifier.isPrivate(constructor.getModifiers())); 
    } 
} 

如果你想擁有的對象構造一個適當的測試,你應該測試的公共API,它可以讓你獲得的構造的對象。這就是所說的API應該存在的原因:正確構建對象,所以你應該測試它:)。

+0

好主意。代碼覆蓋率太差的工具不知道你測試過它。 –

+0

請參閱https://github.com/trajano/commons-testing以瞭解執行此操作的庫以及更多用於測試實用程序類的庫。 –

4

如果您有一個私有構造函數,它會從您的代碼的某種非私有方法調用。所以你測試了這個方法,並且你的構造函數被覆蓋了。按照每種方法進行測試沒有宗教美德。您正在尋找函數或更好的分支覆蓋級別,只需通過使用它的代碼路徑來執行構造函數即可獲得。

如果該代碼路徑錯綜複雜且難以測試,則可能需要重構它。

5

,以確保沒有一個錯誤初始化此類

通常我做什麼,是對方法/構造從私有更改爲默認包可見的一個實例。我爲測試類使用相同的包,所以從測試中可以訪問方法/構造函數,即使它不是來自外部。

要執行政策,不能實例化類,你可以:

  1. 拋拋出UnsupportedOperationException從默認的空構造函數(「不實例化這個類!」)。
  2. 聲明類的抽象:如果它只包含靜態方法,你可以調用靜態方法但不實例化它,除非你繼承它。

或者同時應用1 + 2,如果您的測試與目標類共享相同的包,則仍然可以繼承並運行構造函數。 這應該是相當「錯誤證明」;惡意程序員總是會找到一個解決辦法:)

3

如果您在構造函數中添加例外,如:

private Utils() { 
    throw new UnsupportedOperationException(); 
} 

constructor.newInstance()在測試類的調用將拋出一個InvocationTargetException,而不是你UnsupportedOperationException,但所需的異常將包含在拋出的一箇中。
如果您想要斷言您的異常,則可以在調用異常被捕獲後拋出調用異常的目標。
例如,使用JUnit 4,你可以這樣做:

@Test(expected = UnsupportedOperationException.class) 
public void utilityClassTest() throws NoSuchMethodException, IllegalAccessException, InstantiationException { 
    final Constructor<Utils> constructor = Utils.class.getDeclaredConstructor(); 
    constructor.setAccessible(true); 
    try { 
     constructor.newInstance(); 
    } catch (InvocationTargetException e) { 
     throw (UnsupportedOperationException) e.getTargetException(); 
    } 
} 
-1

不要。構造函數是私有的。這就是你需要的。 Java強制其隱私。

不要測試平臺。

+0

測試不可訪問的代碼的實際用例是實現100%的測試覆蓋率,以便無人再次查看該類。如果覆蓋率保持在95%,許多開發人員可能會試圖找出導致這個問題反覆出現的原因。 另外,Java在強制隱私方面很糟糕。 :) – thisismydesign