Mockito — nó là gì, các khái niệm chính và nguyên lý hoạt động

Tác giả: IT Sectr Đã đăng: 2026-04-08 Thời gian đọc: 8 phút

Mockito là một framework mã nguồn mở để tạo các đối tượng mock trong kiểm thử đơn vị Java và Kotlin, cho phép cô lập mã được kiểm thử khỏi các phụ thuộc bên ngoài. Nhờ nó, nhà phát triển thay thế các kho lưu trữ thực, API client và cơ sở dữ liệu bằng các stub có kiểm soát với hành vi được xác định trước. Theo Mockito.org, thư viện được sử dụng trong hơn 60% các dự án Java có sử dụng kiểm thử đơn vị.

Các điểm chính

  • Mockito — một thư viện tạo các đối tượng mock thay thế các phụ thuộc thực trong kiểm thử.
  • Mock — một đối tượng stub mô phỏng hành vi của một thành phần thực.
  • Stubbing — cấu hình giá trị trả về khi gọi phương thức mock.
  • Verify — kiểm tra rằng một phương thức đã được gọi với các đối số cụ thể.
  • @InjectMocks — tự động tiêm các phụ thuộc mock vào đối tượng được kiểm thử.

Mockito là gì?

Mockito là một thư viện mã nguồn mở để tạo các đối tượng mock (stub) trong kiểm thử đơn vị cho Java, Kotlin và các ngôn ngữ JVM khác. Không giống JUnit, chịu trách nhiệm thực thi kiểm thử, Mockito giải quyết vấn đề cô lập — nó thay thế các phụ thuộc thực của lớp được kiểm thử bằng các đối tượng có thể dự đoán trước.

Không có mock, việc kiểm thử một phương thức truy cập cơ sở dữ liệu hoặc API bên ngoài đòi hỏi phải thiết lập môi trường thực — triển khai cơ sở dữ liệu, khởi động máy chủ. Mockito thay thế các phụ thuộc này bằng các đối tượng có hành vi cố định: phương thức repository.findById(1) luôn trả về một đối tượng User xác định mà không truy cập cơ sở dữ liệu.

Kiến trúc của Mockito dựa trên mẫu Proxy (cho các interface và lớp). Thư viện tạo một lớp con hoặc proxy cho kiểu được chỉ định và chặn tất cả các lời gọi phương thức, trả về các giá trị mặc định hoặc giá trị được đặt qua when().thenReturn().

Cách Mockito hoạt động

Nguyên lý hoạt động của Mockito dựa trên ba thao tác cơ bản: tạo mock, cấu hình hành vi (stubbing) và xác minh lời gọi (verification). Mỗi thao tác sử dụng các phương thức tĩnh từ lớp org.mockito.Mockito — lớp được tải xuống nhiều nhất trong hệ sinh thái Java theo thống kê của Maven Central. Tất cả các lời gọi phương thức mock được ghi lại trong bộ nhớ, cho phép xác minh sau đó qua verify.

Ba bước của kiểm thử với Mockito

Một kiểm thử điển hình với Mockito bao gồm ba pha: Arrange — tạo mock và cấu hình stub qua when().thenReturn(), Act — gọi phương thức được kiểm thử, Assert — kiểm tra kết quả qua assertEquals và verify(mock). Cách tiếp cận này được gọi là AAA (Arrange-Act-Assert).

Ví dụ cơ bản với mock repository

Hãy xem một kiểm thử đơn giản nơi Mockito thay thế repository người dùng. Phương thức when().thenReturn() cấu hình mock để lời gọi findById trả về một đối tượng User được chuẩn bị trước.

java
// Tạo mock repository
UserRepository mockRepo = mock(UserRepository.class);

// Cấu hình hành vi: findById(1) trả về người dùng
when(mockRepo.findById(1)).thenReturn(new User("Alice"));

// Kiểm tra rằng phương thức đã thực sự được gọi
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);

Tạo các đối tượng mock

Mockito cung cấp hai cách để tạo mock: phương thức tĩnh mock(Class) và annotation @Mock với khởi tạo qua MockitoAnnotations.openMocks(). Cách đầu tiên gọn nhẹ cho một hoặc hai mock, cách thứ hai thuận tiện khi có nhiều phụ thuộc — annotation làm giảm mã boilerplate.

Thông qua phương thức tĩnh mock()

Phương thức mock() nhận một lớp và trả về một đối tượng stub có thể được cấu hình qua when().thenReturn(). Tất cả các phương thức không được cấu hình trả về giá trị mặc định: 0 cho số, false cho boolean, null cho đối tượng.

