MockK: این چیست، مفاهیم کلیدی و نحو نگارش

نویسنده: IT Sectr منتشر شده: 2026-04-08 زمان مطالعه: 8 دقیقه

MockK — یک چارچوب Kotlin-first برای ایجاد اشیاء mock است که به‌طور ویژه برای اکوسیستم Kotlin با توجه به ویژگی‌های زبانی آن طراحی شده است: کروتین‌ها، توابع افزوده، data class و sealed class. به عکس Mockito که از Java به Kotlin پورت شده، MockK از ابتدا برای نحو نگارش Kotlin طراحی شده و برای کار با کلاس‌های final به پلاگین‌های اضافی نیاز ندارد. به طور میزان داده‌های MockK.io، این کتابخانه در بیش از 40% پروژه‌های Kotlin با آزمون‌های واحد استفاده می‌شود.

نکات کلیدی

  • MockK — یک کتابخانه Kotlin-محور برای mock کردن با پشتیبانی از کروتین‌ها و ویژگی‌های زبان.
  • mockk() — روش اصلی ایجاد شیء mock، مشابه Mockito.mock().
  • every { } — بلوک برای پیکربندی رفتار mock (stubbing) به سبک اعلامی.
  • coEvery / coVerify — ساختارهای ویژه برای کار با توابع suspend کروتین.
  • Relaxed mock — mockای که بدون stubbing صریح مقادیر پیش‌فرض را برمی‌گرداند.

MockK چیست؟

MockK — یک کتابخانه برای ایجاد اشیاء mock است که به زبان Kotlin نوشته شده و برای نحو نگارش آن بهینه‌سازی شده است. همان مشکلاتی که Mockito حل می‌کند حل می‌کند — جداسازی کد تست شده از وابستگی‌ها — اما این کار را با استفاده از ساختارهای ویژه Kotlin انجام می‌دهد: لامبداها، DSL، reified generics و توابع suspend.

مزیت اصلی MockK نسبت به راه‌حل‌های پورت شده — پشتیبانی ذاتی از Kotlin است. در Mockito، mock کردن final-class نیازمند opt-in است (mockito-inline)، و روش‌های ساکن نیازمند mockStatic هستند. MockK از این قابلیت به‌صورت پیش‌فرض پشتیبانی می‌کند، زیرا کلاس‌های Kotlin به‌صورت پیش‌فرض final هستند و دور زدن این محدودیت در معماری کتابخانه ساخته شده است.

نسخه 1.13.12 (2024) — انتشار پایدارای است که از Kotlin 2.0، کامپایلر K2 و پروژه‌های چند سیستمی (KMP) پشتیبانی می‌کند. MockK نیز با Kotlin/Native و Kotlin/JS کار می‌کند، که آن را به تنها گزینه برای پروژه‌های KMP تبدیل می‌کند، جایی که نه Mockito و نه EasyMock قابل استفاده نیستند.

MockK با توجه به ویژگی‌های Kotlin طراحی شده و از قابلیت‌های زبان — reified generics، DSL با لامبداها، توابع inline — برای ارائه API مختصر و ایمن از نظر نوع بدون از دست دادن عملکرد استفاده می‌کند.

MockK چگونه کار می‌کند

مکانیزم MockK بر اساس ابزاردهای بایتکد از طریق کتابخانه ByteBuddy استوار دارد (مانند Mockito)، اما آن را در چهارچوب یک DSL دوستانه Kotlin قرار می‌دهد. به جای زنجیره‌های when().thenReturn()، MockK از بلوک‌های لامبدایی every { } و coEvery { } استفاده می‌کند که به عنوان یک افزایش طبیعی زبان به نظر می‌رسند. در داخل، MockK فراخوانی داخل لامبدا را رهگیری می‌کند، روش و آرگومان‌ها را از طریق بازتابی تجزیه و تحلیل می‌کند و آنها را با قوانین stubbing ثبت شده مطابقت می‌دهد.

نحو نگارش پایه MockK

