Mục lục
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
<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