Tích hợp Mockito vào Junit 5 bằng ExtendWith

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách tích hợp Mockito và Junit 5 bằng cách sử dụng @ExtendWith annotation. Nhưng trước tiên chúng ta cần tìm hiểu một chút về Mockito là gì? và vì sao lại cần sử dụng đến nó.

Mockito là gì?

Mockito là một mock framework cho phép chúng ta giả lập các kết quả trả về khi triển khai unit-test. Giúp việc triển khai unit-test đơn giản và hiệu quả hơn. Sau đây chúng ta sẽ cùng tìm hiểu tại sao cần sử dụng Mockito khi triển khai unit-test.

Như các bạn đã biết unit-test là một loại kiểm thử phần mềm theo đơn vị nhỏ nhất như các method, class, module v.v để đảm bảo tính đúng đắn của từng đơn vị này. Giả sử chúng ta có một class như sau

public class UserService {

    private UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Map<String, List<User>> findAndGroupUserByName(String tx) {
        List<User> users = userRepository.findAll();

        List<User> filter = new ArrayList<>();
        for (User user: users) {
            if (user.getName().startsWith(tx)) {
                filter.add(user);
            }
        }

        return filter.stream().collect(Collectors.groupingBy(User::getName));
    }
}

Trong đoạn mã trên, hàm findAndGroupUserByName() dùng để lọc các user thỏa mãn điều kiện và nhóm chúng lại theo name. Ở đây chúng ta có thể thấy hàm findAndGroupUserByName() phụ thuộc vào kết quả trả về của userRepository. Khi viết unit-test cho hàm này mình không muốn kết quả kiểm thử phụ thuộc vào kết quả trả về userRepository, do đó mình sẽ dùng Mockito để giả lập kết quả trả về.

Đây là một trong số nhiều lý do để sử dụng Mockito, tiếp theo chúng ta sẽ đi tìm hiểu cách tích hợp Mockito vào Junit.

Maven dependencies

Required dependency

Để tích hợp Mockito với Junit 5 chúng ta cần thêm 2 dependency chính của chúng
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.3.1</version>
    <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-api</artifactId>
     <version>5.6.0</version>
     <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.21.0</version>
    <scope>test</scope>
</dependency>

Trong đó junit-jupiter-engine, junit-jupiter-api là các dependency chính của JUnit 5 và mockito-core là dependency của Mockito.

JUnit 4 Compatibility

Ngoài ra, trong trường hợp chúng ta đã có các unit-test trước đó được viết dựa trên phiên bản Junit 4 thì có thể thêm các dependency sau tương thích ngược khi cập nhật lên Junit 5.

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.6.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-runner</artifactId>
    <version>1.6.0</version>
    <scope>test</scope>
</dependency>

Mockito Extension

Cuối cùng để tích hợp với Junit 5, Mockito cung cấp gói mockito-junit-jupiter.

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

Sử dụng Mockito trong Junit 5

Để triển khai unit-test sử dụng Mockito chúng ta có thể khởi tạo một class theo cấu trúc như sau

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    // some code
}

Trong đó 

  • @ExtendWith(MockitoExtension.class) cho phép tích hợp mockito vào junit test
  • @TestInstance(TestInstance.Lifecycle.PER_CLASS) cho phép sử dụng @BeforeAll annotation trên instance method, thay vì static method

Trong mockito, chúng ta sử dụng @InjectMocks annotation để đánh dấu class mà chúng ta muốn kiểm thử. @Mock để đánh dấu những class mà chúng ta không quan tâm, và giá trị trả về của các method của class này sẽ được giả lập.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService = new UserService(userRepository);

Như vậy ở đây mình muốn kiểm thử UserService và giả lập kết quả trả về của UserRepository. Lưu ý rằng UserService đã được triển khai ở đầu bài viết. Sau đây là ví dụ hoàn chỉnh dùng để kiểm thử UserService

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService = new UserService(userRepository);

    @BeforeEach
    public void beforeEach() {
        List<User> users = new ArrayList<>();
        users.add(new User("1", "Hai"));
        users.add(new User("2", "Hieu"));
        users.add(new User("3", "oanh"));
        users.add(new User("4", "Hai"));
        Mockito.when(userRepository.findAll()).thenReturn(users);
    }

    @Test
    public void findAndGroupUserByNameTest() {
        Map<String, List<User>> map = userService.findAndGroupUserByName("H");
        Assertions.assertNotNull(map); // true
        Assertions.assertEquals(map.size(), 2); // true
        Assertions.assertEquals(map.get("Hai").size(), 2); // true
        Assertions.assertEquals(map.get("Hieu").size(), 1); // true
    }
}

@BeforeEach là annotation dùng để đánh dấu một method sẽ được chạy trước mỗi Test-Method, ở đây chúng ta dùng để giả lập kết quả trả về. Để giả lập kết quả trả về chúng ta cần hướng dẫn cho Mockito biết bằng cách sử dụng mệnh đề when-thenReturn.

Tóm lược

Qua bài viết này chúng ta đã tìm hiểu được cách tích hợp Mockito vào Junit 5. Qua quá trình phát triển thì giờ đây các thư viện này đã thông minh hơn rất nhiều giúp cho việc tích hợp dễ dàng và tồn ít mã hơn khi sử dụng những phiên bản trước.

Xem mã nguồn đầy đủ tại đây: integrate-mockito

Nguồn tham khảo

https://site.mockito.org/

https://www.baeldung.com/mockito-junit-5-extension

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