Mock Static method với Mockito

Khi viết unit-test đôi lúc chúng ta sẽ gặp phải các tình huống cần phải giả lập kết quả trả về (sau đây gọi là mock) của các static method. Để làm được điều này, có rất nhiều thư viện hỗ trợ như powermock, mockito. Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách mock static method với mockito.

Maven dependency

Để sử dụng tính năng mock static method chúng ta cần sử dụng mockito-inline dependency, lưu ý rằng dependency này đã tích hợp sẵn mockito-core mà chúng ta thường sử dụng.

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

Mock static method

Giả sử chúng ta có một class với các static method như sau. Hãy tưởng tượng đây là một class chứa các static method dùng để truy cập hoặc kiểm định thông tin của người dùng khi hệ thống đã xác thực thành công thông qua JWT Token.

public class SecurityContextHolder {

    public static String getUserId() {
        return "deft";
    }

    public static Date getExpirationCert(String cert) throws CertificateException {
        CertificateFactory certFact = CertificateFactory.getInstance("X.509");
        X509Certificate res = (X509Certificate) certFact
                .generateCertificate(new ByteArrayInputStream(cert.getBytes()));

        return res.getNotAfter();
    }
}

UserSerivce được triển khai như sau

public class UserService {

    public boolean canAccess() {
        String userId = SecurityContextHolder.getUserId();
        if (userId.equals("admin")) {
            return true;
        }
        return false;
    }

    public boolean isCertValid(String cert) throws CertificateException {
        Date expire = SecurityContextHolder.getExpirationCert(cert);
        if (expire.before(new Date())) {
            return false;
        }
        return true;
    }
}

Mock static method không có tham số đầu vào

Chúng ta sẽ bắt đầu với trường hợp đơn giản nhất là mock static method không chứa tham số đầu vào. Trong UserSerivce chứa hàm canAccess() không chứa tham số đầu vào.

Để test canAccess() chúng ta cần mock getUserId() để đảm bảo cover hết các trường hợp. Để mock static method chúng ta có thể làm như sau

public class UserSerivceTest {

    @Test
    public void canAccessTest() {
        try(MockedStatic<SecurityContextHolder> mockedStatic =
                    Mockito.mockStatic(SecurityContextHolder.class)) {
            mockedStatic.when(SecurityContextHolder::getUserId).thenReturn("admin");
            UserService userService = new UserService();
            boolean canAccess1 = userService.canAccess();
            assertEquals(true, canAccess1);

            mockedStatic.when(SecurityContextHolder::getUserId).thenReturn("user");
            boolean canAccess2 = userService.canAccess();
            assertEquals(false, canAccess2);

        }
    }
}

Tuy nhiên, cần lưu ý rằng các MockedStatic được tạo ra cần close() khi không còn sử dụng để tránh xảy ra các lỗi không đáng có. Ở ví dụ trên dùng try-with-resource cho phép tự close MockStatic object khi sử dụng xong.

Mock static method tham số đầu vào

Đối với các static method có tham số đầu vào chúng ta cần hướng dẫn cho nó biết rằng đối với giá trị của một tham số nhất định thì nên trả về kết quả gì. Ở phần này chúng ta sẽ test với hàm isCertValid(String cert).

@Test
public void getExpirationCertTest() throws CertificateException {
    try (MockedStatic<SecurityContextHolder> mockedStatic =
                 Mockito.mockStatic(SecurityContextHolder.class)) {

        String cert = "tmpCert";
        Date afterOneDay = new Date(System.currentTimeMillis() + 1000 * 60 * 60);
        mockedStatic.when(() -> SecurityContextHolder.getExpirationCert(cert)).thenReturn(afterOneDay);
        UserService userService = new UserService();
        boolean isCertValid = userService.isCertValid(cert);
        assertEquals(true, isCertValid);
    }
}

Ngoài ra chúng ta có thể mock kết quả trả về mà không cần quan tâm tham số đầu vào có là giá trị gì đi nữa với ArgumentMatchers class.

@Test
public void getExpirationCertAnyTest() throws CertificateException {
    try (MockedStatic<SecurityContextHolder> mockedStatic =
                 Mockito.mockStatic(SecurityContextHolder.class)) {

        Date afterOneDay = new Date(System.currentTimeMillis() + 1000 * 60 * 60);
        mockedStatic.when(() -> SecurityContextHolder.getExpirationCert(ArgumentMatchers.anyString()))
                .thenReturn(afterOneDay);
        UserService userService = new UserService();
        boolean isCertValid = userService.isCertValid("anyvalue");
        assertEquals(true, isCertValid);
    }
}

ArgumentMatchers hỗ trợ rất nhiều method:

  • anyString() – Khớp với bất kỳ String này
  • anyBoolean() – Khớp với bất kỳ giá trị bool nào
  • anyList() – Khớp với bất kỳ List nào

Và rất nhiều các method tương tự khác mà chúng ta có thể sử dụng trong các trường hợp khác nhau tùy thuộc vào tham số đầu vào là gì.

Tóm lược

Qua bài viết này chúng ta đã cùng nhau tìm hiểu cách mock static method sử dụng mockito. Hy vọng là sẽ giúp ích cho các bạn. Hãy nhớ close() các MockStatic sau khi sử dụng xong nhé.

Các bạn có thể tham khảo mã nguồn tại đây: mock-static

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x