Junit5 mock a static method
Asked Answered
P

6

40

I want to mock a static method in JUnit 5. But unfortunately, JUnit 5 doesn’t support Mockito. Is there another method to achieve the same other than reverting back to JUnit 4?

Ploughshare answered 16/10, 2018 at 7:50 Comment(1)
From Mockito 3.4, it is possible to do it out of the box. Please, check my answer and example.Mythology
T
15

The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.

With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:

A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.

Ticket answered 24/10, 2018 at 14:24 Comment(0)
M
57

From Mockito 3.4.0 (2020-07-10), it is possible to mock static methods out of the box even in JUnit 5, without any extension.

In the documentation, you can find an example: 48. Mocking static methods (since 3.4.0)

Important note: You need to use inline mock maker. So the dependency to use is not the core one:

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>3.4.6</version>
            <scope>test</scope>
        </dependency>

Example: Class under test:

package teststatics;

public class FooWithStatics {
    public static Long noParameters() {
        return System.currentTimeMillis();
    }
    public static String oneParameter(String param1) {
        return param1.toUpperCase();
    }
}

Test class:

package teststatics;

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

public class FooWithStaticsTest {

    @Test
    void testStatic() {
        // Before mock scope, usual behavior.
        assertNotEquals(0L, FooWithStatics.noParameters());
        assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));

        // Mock scope
        try (MockedStatic mocked = mockStatic(FooWithStatics.class)) {

            // Mocking
            mocked.when(FooWithStatics::noParameters).thenReturn(0L);
            mocked.when(() -> FooWithStatics.oneParameter("xxx")).thenReturn("yyy");

            // Mocked behavior
            assertEquals(0L, FooWithStatics.noParameters());
            assertEquals("yyy", FooWithStatics.oneParameter("xxx"));

            // Verifying mocks.
            mocked.verify(times(1), FooWithStatics::noParameters);
            mocked.verify(times(1), () -> FooWithStatics.oneParameter("xxx"));
        }

        // After mock scope returns to usual behavior.
        assertNotEquals(0L, FooWithStatics.noParameters());
        assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
    }
}
Mythology answered 4/8, 2020 at 8:6 Comment(6)
See also the answer to Why doesn't Mockito mock static methods? which links to the announcing blog post with a simple 3-steps example.Ferrand
@Mythology Using mockedStatic how can I mock a static void method? I can't use the above instruction for a method that has void as a return type.Conal
@Mythology very helpful answer. I was struggling for a while and had to use jmockit to mock static methods, but this really simplified lot of my junit5 tests. Thank you :)Calcariferous
Just to add, please ensure the try block is covered entirely else, mocking does not happen.Columella
what if your static methods are void?Benford
Upvoted as this was useful, however I should comment I am using mockito-inline:4.8.1, with this api, mocked.verify(times(1), () -> FooWithStatics.oneParameter("xxx")), is no longer valid, instead it must be mocked.verify(() -> FooWithStatics.oneParameter("xxx"), times(1))Eudosia
T
15

The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.

With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:

A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.

Ticket answered 24/10, 2018 at 14:24 Comment(0)
U
10

The reason why Mockito doesn't provide static methods mocking at the moment is because of the common belief that static method shouldn't need to be mocked.

However, there is an open item for Mockito here that discusses the issue.

While this doesn't answer your question, in general it tells you why you shouldn't need the feature at all or will allow you to join the conversation with your ideas.

Upturned answered 16/10, 2018 at 17:52 Comment(5)
A common scenario where this is needed is if we need to mock calls to System.getenv which I don't see it as a bad pattern necessarily. Please correct me if I am wrongDecane
@Decane the scenario you mentioned should be handled by the UT framework, in fact it's possible to address it using Rules in JUnit4 or Extensions in JUnit5Upturned
It's not supported out of the box, that's true, but there are smart ways to do it with extensions.Upturned
could you please link me to any reference that talks about that? We are considering dowgrading to Junit4 just because this feature is missing..Decane
You don't need to downgrade. Worst case you can still using @rule called by an extension. I'll put together an example for you.Upturned
M
5
  1. Make sure to have mockito-inline dependency in your POM file

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-inline</artifactId>
        <version>3.6.28</version>
        <scope>test</scope>
    </dependency>
    
  2. In my case I had to test scenario where exception thrown static method encode() of URLEncoder Class, so for that

    try (MockedStatic theMock  = mockStatic(URLEncoder.class)) {
        theMock.when(() -> URLEncoder.encode("Test/11", StandardCharsets.UTF_8.toString()))
        .thenThrow(UnsupportedEncodingException.class);
        when(restClient.retrieveByName("Test%2F11")).thenReturn(null);
        Assertions.assertThrows(ResponseStatusException.class, ()->service.retrieveByName("Test/11"));
    }
    
Monkey answered 2/7, 2021 at 5:14 Comment(0)
S
3

Mocking of Static Methods

  1. Add the latest mockito-inline dependency from here:
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>
  1. Utility class with a static method to illustrate:
class FilesUtility{
    public static boolean something(String x , String y){
        return true;
    }
}
  1. Test case on Mocked Static method:
@Test
void testStaticMethod() {
    try (MockedStatic<? extends FilesUtility> mocked = 
       mockStatic(FilesUtility.class, CALLS_REAL_METHODS)) {

    mocked
        when(() -> FilesUtility.something(any(), any()))
        .thenReturn(false);
    }
}

IMPORTANT

The default behaviour is to do nothing, but it's also possible to provide an Answer when creating the mock by explicitly specifying the CALLS_REAL_METHODS.

mockStatic(FilesUtility.class, CALLS_REAL_METHODS)

IMPORTANT

So, if your mocked static method internally calls any other method in that mocked class, you don't have to mock their behaviour as well - you can keep their behaviour default by specifying CALLS_REAL_METHODS argument.

It is equivalent to Spying on that class.


OPTIONAL If you face any issue with Spring, exclude the mockito-core dependency from the spring-boot-starter-test as it is included in the mockito-inline.

Scrapbook answered 1/6, 2023 at 14:1 Comment(1)
CALLS_REAL_METHODS was the winner on this answer.Kincardine
H
0

We can mock a static method by JMockit.

JMockit is used for mocking the external dependencies outside the test boundary, similar to Mockito and other such mocking libraries. The most important feature of JMockit is that it lets us mock anything, even the things that are hard to mock with other libraries such as constructors, static and final methods. It even allows mocking the member fields and initialization blocks as well.

Follow the below steps to enable JMockit:

  1. The JMockit artifact is located in the central Maven repository, add the JMockit dependency in pom.xml
    <!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
    <dependency>
        <groupId>org.jmockit</groupId>
        <artifactId>jmockit</artifactId>
        <version>1.49</version>
        <scope>test</scope>
    </dependency>
  1. Mock the Class method in TestClass:

     public class TestClass{
     @Test
     public void testMethod() {
         new MockUp<ClassName>(){
             @Mock
             //mock  the method here
         };
     }
    

    }

Follow the tutorial to know more about how to use the JMockit.

Humber answered 14/1, 2023 at 4:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.