Mocking MessageDigest.getInstance() to throw an exception
Asked Answered
A

8

6

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?

Avila answered 18/5, 2011 at 23:11 Comment(0)
E
3

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.

Ewan answered 19/5, 2011 at 4:42 Comment(0)
G
1

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.

Goalkeeper answered 18/5, 2011 at 23:24 Comment(0)
W
1

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)

Waxwing answered 18/5, 2011 at 23:26 Comment(0)
C
1

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.

Chrissie answered 7/10, 2013 at 9:1 Comment(0)
S
0

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);
}
Spiroid answered 19/5, 2011 at 3:46 Comment(0)
G
0

In my case, our pipeline needs 100% coverage, so I did the following:

  • define a static inner class with only one static method to return the instance of MessageDigest
  • define the method just as @TomAnderson did: in catch clause, throw an AssertionError("unreachable", e) to indicate that it is definitely impossible to reach here
  • ignore this static class in jacoco.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

Georgiegeorgina answered 29/10, 2019 at 9:58 Comment(0)
B
0

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);
   }
}
Banc answered 19/11, 2021 at 9:37 Comment(0)
C
0

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));
    }

}
Cadena answered 9/5 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.