簡體   English   中英

模擬 MessageDigest.getInstance() 拋出異常

[英]Mocking MessageDigest.getInstance() to throw an exception

我得到了以下方法:

private MessageDigest getMessageDigest() {
    try {
        return MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        throw new Error(e);
    }
}

為了獲得 100% 的代碼覆蓋率,我需要進入 catch 塊。 但我絕對不確定我該怎么做。 在這種情況下,是否有一些模擬框架可以幫助我? 如果是這樣 - 如何? 或者還有另一種方法而不必捕獲異常嗎?

MessageDigest 上的 getInstance 方法看起來像一個靜態方法。 靜態方法不能被模擬。 我同意棘輪的觀點,即您不應該以 100% 的代碼覆蓋率為目標,而應專注於測試具有復雜代碼的區域。

只是要對這個問題進行跟進,可以使用PowerMock來完成。

作為摘錄,這是我的工作代碼:

@RunWith(PowerMockRunner.class)
@PrepareForTest({MyClass.class, MessageDigest.class})
public class MyClassTest {

    private MyClass myClass = new MyClass();
    @Mock private MessageDigest messageDigestMock;

    @Test
    public void shouldDoMethodCall() throws Exception {
        setupMessageDigest();

        String value = myClass.myMethodCall();

        // I use FestAssert here, you can use any framework you like, but you get
        // the general idea
        Assertions.assertThat(value).isEqualToIgnoringCase("hashed_value");
    }

    public void setupMessageDigest() throws Exception {
        PowerMockito.mockStatic(MessageDigest.class);
        when(MessageDigest.getInstance("SHA1")).thenReturn(messageDigestMock);
        when(messageDigestMock.digest(Matchers.<byte[]>anyObject())).thenReturn("hashed_value".getBytes());
    }

}

“MyClass”類將簡單地執行以下操作:

public class MyClass {

    public String myMethodCall() {

        return new String(MessageDigest.getInstance("SHA1").digest("someString".getBytes()));

    }        

}

在附加測試中,您可以編寫

when(MessageDigest.getInstance("SHA1")).thenThrow(new NoSuchAlgorithmException());

而不是我提到的返回,到達你的 catch 塊。

但是請注意,使用 PowerMock 有一些缺點。 它通常會使用更多的內存和更多的實例化時間,因此您的測試將運行更長時間。 對於這個特定的測試,它不會有太大的不同,但只是一個問題。

我會這樣寫:

try {
    return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
    throw (AssertionError)new AssertionError("unreachable").initCause(e);
}

並聲明因為catch塊不可達,所以不需要測試。

老實說,在這種情況下,您不需要覆蓋該代碼,它是不可訪問的樣板,以確保您不必擔心用戶代碼中的已檢查異常(如果您能解釋為什么 2錯過了百分比)

就我而言,我們的管道需要 100% 的覆蓋率,因此我執行了以下操作:

  • 定義一個只有一個靜態方法的靜態內部類來返回MessageDigest的實例
  • 像@TomAnderson 那樣定義方法:在catch子句中,拋出一個AssertionError("unreachable", e)來表明絕對不可能到達這里
  • 對於jacocoTestReportjacocoTestCoverageVerification任務,忽略jacoco.gradle這個靜態類。 要了解如何排除內部類,請查看我的另一篇文章: 使用 Gradle 時如何忽略 Jacoco 中的內部靜態類(鏈接到另一篇關於如何在 Maven 中執行此操作的文章,以防您使用它)

我將方法提取到一個類中,因為 Gradle 沒有一致的語法來忽略類中的成員。 檢查Jacoco這里的過濾選項

您的異常無法訪問,因為永遠不會拋出該異常。 想像 Mockito這樣的東西做一些類似於以下的事情是合乎邏輯的:

doThrow(new NoSuchAlgorithmException()).when(MessageDigest.getInstance("MD5")); // this is psuedo code

但這仍然沒有多大意義。 您最好編寫如下代碼:

private static final MessageDigest MD5_DIGEST;
static {
   try {
      MD5_DIGEST = MessageDigest.getInstance("MD5");
   ///CLOVER:OFF
   } catch (Exception e) {
      // can't happen since MD5 is a known digest
   }
   ///CLOVER:ON
}

public MessageDigest getMessageDigest() {
   return MD5_DIGEST;
}

否則你需要修改你的方法以進行測試:

public MessageDigest getMessageDigest(String digest) throws NoSuchAlgorithmException {
   return MessageDigest.getInstance(digest);
}

您可以創建一個包裝 MessageDigest 類:

@Component
public class MessageDigest {
   public java.security.MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException {
    return java.security.MessageDigest.getInstance(algorithm);
   }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM