- Sản phẩm & Dịch vụSản phẩm & Dịch vụ
- Giải phápGiải pháp
- Bảng giáBảng giá
- Công tyCông ty
- Tài liệuTài liệu
vi
vi
Chuyên mục với các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.
Technical Writer
I have over 5 years of experience writing technical documentation for tech products, making them accessible and user-friendly. My focus is always on providing clear and precise information. @#@ Tôi đã có hơn 5 năm kinh nghiệm viết tài liệu kỹ thuật cho các sản phẩm công nghệ, giúp người dùng dễ dàng tiếp cận và sử dụng. Tôi luôn tập trung vào việc cung cấp thông tin chính xác và dễ hiểu.
Thảo luận (0)
Đăng nhập để thảo luận
Trong bài viết hôm nay, chúng ta sẽ học cách mock dependency injection object trong unit test bằng @InjectMocks – một trong những annotation mạnh mẽ và tiện lợi nhất của Mockito.

@InjectMocks giúp chúng ta “tiêm” (inject) các dependency đã được mock vào đối tượng của lớp cần test một cách tự động như thế nào, qua đó đơn giản hóa đáng kể quá trình thiết lập test và giảm thiểu boilerplate code. Chúng ta cũng sẽ tìm hiểu về các cơ chế injection khác nhau mà Mockito sử dụng và cách áp dụng chúng trong thực tế.
Annotation @InjectMocks của Mockito cho phép chúng ta tự động tiêm (inject) các dependency đã được mock (hoặc spy) vào một đối tượng của lớp được đánh dấu bằng chính @InjectMocks. Điều này cực kỳ hữu ích khi lớp mà chúng ta muốn kiểm thử (System Under Test – SUT) có các dependency bên ngoài cần được cô lập hoặc kiểm soát hành vi trong quá trình test. Các đối tượng mock được tiêm vào sẽ được khai báo bằng @Mock hoặc @Spy.
Mockito sẽ cố gắng tiêm các dependency đã được mock theo ba cách tiếp cận, với một thứ tự ưu tiên nhất định:
setEmailService(EmailService emailService)).Quy tắc tìm mock:
private EmailService primaryEmailService; và bạn có @Mock EmailService primaryEmailService;, Mockito sẽ cố gắng khớp theo tên.Để thấy rõ cách Mockito thực hiện việc tiêm dependency của các mock, chúng ta hãy tạo một số service và các lớp ứng dụng có dependency.
Đầu tiên là một interface Service và hai lớp triển khai đơn giản:
package com.journaldev.injectmocksservices;public interface Service {
public boolean send(String msg);}
package com.journaldev.injectmocksservices;public class EmailService implements Service {
@Override public boolean send(String msg) { System.out.println("Sending email"); return true; }}
package com.journaldev.injectmocksservices;public class SMSService implements Service {
@Override public boolean send(String msg) { System.out.println("Sending SMS"); return true; }}
Tiếp theo, chúng ta sẽ tạo ba lớp AppServices khác nhau, mỗi lớp đại diện cho một cách Mockito có thể tiêm dependency:
AppServices: Sử dụng Constructor Based Injection.package com.journaldev.injectmocksservices;// Dành cho Constructor Based @InjectMocks injection public class AppServices {
private EmailService emailService; private SMSService smsService; public AppServices(EmailService emailService, SMSService smsService) { this.emailService = emailService; this.smsService = smsService; } public boolean sendSMS(String msg) { return smsService.send(msg); } public boolean sendEmail(String msg) { return emailService.send(msg); }}
AppServices1: Sử dụng Property Setter Based Injection.package com.journaldev.injectmocksservices;// Dành cho Property Setter Based @InjectMocks injection public class AppServices1 {
private EmailService emailService; private SMSService smsService; public void setEmailService(EmailService emailService) { this.emailService = emailService; } public void setSmsService(SMSService smsService) { this.smsService = smsService; } public boolean sendSMS(String msg) { return smsService.send(msg); } public boolean sendEmail(String msg) { return emailService.send(msg); }}
AppServices2: Sử dụng Field Based Injection.package com.journaldev.injectmocksservices;// Dành cho Field Based @InjectMocks injection public class AppServices2 {
private EmailService emailService; private SMSService smsService; public boolean sendSMS(String msg) { return smsService.send(msg); } public boolean sendEmail(String msg) { return emailService.send(msg); }}
Giờ là lúc chúng ta viết các test case để xem @InjectMocks hoạt động như thế nào với từng kiểu injection. Lưu ý rằng lớp test của tôi là MockitoInjectMocksExamples đang extend BaseTestCase. Điều này nhằm mục đích khởi tạo các mock của Mockito trước khi các test chạy. Đây là mã của lớp BaseTestCase:
package com.journaldev.mockito.injectmocks;import org.junit.jupiter.api.BeforeEach; import org.mockito.MockitoAnnotations;
class BaseTestCase {
@BeforeEach void init_mocks() { MockitoAnnotations.initMocks(this); }}
Rất quan trọng: Nếu bạn không gọi MockitoAnnotations.initMocks(this); (hoặc sử dụng @RunWith(MockitoJUnitRunner.class) nếu dùng JUnit 4, hoặc @ExtendWith(MockitoExtension.class) nếu dùng JUnit 5), bạn sẽ gặp lỗi NullPointerException vì các đối tượng mock sẽ không được khởi tạo.
Và đây là lớp test chính của chúng ta:
MockitoInjectMocksExamples
package com.journaldev.mockito.injectmocks;import static org.junit.jupiter.api.Assertions.; import static org.mockito.Mockito.;
import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock;
import com.journaldev.injectmocksservices.AppServices; import com.journaldev.injectmocksservices.AppServices1; import com.journaldev.injectmocksservices.AppServices2; import com.journaldev.injectmocksservices.EmailService; import com.journaldev.injectmocksservices.SMSService;
class MockitoInjectMocksExamples extends BaseTestCase {
@Mock EmailService emailService; @Mock SMSService smsService; @InjectMocks AppServices appServicesConstructorInjectionMock; @InjectMocks AppServices1 appServicesSetterInjectionMock; @InjectMocks AppServices2 appServicesFieldInjectionMock; @Test void test_constructor_injection_mock() { // Thiết lập hành vi cho emailService và smsService đã được inject vào appServicesConstructorInjectionMock when(appServicesConstructorInjectionMock.sendEmail("Email")).thenReturn(true); when(appServicesConstructorInjectionMock.sendSMS(anyString())).thenReturn(true); // Kiểm tra các trường hợp đã được stub assertTrue(appServicesConstructorInjectionMock.sendEmail("Email")); // Kiểm tra trường hợp chưa được stub, mặc định Mockito trả về false cho boolean assertFalse(appServicesConstructorInjectionMock.sendEmail("Unstubbed Email")); assertTrue(appServicesConstructorInjectionMock.sendSMS("SMS")); }}
Tiếp theo là test case cho phương thức tiêm qua setter. Test này nằm trong cùng lớp MockitoInjectMocksExamples.
@Test
void test_setter_injection_mock() {
// Thiết lập hành vi cho emailService và smsService đã được inject vào appServicesSetterInjectionMock
when(appServicesSetterInjectionMock.sendEmail("New Email")).thenReturn(true);
when(appServicesSetterInjectionMock.sendSMS(anyString())).thenReturn(true);
// Kiểm tra các trường hợp đã được stub
assertTrue(appServicesSetterInjectionMock.sendEmail("New Email"));
assertFalse(appServicesSetterInjectionMock.sendEmail("Unstubbed Email"));
assertTrue(appServicesSetterInjectionMock.sendSMS("SMS"));
}
Và cuối cùng là test case cho phương thức tiêm trực tiếp vào trường (field). Test này cũng nằm trong cùng lớp MockitoInjectMocksExamples.
@Test
void test_field_injection_mock() {
// Thiết lập hành vi cho emailService và smsService đã được inject vào appServicesFieldInjectionMock
when(appServicesFieldInjectionMock.sendEmail(anyString())).thenReturn(true);
when(appServicesFieldInjectionMock.sendSMS(anyString())).thenReturn(true);
// Với anyString(), mọi chuỗi đều được trả về true
assertTrue(appServicesFieldInjectionMock.sendEmail("Email"));
assertTrue(appServicesFieldInjectionMock.sendEmail("New Email"));
assertTrue(appServicesFieldInjectionMock.sendSMS("SMS"));
}
Bạn có thể thấy, dù ba lớp AppServices có cấu trúc khác nhau để nhận dependency, Mockito với @InjectMocks vẫn “hiểu” và tiêm các mock tương ứng vào một cách mượt mà. Điều này giúp chúng ta tập trung vào việc định nghĩa hành vi của các mock mà không phải lo lắng về việc khởi tạo và nối kết chúng với lớp cần test.
Qua bài viết này, chúng ta đã cùng nhau khám phá sâu hơn về annotation @InjectMocks của Mockito – một công cụ vô cùng mạnh mẽ giúp đơn giản hóa quá trình viết unit test trong Java. Chúng ta đã hiểu rõ cách @InjectMocks tự động tiêm các dependency đã được mock thông qua ba cơ chế ưu tiên: Constructor, Setter, và Field.
Hãy áp dụng kiến thức này vào các dự án thực tế của bạn để xây dựng các bộ unit test mạnh mẽ, hiệu quả hơn. Việc làm chủ Mockito và các kỹ thuật mocking sẽ nâng cao đáng kể chất lượng code và quy trình phát triển phần mềm của bạn.