2

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.

1 Answer 1

6


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();
    }
}
3
  • 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
    Commented May 22, 2022 at 16:54
  • It is OK, see stackoverflow.com/questions/65965396/… Only one thing you must do not forget to close mock in @AfterEach
    – Eugene
    Commented May 22, 2022 at 18:31
  • I have updated the answer and added an example.
    – Eugene
    Commented May 22, 2022 at 18:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.