【问题标题】:Mocking MessageDigest.getInstance() to throw an exception模拟 MessageDigest.getInstance() 以引发异常
【发布时间】:2011-05-18 23:11:08
【问题描述】:
我得到了以下方法:
private MessageDigest getMessageDigest() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new Error(e);
}
}
要获得 100% 的代码覆盖率,我需要进入 catch 块。但我绝对不确定我该怎么做。在这种情况下,是否有一些模拟框架可以帮助我?如果是这样 - 如何?或者有没有其他方法无需捕获异常?
【问题讨论】:
标签:
java
unit-testing
mocking
【解决方案1】:
MessageDigest 上的 getInstance 方法看起来像一个静态方法。不能模拟静态方法。我同意 ratchet 的观点,你不应该以 100% 的代码覆盖率为目标,而应该专注于测试复杂代码的领域。
【解决方案2】:
我会这样写:
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw (AssertionError)new AssertionError("unreachable").initCause(e);
}
并声明因为catch块不可达,所以不需要测试。
【解决方案3】:
老实说,在这种情况下,您不需要覆盖无法访问的样板代码,以确保您不必担心用户代码中的已检查异常(如果您能解释的话,大多数情况下 98% 的覆盖率就足够了为什么这 2% 被错过了)
【解决方案4】:
只是为了跟进这个问题,可以通过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 有一些缺点。它通常会使用更多的内存和更多的实例化时间,因此您的测试会运行更长时间。对于这个特定的测试,它不会有很大的不同,但只是作为一个抬头。
【解决方案5】:
您的异常无法访问,因为该异常永远不会被抛出。我想用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);
}
【解决方案7】:
您可以创建一个包装器 MessageDigest 类:
@Component
public class MessageDigest {
public java.security.MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException {
return java.security.MessageDigest.getInstance(algorithm);
}
}