java
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);

Thông qua annotation @Mock với JUnit 5

Annotation @Mock kết hợp với @ExtendWith(MockitoExtension.class) tự động tạo mock cho tất cả các trường của lớp kiểm thử. MockitoExtension chịu trách nhiệm khởi tạo trước mỗi kiểm thử.

java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void getUserShouldReturnUserFromRepo() {
        when(userRepository.findById(1)).thenReturn(new User("Alice"));
        User result = userService.getUser(1);
        assertEquals("Alice", result.getName());
    }
}

Stubbing: cấu hình hành vi mock

Stubbing là quá trình xác định những gì một phương thức mock nên trả về khi được gọi với các đối số cụ thể. Cú pháp cơ bản: when(mock.method(args)).thenReturn(value). Cho các kịch bản khác nhau, Mockito cung cấp một số biến thể của phương thức then.

Phương thứcMục đích
thenReturn(value)Luôn trả về giá trị đã chỉ định
thenThrow(exception)Ném ngoại lệ khi được gọi
thenAnswer(answer)Tính toán giá trị trả về một cách động
thenCallRealMethod()Gọi phương thức thực (mock một phần)

Phản hồi động qua thenAnswer

Khi giá trị trả về phụ thuộc vào đối số của lời gọi, thenAnswer được sử dụng với một lambda. Điều này hữu ích để mô phỏng làm việc với dữ liệu thực — ví dụ, tạo ID dựa trên đối tượng được truyền vào.

java
when(repository.save(any())).thenAnswer(invocation -> {
    User user = invocation.getArgument(0);
    user.setId(42);
    return user;
});

Verify: kiểm tra tương tác với mock

Verify là một tính năng độc đáo của Mockito mà các thư viện đối tượng mock cũ hơn (EasyMock, jMock) không cung cấp.

Verify làm cho kiểm thử đáng tin cậy hơn vì nó kiểm tra không chỉ giá trị trả về mà còn cả các tác dụng phụ — lời gọi đến các phương thức không trả về kết quả (phương thức void). Phương thức verify(mock).methodName(args) kiểm tra xem một phương thức mock cụ thể có được gọi với các đối số đã chỉ định hay không. Điều này cho phép kiểm tra không chỉ kết quả mà còn cả quá trình — thực tế truy cập vào phụ thuộc.

Kiểm tra số lượng lời gọi

Theo mặc định, verify kiểm tra rằng phương thức đã được gọi đúng một lần. Nếu cần số lượng khác, times(n), atLeast(n), never() và các bộ sửa đổi khác từ lớp Mockito được sử dụng.

java
// Kiểm tra số lượng lời gọi
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);

// Kiểm tra thứ tự lời gọi
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();

ArgumentCaptor để chụp đối số

Khi cần kiểm tra chính xác đối tượng nào đã được gọi cùng với một phương thức, ArgumentCaptor được sử dụng. Nó chụp giá trị đối số trong quá trình gọi và cho phép kiểm tra các trường của nó một cách riêng lẻ. ArgumentCaptor đặc biệt hữu ích khi mã được kiểm thử tạo một đối tượng bên trong và truyền nó cho một phụ thuộc — bạn không thể kiểm tra đối tượng đó bằng cách khác.

java
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());

Annotation @Mock và @InjectMocks

@Mock@InjectMocks là hai annotation chính của Mockito giúp giảm đáng kể mã boilerplate. @Mock tạo mock cho một trường, và @InjectMocks tiêm tất cả các mock từ lớp kiểm thử vào đối tượng được kiểm thử thông qua constructor, setter hoặc trường.

Cơ chế @InjectMocks cố gắng tiêm các phụ thuộc theo thứ tự sau: constructor có nhiều đối số nhất, setter theo kiểu, trường riêng tư. Nếu không có phương pháp nào hoạt động, đối tượng sẽ giữ các phụ thuộc null và kiểm thử sẽ thất bại với NullPointerException.

Quy tắc sử dụng @InjectMocks

Cần hiểu rằng: @InjectMocks không phân tích kiểu của trường — nó thay thế bất kỳ mock nào tương thích theo kiểu. Nếu một lớp có hai trường cùng kiểu, Mockito có thể tiêm nhầm mock. Trong những trường hợp như vậy, nên sử dụng constructor rõ ràng với các tham số mock.

Mockito trong các dự án Android

