模拟MessageDigest.getInstance()以抛出异常

时间:2011-05-18 23:11:09

标签: java unit-testing mocking

我得到了以下方法:

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

要获得100%的代码覆盖率,我需要进入catch块。但我完全不确定如何做到这一点。在这种情况下,是否有一些可以帮助我的模拟框架?如果是这样 - 怎么样?或者是否有另一种方法而不必捕捉异常?

6 个答案:

答案 0 :(得分:2)

MessageDigest上的getInstance方法看起来像一个静态方法。静态方法不能被模拟。我同意棘手,你不应该以100%的代码覆盖为目标,而是专注于测试复杂代码的区域。

答案 1 :(得分:1)

老实说,在这种情况下,你不需要覆盖那些代码,它是不可访问的样板,以确保你不必担心用户代码中的已检查异常(大多数情况下,如果你能解释,98%的覆盖率是足够的为什么2%的人错过了)

答案 2 :(得分:1)

只需对此问题进行跟进,就可以使用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有一些缺点。它通常会使用更多的内存和更多的实例时间,因此您的测试运行时间会更长。对于这个特定的测试,它不会产生很大的不同,但只是一个头脑。

答案 3 :(得分:0)

我写这个:

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

并声明因为catch块无法访问,所以不需要对其进行测试。

答案 4 :(得分:0)

您的异常无法访问,因为永远不会抛出该异常。我认为像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);
}

答案 5 :(得分:0)

就我而言,我们的管道需要100%的覆盖率,因此我执行了以下操作:

  • 仅用一个静态方法定义一个静态内部类以返回MessageDigest的实例
  • 定义@@ TomAnderson一样的方法:在catch子句中,抛出AssertionError("unreachable", e)表示绝对不可能到达此处
  • 对于jacoco.gradlejacocoTestReport任务,在jacocoTestCoverageVerification中忽略此静态类。要知道如何排除内部类,请查看我的另一篇文章:How to ignore inner static classes in Jacoco when using Gradle (如果您使用它,它会链接到另一篇有关如何在Maven中进行操作的文章)

我将方法提取到类中,因为Gradle没有一致的语法来忽略类中的成员。选中Filtering options of Jacocohere