بلوک every { mock.method() } returns value به این معنی است که «هر بار که روش فراخوانده شود، مقدار را برگردان». چنین نحو نگارش اعلامی به سبک Kotlin نزدیک‌تر است و سردرگمی با ترتیب آرگومان‌ها در when() را از بین می‌برد. به لطف reified generics Kotlin، نوع mock بدون تشخیص صریح کلاس به‌صورت خوداتیک استنباط می‌شود.

kotlin
val repository = mockk<UserRepository>()

// Stubbing: هر فراخوانی findById(1) کاربر را برمی‌گرداند
every { repository.findById(1) } returns User("Alice")

// فراخوانی و بررسی
val result = repository.findById(1)
assertEquals("Alice", result.name)

Relaxed mock: کد تمیزتر، اندکتر boilerplate

به عکس Mockito که هر روش را باید صریحاً پیکربندی کرد، MockK از relaxed mock پشتیبانی می‌کند — mockای که برای هر روش مقادیر پیش‌فرض «معقول» را برمی‌گرداند: لیست خالی برای List، 0 برای Int، رشته خالی برای String. این حجم کد آماده‌سازی را به شدت کاهش می‌دهد.

kotlin
// Relaxed mock — همه روش‌ها مقادیر پیش‌فرض را برمی‌گردانند
val api = mockk<ApiService>(relaxed = true)

// نیازی به stubbing ندارد — لیست خالی را برمی‌گرداند
println(api.getUsers()) // []

ایجاد mock‌ها و mock‌های سبک‌شده

MockK چندین روش برای ایجاد اشیاء mock ارائه می‌دهد: mockk<T>() برای mock سخت‌گیرانه (هر روش باید صریحاً پیکربندی شود)، mockk<T>(relaxed = true) برای mock سبک‌شده و spyk(obj) برای ایجاد یک جاسوس بر روی شیء واقعی.

تابعنوعرفتار بدون stubbing
mockk()Mock سخت‌گیرانهدر صورت فراخوانی روش stub-نشده استسنا می‌اندازد
mockk(relaxed = true)Mock سبک‌شدهمقدار پیش‌فرض را برمی‌گرداند
spyk()جاسوساگر stub پیکربندی نشده باشد، روش واقعی را فراخوان می‌کند
slot()Argument Captorآرگومان را برای بررسی ذخیره می‌کند

انتخاب بین mock سخت‌گیرانه و سبک‌شده به زمینه بستگی دارد. Mock سخت‌گیرانه تضمین می‌کند که آزمون از روش‌هایی که رفتارشان تعریف نشده است، استفاده نمی‌کند — این قابلیت اطمینان را افزایش می‌دهد. Mock سبک‌شده برای نمونه‌سازی سریع آزمون‌ها مناسب است جایی که همه وابستگی‌ها مهم نیستند. در عمل، توصیه می‌شود از mock سخت‌گیرانه شروع کرده و تنها زمانی به relaxed روی آورید که stubbing بیشتر از خود آزمون خط ها را اشغال کند.

Stubbing: پیکربندی رفتار با بلوک every

بلوک every — ساختار مرکزی stubbing در MockK است. داخل لامبدا، فراخوانی روش با آرگومان‌های مشخص توصیف می‌شود، و سپس مقدار از طریق returns برگردانده می‌شود، استسنا از طریق throws پرتاب می‌شود، یا پاسخ از طریق answers محاسبه می‌شود.

روش‌های مختلف stubbing

MockK از همه سناریوهای مورد نیاز جهت آزمون پشتیبانی می‌کند: بازگرداندن مقدار، پرتاب استسنا، محاسبه پاسخ بر اساس آرگومان‌ها، چندین پاسخ به ترتیب (تسلسل فراخوانی‌ها).

kotlin
// بازگرداندن مقدار
every { repo.findById(1) } returns User("Alice")

// پرتاب استسنا
every { repo.findById(999) } throws NotFoundException()