Trong phát triển Android, Mockito được sử dụng cùng với JUnit để kiểm thử ViewModel, Repository và UseCase. Vì các lớp này chạy trên JVM mà không có ngữ cảnh Android, Mockito thay thế các phụ thuộc của chúng — Room DAO, Retrofit API, SharedPreferences — bằng các stub có hành vi có thể dự đoán trước.

Thiết lập Gradle cho Mockito

Để thêm Mockito vào dự án Android, chỉ cần thêm phụ thuộc mockito-core hoặc mockito-inline (cái sau hỗ trợ mock các lớp final và phương thức tĩnh). Phiên bản 5.12.0 (2024) bao gồm hỗ trợ Java 21 và tích hợp JUnit 5 được cải thiện.

kotlin
// build.gradle.kts (module)
dependencies {
    testImplementation("org.mockito:mockito-core:5.12.0")
    testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}

Mockito và PowerMock: thực hành lỗi thời

Trước đây, việc mock các phương thức tĩnh và constructor yêu cầu PowerMock — một phần mở rộng hoạt động thông qua bytecode instrumentation. Bắt đầu từ Mockito 5.x với mockito-inline, khả năng này được tích hợp trực tiếp: mockStatic(ClassName.class) cho phép mock các phương thức tĩnh mà không cần thư viện bổ sung.

Kiểm thử ViewModel với Mockito

Một kịch bản điển hình: một ViewModel gọi một phương thức repository và biến đổi kết quả thành trạng thái UI. Mockito thay thế repository, và kiểm thử xác minh rằng ViewModel xử lý chính xác cả phản hồi thành công và lỗi. Khi sử dụng Clean Architecture, các mock được tạo cho từng lớp: DataSource, Repository và UseCase — điều này cho phép kiểm thử từng lớp một cách cô lập.

  • trường hợp thành công — when(repo.getData()).thenReturn(Result.success(data)) → kiểm tra state = Success(data).
  • trường hợp lỗi — when(repo.getData()).thenReturn(Result.error(exception)) → kiểm tra state = Error(message).
  • trạng thái tải — verify rằng ViewModel đã đặt isLoading = true trước khi gọi repository.

Câu hỏi thường gặp

Mockito khác MockK như thế nào?

Mockito là thư viện cho Java và Kotlin sử dụng proxy và phản xạ. MockK là thư viện Kotlin-first hỗ trợ coroutine, hàm mở rộng và lớp final mà không cần cấu hình thêm.

Làm thế nào để tạo mock cho lớp final trong Mockito?

Bắt đầu từ Mockito 2.1, việc mock các lớp final được hỗ trợ thông qua opt-in. Trong phiên bản 5.x (mockito-inline), tính năng này được bật theo mặc định. Chỉ cần thêm phụ thuộc mockito-inline và sử dụng phương thức mock() tiêu chuẩn.

Spy trong Mockito là gì?

Spy là một mock một phần, theo mặc định gọi các phương thức thực nhưng cho phép ghi đè một số trong số chúng qua when().thenReturn(). Spy hữu ích để kiểm thử mã legacy khi không thể viết lại toàn bộ lớp.

Sự khác biệt giữa thenReturn và thenAnswer là gì?

thenReturn luôn trả về cùng một giá trị, bất kể đối số. thenAnswer tính toán giá trị trả về dựa trên lời gọi — đối số, bản thân mock và trạng thái. Đối với phản hồi động, luôn sử dụng thenAnswer.

Tại sao verify quan trọng đối với kiểm thử có mock?

Verify kiểm tra không chỉ kết quả mà còn cả quá trình — thực tế truy cập vào phụ thuộc. Điều này rất quan trọng đối với các dịch vụ cần lưu dữ liệu hoặc gửi thông báo. Không có verify, kiểm thử sẽ không phát hiện rằng một phương thức đã không gọi save() hoặc send().

Tóm tắt

  • Mockito — thư viện tạo đối tượng mock, tiêu chuẩn thực tế cho việc mock trong Java và Kotlin.
  • Mock được tạo qua mock(Class) hoặc annotation @Mock với MockitoExtension.
  • Stubbing qua when().thenReturn() xác định hành vi của phương thức mock.
  • Verify kiểm tra thực tế và số lượng lời gọi phương thức mock với các đối số đã chỉ định.
  • @InjectMocks tự động tiêm các mock vào đối tượng được kiểm thử.
  • Tích hợp Android — Mockito được sử dụng để kiểm thử ViewModel, Repository và UseCase.
  • ArgumentCaptor chụp các đối số lời gọi để kiểm tra chi tiết các trường của đối tượng.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm