[英]JUnit: testing helper class with only static methods
我正在使用 JUnit4 和 Cobertura 測試一個只有靜態方法的助手類。 測試方法很容易,並且已經完成。
但是,cobertura 表明該類沒有完全被測試覆蓋,因為它沒有在任何地方實例化。
我不想創建這個類的實例(它是一個輔助類),所以第一個解決方案是隱藏構造函數(這通常是輔助類的好方法)。
然后 cobertura 抱怨空的私有構造函數沒有被測試覆蓋。
對於這種情況,是否有任何解決方案可以實現 100% 的代碼覆蓋率?
頂層管理需要代碼覆蓋率(在這種情況下),所以對我來說,為這個特定的類獲得 100% 的覆蓋率是非常有幫助的。
有幾種解決方案:
您可以添加一個公共構造函數並從測試中調用它。 雖然它沒有意義,但它也沒有傷害(很多)。
創建一個虛擬靜態實例(您可以在此處調用私有構造函數)。 丑陋,但您可以為該字段命名來傳達您的意圖( JUST_TO_SILENCE_COBERTURA
是一個好名字)。
您可以讓您的測試擴展助手類。 這將本質上調用默認構造函數,但您的幫助類不能再是final
的。
我建議使用最后一種方法,尤其是因為該類不再是final
的。 如果您的代碼的使用者想要添加另一個輔助方法,他們現在可以擴展現有類並接收一個句柄來獲取所有輔助方法。 這會創建傳達意圖的輔助方法的耦合(它們屬於一起) - 如果輔助類是final
的,這是不可能的
如果您想防止用戶意外實例化輔助類,請將其abstract
化,而不是使用隱藏的構造函數。
如果您絕對需要實現 100% 的代碼覆蓋率 - 其優點可以在其他地方進行辯論 :) - 您可以在測試中使用反射來實現它。 作為習慣,當我實現一個僅靜態的實用程序類時,我添加了一個私有構造函數以確保無法創建該類的實例。 例如:
/**
* Constructs a new MyUtilities.
* @throws InstantiationException
*/
private MyUtilities() throws InstantiationException
{
throw new InstantiationException("Instances of this type are forbidden.");
}
那么你的測試可能看起來像這樣:
@Test
public void Test_Constructor_Throws_Exception() throws IllegalAccessException, InstantiationException {
final Class<?> cls = MyUtilties.class;
final Constructor<?> c = cls.getDeclaredConstructors()[0];
c.setAccessible(true);
Throwable targetException = null;
try {
c.newInstance((Object[])null);
} catch (InvocationTargetException ite) {
targetException = ite.getTargetException();
}
assertNotNull(targetException);
assertEquals(targetException.getClass(), InstantiationException.class);
}
基本上,您在這里所做的是按名稱獲取類,查找該類類型的構造函數,將其設置為 public( setAccessible
調用),不帶參數調用構造函數,然后確保拋出的目標異常是一個InstantiationException
。
無論如何,正如您所說,這里的 100% 代碼覆蓋率要求有點痛苦,但聽起來它超出了您的控制范圍,因此您無能為力。 我實際上在自己的代碼中使用了與上述類似的方法,我確實發現它是有益的,但不是從測試的角度來看。 相反,它只是幫助我比以前了解更多關於反射的知識:)
在所有情況下獲得 100% 的覆蓋率很好,但在某些情況下這是不可能的。 當然,如果您有一個從未實例化的類,Cobertura 會將其視為不完整的測試覆蓋,因為這些代碼行實際上在類中,但它們沒有經過測試。
事實是您永遠不會調用私有構造函數(我假設您已經通過將構造函數設為私有來隱藏構造函數),所以我不會打擾。 測試應該是關於得到你所期望的,雖然我同意 100% 的覆蓋率很好,但在某些情況下(像這樣)這沒有用。
看看100% 代碼覆蓋率。
不。
除非您顯式調用私有構造函數(這將是糟糕的代碼),否則您將無法覆蓋這些行。
在我的案例中, lombok @UtilityClass可以將代碼覆蓋率提高到 100%。
您可以跳過 100% 的覆蓋范圍。 它根本不應該傷害。 但是,如果您正在開發一個非常嚴格的產品,目標是 100% 的覆蓋率,那么您可以使用以下策略:
在您的情況下,缺少的覆蓋是構造函數:您應該測試構造函數的預期行為。
如果不定義構造函數,則允許實例化類(通過默認構造函數)。 您應該測試默認構造函數的行為:在測試中,調用構造函數並測試結果,例如沒有拋出錯誤,或者返回一個有效的實例。
相反,如果作為實用程序類實踐,您還將構造函數定義為private
並在構造函數中拋出UnsupportedOperationException
。 在測試中,您可以斷言該行為。 :
public class HelperClassTest {
@Test(expected = IllegalAccessException.class)
public void ctorShouldBePrivate() throws InstantiationException, IllegalAccessException {
HelperClass.class.newInstance();
}
@Test
public void whenCtorIsCalledThroughReflectionUnsupportedOperationExceptionShouldBeThrown() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Constructor<HelperClass> constructor = HelperClass.class.getDeclaredConstructor();
constructor.setAccessible(true);
try {
constructor.newInstance();
fail("Exception is expected");
} catch (InvocationTargetException e) {
assertThat(e.getCause(), is(instanceOf(UnsupportedOperationException.class)));
}
}
...
}
public class HelperClass{
private HelperClass() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
...
}
在 junit 5 (jupiter) 上,當您使用靜態方法向類添加私有構造函數時,覆蓋率為 100%
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.