// پاسخ پویا
every { repo.save(any()) } answers {
    val user = firstArg<User>()
    user.copy(id = 42)
}

// تسلسل پاسخ‌ها
every { repo.findAll() } returnsMany listOf(
    listOf(User("Alice")),
    listOf(User("Bob")),
    emptyList()
)

Verify و coVerify برای کروتین

Verify در MockK از نظر مفهوم مشابه Mockito.verify() است، اما از DSL Kotlin استفاده می‌کند: verify { mock.method() }. برای توابع suspend از coVerify { mock.suspendMethod() } استفاده می‌شود که با کروتین‌ها به درستی کار می‌کند و نیازی به runner ویژه ندارد.

بررسی تعداد فراخوانی‌ها

MockK از همان مودیفکاتورهایی که Mockito پشتیبانی می‌کند پشتیبانی می‌کند: exactly(1)، atLeast(2)، atMost(5)، wasNot(Called). نحو نگارش مینیمالیست — مودیفکاتور به عنوان اولین آرگومان در verify { } ارسال می‌شود.

kotlin
// بررسی: روش دقیقاً 1 بار فراخوانده شد
verify(exactly = 1) { repo.save(any()) }

// بررسی ترتیب فراخوانی‌ها
verifySequence {
    repo.save(any())
    repo.flush()
}

// coVerify برای توابع suspend
coVerify { api.fetchUsers() }

Slot: ذخیره آرگومان‌ها

برای بررسی آرگومان‌ها از slot() — معادل ArgumentCaptor استفاده می‌شود. Slot قبل از فراخوانی اعلام می‌شود، به every یا verify ارسال می‌شود، و پس از اجرای آزمون شامل مقدار ذخیره شده است.

kotlin
val userSlot = slot<User>()

verify { repo.save(capture(userSlot)) }

assertEquals("Alice", userSlot.captured.name)

انواتاسیون‌های MockK و ادغام با JUnit

MockK انواتاسیون‌های @MockK و @RelaxedMockK را برای ایجاد mock از طریق مقداردهی در JUnit 5 فراهم می‌کند. افزونه MockKExtension به‌طور خودکار قبل از هر آزمون mock می‌سازد و پس از آن پاک می‌کند — مشابه MockitoExtension، اما با پشتیبانی از حالت relaxed.

مثال با MockKExtension

انواتاسیون @InjectMockKs (یا جایگزین @MockK با ایجاد صریح شیء) mock‌ها را به نمونه مورد آزمون تزریق می‌کند. این کدهای boilerplate را کاهش می‌دهد و کد آزمون را تمیزتر می‌کند.

kotlin
@ExtendWith(MockKExtension::class)
class UserServiceTest {

    @MockK
    lateinit var repository: UserRepository

    @InjectMockKs
    lateinit var service: UserService

    @Test
    fun `getUser returns user from repository`() {
        every { repository.findById(1) } returns User("Alice")
        assertEquals("Alice", service.getUser(1)?.name)
    }
}

MockK در مقابل Mockito: چه چیزی را برای Kotlin انتخاب کنیم

انتخاب بین MockK و Mockito به ترکیب تیم و نوع پروژه بستگی دارد. Mockito اکوسیستم بزرگ‌تری، نمونه‌های بیشتر و ادغامات بیشتری دارد، اما MockK نحو نگارش تمیزتر Kotlin و پشتیبانی ذاتی از ویژگی‌های زبان را فراهم می‌کند. برای پروژه‌های جدید Kotlin، MockK به عنوان راه‌حل ایدیوماتیک‌تر توصیه می‌شود.

معیارMockKMockito
نحو نگارشDSL Kotlin (every, verify)سبک Java (when, thenReturn)
کروتین‌هاcoEvery, coVerify (ذاتی)نیازمند کتابخانه‌های اضافی
Final classبه‌صورت پیش‌فرض پشتیبانی می‌شودنیازمند mockito-inline
KMPپشتیبانی می‌شودپشتیبانی نمی‌شود
Relaxed mockداخلیهیچ معادلی ندارد
محبوبیتدر جامعه Kotlin در حال رشددر Java و پروژه‌های ترکیبی دومینه دارد

