Reputation: 3494
I got the following method:
private MessageDigest getMessageDigest() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new Error(e);
}
}
To get 100% code coverage I need to get into the catch block. But I am absolutely not sure how I can do that. Is there some mocking framework that could help me in this case? If so - how? Or is there even another way without having to catch an exception?
Upvotes: 6
Views: 5039
Reputation: 1
I came across something similar and found this article very useful for mocking MessageDigest.getInstance().
https://www.baeldung.com/mockito-mock-static-methods
@Test
void mockStatic() throws ABCException {
String acc= "123";
String pid= "345";
String pmd= "5056";
String epi= "1923";
try (MockedStatic<MessageDigest> messageDigestMockedStatic = Mockito.mockStatic(MessageDigest.class)) {
messageDigestMockedStatic.when(() -> MessageDigest.getInstance(AppConstants.SHA_256)).thenThrow(NoSuchAlgorithmException.class);
assertThrows(ABCException.class, () -> absService.createToken(acc, pid, pmd, epi, false));
}
}
Upvotes: 0
Reputation: 1797
You can create a wrapper MessageDigest class:
@Component
public class MessageDigest {
public java.security.MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException {
return java.security.MessageDigest.getInstance(algorithm);
}
}
Upvotes: 0
Reputation: 12807
In my case, our pipeline needs 100% coverage, so I did the following:
MessageDigest
catch
clause, throw an AssertionError("unreachable", e)
to indicate that it is definitely impossible to reach herejacoco.gradle
for jacocoTestReport
and jacocoTestCoverageVerification
tasks. To know how to exclude inner class, check my other post: How to ignore inner static classes in Jacoco when using Gradle
(which links to another post of how to do it in Maven, in case you use it)I extract the method to a class, because Gradle does not have a consistent syntax to ignore members in a class. Check Filtering options of Jacoco and here
Upvotes: 0
Reputation: 1741
Just to have a follow-up on this question, it can be done with PowerMock.
As an extract, this is my working code:
@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());
}
}
The class "MyClass" will simply do something like:
public class MyClass {
public String myMethodCall() {
return new String(MessageDigest.getInstance("SHA1").digest("someString".getBytes()));
}
}
In an additional test, you could write
when(MessageDigest.getInstance("SHA1")).thenThrow(new NoSuchAlgorithmException());
instead of my mentioned return, to get to your catch block.
Do note, however, that using PowerMock has some drawbacks. It will generally use more memory and more instatiation time, so your test will run longer. For this specific test, it won't make a big difference, but just as a head's up.
Upvotes: 1
Reputation: 5259
The getInstance method on MessageDigest looks like a static method. Static methods cannot be mocked. I agree with ratchet that you should not aim for 100 % code coverage but focus on testing the areas with complex code instead.
Upvotes: 3
Reputation: 7651
Your exception is unreachable because that exception will never be thrown. I suppose it's logical with something like Mockito to do something akin to:
doThrow(new NoSuchAlgorithmException()).when(MessageDigest.getInstance("MD5")); // this is psuedo code
But it still doesn't make much sense. You are better off writing your code like:
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;
}
otherwise you'll need to modify your method to be testable:
public MessageDigest getMessageDigest(String digest) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(digest);
}
Upvotes: 0
Reputation: 48226
honestly in this case you don't need to cover that code it's non reachable boilerplate to ensure you don't have to worry about checked exceptions in the user code (most of the time 98% coverage is sufficient if you can explain why the 2 percent got missed)
Upvotes: 1
Reputation: 47243
I'd write this as:
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw (AssertionError)new AssertionError("unreachable").initCause(e);
}
And declare that because the catch block is unreachable, it doesn't need to be tested.
Upvotes: 1