2024/12/23

Mockito: mock static final method

以下記錄 Mockito 在處理 static method,以及 final class/method 的方法。

準備

先準備一個要被 mock 的類別

import java.util.UUID;

public class DataUtils {
    public static String getUuid() {
//        return UUID.randomUUID().toString();
        return "UUID";
    }

    public static String concat(String a, String b) {
        return a+":"+b;
    }

    public final int finalMethod() {
        return 1;
    }
    public String multiply(String a, int times) {
        return a.repeat( times );
    }
}

mock static method

  • 沒有參數的 static method

Mockito.mockStatic(Class classToMock)

注意 MockedStatic 的 scope 問題,必須要放在 try-with-resources 裡面

    @Test
    // mock 沒有參數的 static method 的方法
    public void mock_test1() {
        assertEquals("UUID", DataUtils.getUuid());

        // Mockito.mockStatic(Class<T> classToMock)
        // 注意 MockedStatic 的 scope 問題,必須要放在 try-with-resources 裡面
        // MockedStatic 必須要在 block 結束時,自動回收
        try (MockedStatic<DataUtils> utils = mockStatic(DataUtils.class)) {
            utils.when(DataUtils::getUuid).thenReturn("Custom UUID");
            assertEquals("Custom UUID", DataUtils.getUuid());
        }

        assertEquals("UUID", DataUtils.getUuid());
    }
  • mock 有參數的 static method
    @Test
    // mock 有參數的 static method
    public void mock_test2() {
        assertEquals("A:B", DataUtils.concat("A", "B"));

        // Mockito.mockStatic(Class<T> classToMock)
        try (MockedStatic<DataUtils> utils = mockStatic(DataUtils.class)) {
            utils.when(() -> DataUtils.concat("A", "B"))
                    .thenReturn( "A-B" );
            assertEquals("A-B", DataUtils.concat("A", "B"));
        }

        assertEquals("A:B", DataUtils.concat("A", "B"));
    }
  • MockitoException

static mocking is already registered in the current thread 的 exception

    @Test
    public void mock_test3() {
        // 將 utils 再一次 建立一個 mockStatic 物件時,會發生 exception
//        org.mockito.exceptions.base.MockitoException:
//        For mock.DataUtils, static mocking is already registered in the current thread
//        To create a new mock, the existing static mock registration must be deregistered
        assertThrows(MockitoException.class, () -> {
            MockedStatic<DataUtils> utils = mockStatic(DataUtils.class);
            utils = mockStatic(DataUtils.class);
        });
    }
  • JUnit @Before @After

利用 JUnit 的 @Before @After,在每一次執行 @Test 的前後,處理 MockedStatic

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mockStatic;

public class DataUtilsMockitoTest2 {
    private MockedStatic<DataUtils> mockStatic;

    // @Before 會在每一次 @Test 之前被執行
    @Before
    public void before() {
        // Registering a static mock for UserService before each test
        mockStatic = mockStatic(DataUtils.class);
    }

    // @After 會在每一次 @Test 之後被執行
    @After
    public void after() {
        // Closing the mockStatic after each test
        mockStatic.close();
    }

    @Test
    public void mock_test4_1() {
        assertTrue(Mockito.mockingDetails(DataUtils.class).isMock());
        mockStatic.when(() -> DataUtils.concat("A", "B"))
                .thenReturn( "A-B" );
        assertEquals("A-B", DataUtils.concat("A", "B"));
    }

    @Test
    public void mock_test4_2() {
        assertTrue(Mockito.mockingDetails(DataUtils.class).isMock());
        mockStatic.when(() -> DataUtils.concat("A", "B"))
                .thenReturn( "A*B" );
        assertEquals("A*B", DataUtils.concat("A", "B"));
    }
}

mock final class/method

先準備一個 final class

public final class DataUtilsFinal extends DataUtils {
    public static String concat(String a, String b) {
        return a+":"+b;
    }

    public String multiply(String a, int times) {
        return a.repeat( times-1 );
    }
}
  • final method
    @Test
    public void mock_test1() {
        // 直接用 mock 就可以使用 finalMethod
        DataUtils dataUtils = new DataUtils();
        DataUtils dataUtilsMock = mock(DataUtils.class);
        when(dataUtilsMock.finalMethod()).thenReturn(0);

        assertEquals(1, dataUtils.finalMethod());
        assertEquals(0, dataUtilsMock.finalMethod());
    }
  • final class
    @Test
    public void mock_test2() {
        // final class
        DataUtilsFinal mock = mock(DataUtilsFinal.class);
        when(mock.multiply("a", 2)).thenReturn("a");

        assertEquals("a", mock.multiply("a", 2));
    }

References

Mocking Static Methods With Mockito | Baeldung

Mock Final Classes and Methods with Mockito | Baeldung

沒有留言:

張貼留言