برای پروژه‌های Kotlin خالص (بدون کلاس‌های Java) MockK تریجیح دارد: boilerplate کمتر، پشتیبانی ذاتی کروتین، هیچ شگفتی با کلاس‌های final. برای پروژه‌های ترکیبی یا تیم‌هایی با سابقه Java، Mockito یک گزینه کاربردی باقی می‌ماند — هر دو کتابخانه را می‌توان در یک پروژه از طریق ماژول‌های مختلف استفاده کرد. هنگام انتقال از Mockito به MockK، کافی است انواتاسیون‌های @Mock را با @MockK جایگزین کرده و بلوک‌های when().thenReturn() را به فرمت every { } تبدیل کرد.

سوالات متداول

تفاوت relaxed mock با mock عادی در MockK چیست؟

Relaxed mock برای همه روش‌های stub-نشده مقادیر پیش‌فرض را برمی‌گرداند (لیست خالی، 0، null)، بدون اینکه استسنا بفرستد. mock عادی (strict) نیازمند stubbing صریح هر روش است — در غیر این صورت آزمون شکست می‌خورد. Relaxed mock برای آزمون‌های سریع مناسب است، strict برای آزمون‌های مطمئن.

چگونه می‌توان توابع افزوده را در MockK mock کرد؟

MockK از mock کردن توابع افزوده از طریق mockkStatic() پشتیبانی می‌کند. این امکان‌پذیر است زیرا توابع افزوده در Kotlin روش‌های ساکنی هستند که پارامتر اول آنها دریافت‌کننده است. برای هر تابع افزوده، کلاسی که در آن اعلام شده است باید مشخص شود.

آیا MockK با Kotlin Multiplatform کار می‌کند؟

بله، MockK از Kotlin Multiplatform (KMP) برای کد مشترک پشتیبانی می‌کند. در سطح سایت‌های JVM، Native و JS می‌توان از API مشترک mockk()، every، verify استفاده کرد. این MockK را به تنها گزینه برای پروژه‌های KMP تبدیل می‌کند که Mockito در آنها کار نمی‌کند.

چگونه می‌توان ترتیب فراخوانی‌ها را در MockK بررسی کرد؟

از verifySequence { } استفاده کنید — بلوکی که در آن فراخوانی‌ها به طور سخت‌گیرانه در ترتیب مورد انتظار مشخص شده‌اند. اگر ترتیب واقعی متفاوت باشد، verifySequence با نشان دادن اولین فراخوانی نامطابق استسنا می‌افکند.

آیا می‌توان MockK و Mockito را در یک پروژه استفاده کرد؟

بله، از نظر فنی ممکن است، اما توصیه نمی‌شود. تعارضات می‌تواند در سطح ابزاردهای بایتکد ایجاد شود (ByteBuddy vs mockito-inline). اگر پروژه از قبل Mockito استفاده می‌کند، انتقال به MockK می‌تواند از طریق جداسازی ماژول‌ها تدریجی انجام شود.

نتایج

  • MockK — کتابخانه Kotlin-first برای mock کردن با پشتیبانی ذاتی از زبان.
  • every { } — DSL اعلامی برای پیکربندی رفتار mock‌ها.
  • coEvery / coVerify — پشتیبانی از توابع suspend کروتین بدون وابستگی‌های اضافی.
  • Relaxed mock — mock با مقادیر پیش‌فرض که boilerplate را کاهش می‌دهد.
  • @MockK / @InjectMockKs — انواتاسیون‌هایی برای ایجاد خودکار mock در JUnit 5.
  • MockK vs Mockito — MockK برای پروژه‌های خالص Kotlin و KMP تریجیح دارد.
  • verifySequence — بررسی ترتیب سخت‌گیرانه فراخوانی‌های روش.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید