1

I don't want to use powermock anymore. Because junit5 started mocking static classes. So i am trying to get rid of powermock methods.

While i was using PowerMock, i could easily spy a class that has a private constructor, and then i was calling the static methods.

This is a part of my code ( When i was using PowerMock )

@RunWith(PowerMockRunner.class)
@PrepareForTest(MessageValidationUtils.class)
public class MessageValidationServiceTest {

    @Mock
    private CheckpointCustomerService checkpointCustomerService;


    @Mock
    private ProductClientService productClientService;


    @Before
    public void setUp() {
        MockitoAnnotations.openMocks(this);
        PowerMockito.spy(MessageValidationUtils.class);
}

After i make a spy object of MessageValidationUtils.class, i was testing this:

when(MessageValidationUtils.validateTelegramKeyMap(messageProcessDto.getMessageMessageType(),
        messageProcessDto.getMessageKeyValueMap())).thenAnswer((Answer<Boolean>) invocation -> true);

After some research i couldn't find anything related to spy a class that has a private constructor and static methods.

Emre Varol
  • 196
  • 2
  • 6
  • 18

1 Answers1

2


During mockStatic definition in Mockito you can specify setting to perform real execution by default Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS). In this way, your static mock will work like Spy.
Let's create simple Utils class for testing.

public class Utils {
    public static String method1() {
        return "Original mehtod1() value";
    }

    public static String method2() {
        return "Original mehtod2() value";
    }

    public static String method3() {
        return method2();
    }
}

Make mock for method2 and perform real execution of method1 and method3

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

public class SpyStaticTest {
    @Test
    public void spy_static_test() {
        try (MockedStatic<Utils> utilities = Mockito.mockStatic(Utils.class, Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS))) {
            utilities.when(Utils::method2).thenReturn("static mock");

            Assertions.assertEquals(Utils.method1(), "Original mehtod1() value");
            Assertions.assertEquals(Utils.method2(), "static mock");
            Assertions.assertEquals(Utils.method3(), "static mock");
        }
    }
}

Example for your class:

    @Test
    public void test() {
        try (MockedStatic<MessageValidationUtils> utilities = Mockito.mockStatic(MessageValidationUtils.class, Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS))) {
            utilities.when(() -> MessageValidationUtils.validateTelegramKeyMap(messageProcessDto.getMessageMessageType(),
                    messageProcessDto.getMessageKeyValueMap())).thenAnswer((Answer<Boolean>) invocation -> true);
            
            //perform testing of your service which uses MessageValidationUtils
        }
    }

UPDATE:
Example to use mockStatic in @BeforeEach

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

public class SpyStaticTest {
    MockedStatic<Utils> utilities;
    
    @BeforeEach
    public void setUp() {
        utilities = Mockito.mockStatic(Utils.class, Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
    }

    @Test
    public void spy_static_test1() {
        utilities.when(Utils::method2).thenReturn("static mock");

        Assertions.assertEquals(Utils.method1(), "Original mehtod1() value");
        Assertions.assertEquals(Utils.method2(), "static mock");
        Assertions.assertEquals(Utils.method3(), "static mock");

    }

    @Test
    public void spy_static_test2() {
        utilities.when(Utils::method1).thenReturn("static mock");

        Assertions.assertEquals(Utils.method1(), "static mock");
        Assertions.assertEquals(Utils.method2(), "Original mehtod2() value");
        Assertions.assertEquals(Utils.method3(), "Original mehtod2() value");

    }

    @AfterEach
    public void  afterTest() {
        utilities.close();
    }
}

UPDATE:
Mockito does not provide method for static Spy creation, but you can define own utils and implemet spy static definition there.

import org.mockito.MockedStatic;
import org.mockito.Mockito;

public class MockitoUtils {
    public static <T> MockedStatic<T> spyStatic(Class<T> classToMock) {
        return Mockito.mockStatic(classToMock, Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
    }
}

In this way your tests will look more clear:

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static com.test.MockitoUtils.spyStatic;

public class SpyStaticTest {
    MockedStatic<Utils> utilsSpy;

    @BeforeEach
    public void setUp() {
        utilsSpy = spyStatic(Utils.class);
    }

    @Test
    public void spy_static_test1() {
        utilsSpy.when(Utils::method2).thenReturn("static mock");

        Assertions.assertEquals(Utils.method1(), "Original mehtod1() value");
        Assertions.assertEquals(Utils.method2(), "static mock");
        Assertions.assertEquals(Utils.method3(), "static mock");

    }

    @Test
    public void spy_static_test2() {
        utilsSpy.when(Utils::method1).thenReturn("static mock");

        Assertions.assertEquals(Utils.method1(), "static mock");
        Assertions.assertEquals(Utils.method2(), "Original mehtod2() value");
        Assertions.assertEquals(Utils.method3(), "Original mehtod2() value");

    }

    @AfterEach
    public void  afterTest() {
        utilsSpy.close();
    }
}
Eugene
  • 5,269
  • 2
  • 14
  • 22
  • By the way, i have a 10 test method, is it a good approach to create a mockStatic object in each test method? I tried to do this in @BeforeEach, but it says : " The static mock created at "....." is already resolved and cannot longer be used" – Emre Varol May 22 '22 at 16:54
  • It is OK, see https://stackoverflow.com/questions/65965396/mockito-3-6-using-mockstatic-in-before-or-beforeclass-with-junit4 Only one thing you must do not forget to close mock in @AfterEach – Eugene May 22 '22 at 18:31
  • I have updated the answer and added an example. – Eugene May 22 '22 at 18:36