Mockito یک فریمورک متنباز برای ایجاد اشیاء mock در تستهای واحد Java و Kotlin است که به شما امکان میدهد کد تستشونده را از وابستگیهای خارجی جدا کنید. با کمک آن، توسعهدهنده مخازن واقعی، کلاینتهای API و پایگاههای داده را با اشیاء جعلی کنترلشده با رفتار مشخص جایگزین میکند. به گزارش Mockito.org، این کتابخانه در بیش از 60% پروژههای Java که تست واحد انجام میدهند استفاده میشود.
نکات اصلی
Mockito یک کتابخانه متنباز برای ایجاد اشیاء mock (جعلی) در تستهای واحد در Java، Kotlin و دیگر زبانهای JVM است. برخلاف JUnit که مسئول اجرای تستهاست، Mockito مشکل جداسازی را حل میکند — وابستگیهای واقعی کلاس تحت تست را با اشیاء قابل پیشبینی جایگزین میکند.
بدون mockها، تست کردن متدی که به پایگاه داده یا API خارجی دسترسی دارد نیاز به راهاندازی محیط واقعی — استقرار DB، راهاندازی سرور — دارد. Mockito این وابستگیها را با اشیاء با رفتار ثابت جایگزین میکند: متد repository.findById(1) همیشه یک شیء User مشخص را بدون مراجعه به پایگاه داده برمیگرداند.
معماری Mockito بر اساس الگوی Proxy (برای اینترفیسها و کلاسها) است. کتابخانه یک زیرکلاس یا پراکسی برای نوع مشخص شده تولید میکند و تمام فراخوانیهای متد را رهگیری میکند و مقادیر پیشفرض یا مقادیر تعیینشده از طریق when().thenReturn() را برمیگرداند.
اصل کار Mockito بر سه عملیات پایه استوار است: ایجاد mock، تنظیم رفتار (stubbing) و بررسی فراخوانیها (verification). هر عملیات از متدهای استاتیک کلاس org.mockito.Mockito استفاده میکند — پراستفادهترین کلاس در اکوسیستم Java طبق آمار Maven Central. تمام فراخوانیهای متدهای mock در حافظه ثبت میشوند که امکان بررسی بعدی آنها را از طریق verify فراهم میکند.
یک تست معمولی با Mockito از سه فاز تشکیل شده است: Arrange — ایجاد mockها و تنظیم stubها از طریق when().thenReturn()، Act — فراخوانی متد تحت تست، Assert — بررسی نتیجه از طریق assertEquals و verify(mock). این رویکرد AAA (Arrange-Act-Assert) نامیده میشود.
یک تست ساده را در نظر بگیرید که در آن Mockito مخزن کاربران را جایگزین میکند. متد when().thenReturn() mock را طوری تنظیم میکند که فراخوانی findById یک شیء User از پیش آمادهشده را برگرداند.
// یک mock از مخزن ایجاد میکنیم
UserRepository mockRepo = mock(UserRepository.class);
// رفتار را تنظیم میکنیم: در findById(1) کاربر را برگردان
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// بررسی میکنیم که متد واقعاً فراخوانی شده است
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockito دو روش برای ایجاد mock ارائه میدهد: متد استاتیک mock(Class) و annotation @Mock با مقداردهی اولیه از طریق MockitoAnnotations.openMocks(). روش اول برای یک یا دو mock جمعوجور است، روش دوم زمانی که وابستگیها زیاد است راحتتر است — annotations کد تکراری را کاهش میدهند.
متد mock() یک کلاس دریافت میکند و یک شیء جعلی برمیگرداند که میتواند از طریق when().thenReturn() تنظیم شود. تمام متدهای تنظیمنشده مقادیر پیشفرض برمیگردانند: 0 برای اعداد، false برای boolean، null برای اشیاء.
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
annotation @Mock به همراه @ExtendWith(MockitoExtension.class) به طور خودکار برای تمام فیلدهای کلاس تست mock ایجاد میکند. افزونه MockitoExtension مسئول مقداردهی اولیه قبل از هر تست است.
@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 — فرآیند تعیین اینکه متد mock در هنگام فراخوانی با آرگومانهای مشخص چه چیزی را برگرداند. نحو پایه: when(mock.method(args)).thenReturn(value). برای سناریوهای مختلف، Mockito چندین نوع متد then ارائه میدهد.
| متد | هدف |
|---|---|
| thenReturn(value) | همیشه مقدار مشخص شده را برمیگرداند |
| thenThrow(exception) | هنگام فراخوانی استثنا پرتاب میکند |
| thenAnswer(answer) | مقدار بازگشتی را به صورت پویا محاسبه میکند |
| thenCallRealMethod() | متد واقعی را فراخوانی میکند (mock جزئی) |
زمانی که مقدار بازگشتی به آرگومانهای فراخوانی بستگی دارد، از thenAnswer با لامبدا استفاده میشود. این برای شبیهسازی کار با دادههای واقعی مفید است — مثلاً تولید ID بر اساس شیء ارسالشده.
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verify یک قابلیت منحصربهفرد Mockito است که کتابخانههای قدیمیتر اشیاء mock (EasyMock, jMock) ارائه نمیدهند.
Verify تستها را قابلاعتمادتر میکند، زیرا نه تنها مقدار بازگشتی، بلکه اثرات جانبی — فراخوانی متدهایی که نتیجه برنمیگردانند (متدهای void) — را بررسی میکند. متد verify(mock).methodName(args) بررسی میکند که آیا متد مشخصی از mock با آرگومانهای دادهشده فراخوانی شده است. این امکان تست نه تنها نتیجه، بلکه فرآیند — واقعیت مراجعه به وابستگی — را فراهم میکند.
به طور پیشفرض verify بررسی میکند که متد دقیقاً یک بار فراخوانی شده است. اگر تعداد متفاوتی نیاز باشد — از times(n)، atLeast(n)، never() و دیگر اصلاحکنندههای کلاس Mockito استفاده میشود.
// بررسی تعداد فراخوانیها
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// بررسی ترتیب فراخوانیها
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
زمانی که نیاز است بررسی شود متد با چه شیء دقیقی فراخوانی شده است، از ArgumentCaptor استفاده میشود. این ابزار مقدار آرگومان را هنگام فراخوانی ضبط میکند و امکان بررسی جداگانه فیلدهای آن را فراهم میکند. ArgumentCaptor به ویژه زمانی مفید است که کد تحت تست یک شیء را در داخل خود ایجاد کرده و به وابستگی منتقل میکند — این شیء را به روش دیگری نمیتوان بررسی کرد.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mock و @InjectMocks — دو annotation کلیدی Mockito هستند که کد تکراری را به طور قابل توجهی کاهش میدهند. @Mock برای فیلد یک mock ایجاد میکند و @InjectMocks تمام mockهای کلاس تست را از طریق سازنده، setter یا فیلد به شیء تحت تست تزریق میکند.
مکانیزم @InjectMocks سعی میکند وابستگیها را به ترتیب زیر تزریق کند: سازنده با بیشترین تعداد آرگومان، setter بر اساس نوع، فیلد خصوصی. اگر هیچ روشی کار نکند — شیء با وابستگیهای null باقی میماند و تست با NullPointerException شکست میخورد.
درک این نکته مهم است: @InjectMocks انواع فیلدها را تحلیل نمیکند — هر mock سازگار با نوع را جایگزین میکند. اگر کلاس دو فیلد از یک نوع داشته باشد — Mockito ممکن است mock اشتباهی را تزریق کند. در چنین مواردی توصیه میشود از سازنده صریح با ارسال mockها استفاده شود.
در توسعه Android، Mockito همراه با JUnit برای تست ViewModel، Repository و UseCase استفاده میشود. از آنجا که این کلاسها روی JVM بدون زمینه Android اجرا میشوند، Mockito وابستگیهای آنها — Room DAO، Retrofit API، SharedPreferences — را با stubهای قابل پیشبینی جایگزین میکند.
برای افزودن Mockito به پروژه Android، کافی است وابستگی mockito-core یا mockito-inline را اضافه کنید (دومی از mocking کلاسهای final و متدهای استاتیک پشتیبانی میکند). نسخه 5.12.0 (2024) شامل پشتیبانی از Java 21 و ادغام بهبودیافته با JUnit 5 است.
// build.gradle.kts (ماژول)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
قبلاً برای mocking متدهای استاتیک و سازندهها نیاز به PowerMock بود — افزونهای که از طریق دستکاری بایتکد کار میکرد. از Mockito 5.x با mockito-inline، این قابلیت به طور مستقیم تعبیه شده است: mockStatic(ClassName.class) امکان mocking متدهای استاتیک بدون کتابخانههای اضافی را فراهم میکند.
سناریوی معمول: ViewModel متد مخزن را فراخوانی میکند و نتیجه را به حالت UI تبدیل میکند. Mockito مخزن را جایگزین میکند و تست بررسی میکند که ViewModel پاسخ موفقیت و خطا را به درستی پردازش میکند. در معماری Clean Architecture، mockها برای هر لایه ایجاد میشوند: DataSource، Repository و UseCase — این امکان تست هر لایه را به صورت جداگانه فراهم میکند.
سوالات متداول
Mockito کتابخانهای برای Java و Kotlin است که از proxy و reflection استفاده میکند. MockK کتابخانه Kotlin-first با پشتیبانی از coroutine، extension functionها و کلاسهای final بدون پیکربندی اضافی است.
از Mockito 2.1، mocking کلاسهای final از طریق opt-in پشتیبانی میشود. در نسخه 5.x (mockito-inline) این قابلیت به طور پیشفرض فعال است. کافی است وابستگی mockito-inline را اضافه کنید و از متد استاندارد mock() استفاده کنید.
Spy یک mock جزئی است که به طور پیشفرض متدهای واقعی را فراخوانی میکند اما امکان بازنویسی برخی از آنها را از طریق when().thenReturn() میدهد. Spy برای تست کدهای legacy که نمیتوان کل کلاس را بازنویسی کرد مفید است.
thenReturn صرفنظر از آرگومانها همیشه یک مقدار را برمیگرداند. thenAnswer مقدار بازگشتی را بر اساس فراخوانی — آرگومانها، خود mock، وضعیت — محاسبه میکند. برای پاسخهای پویا همیشه از thenAnswer استفاده کنید.
Verify نه تنها نتیجه، بلکه فرآیند — واقعیت مراجعه به وابستگی را بررسی میکند. این برای سرویسهایی که باید دادهها را ذخیره کنند یا اعلان ارسال کنند حیاتی است. بدون verify، تست تشخیص نمیدهد که متد save() یا send() را فراخوانی نکرده است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید