Test Doubles — این چیست، انواع و کاربرد

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

Test Doubles — این شیء‌های جایگزینی هستند که در تست‌های واحد به جای وابستگی‌های واقعی استفاده می‌شوند. این اصطلاح توسط Gerard Meszaros در کتاب «xUnit Test Patterns» (2007) به عنوان یک مفهوم کلی برای Mock، Stub، Fake، Spy و Dummy معرفی شد. به گزارش مارتین فاولر (2024)، Test Doubles به جداسازی کامپوننت مورد آزمایش از محیط آن کمک می‌کنند و آزمایش‌ها را تعیین‌گرا، سریع و مستقل از خدمات خارجی می‌سازند.

نکات کلیدی

  • Test Doubles — اصطلاحی کلی برای همه انواع شیء‌های جایگزین در آزمایش
  • Mock تعامل را بررسی می‌کند: چه متدهایی با چه آرگمان‌هایی فراخوانده شده‌اند
  • Stub مقادیر از پیش تعیین شده را بدون بررسی فراخوانی ها بازمی‌گرداند
  • Fake — یک پیاده‌سازی ساده شده اما کاربردی (مثلاً، پایگاه داده in-memory)
  • Spy فراخوانی‌ها را برای بررسی بعدی ثبت می‌کند، Dummy پارامترها را پر می‌کند

Test Doubles چیست؟

Test Doubles — این اصطلاحی از صنعت خودروسازی (بدلکار، «جایگزین» برای بازیگران) است که به توسعه نرم‌افزار منتقل شده است. همانطور که یک بدلکار بازیگر را در یک ساحنه خطرناک جایگزین می‌کند، Test Double یک کامپوننت واقعی را در یک سناریو آزمایشی جایگزین می‌کند. این زمانی ضروری است که وابستگی واقعی در دسترس نباشد، کند باشد، غیرتعیین‌گرا باشد یا عوارض جانبی داشته باشد.

مفهوم Test Double شامل پنج نوع مشخص است که هر کدام مسئله خود را حل می‌کند. طبقه‌بندی Meszaros کانونی است و در تمامی راهنماهای مدرن آزمایش استفاده می‌شود. تفاوت بین انواع در میزان کنترل و بازبینی است: از پر کردن ساده پارامترها (Dummy) تا بررسی کامل تسلسل فراخوانی‌ها (Mock).

چرا Test Doubles لازم هستند

هدف اصلی Test Doubles جداسازی ماژول مورد آزمایش است. در توسعه موبایل، وابستگی‌های واقعی شامل سرورهای API، پایگاه‌های داده، سیستم پرونده، حسگرهای دستگاه، خدمات سیستمی (LocationManager، Camera، Bluetooth) هستند. استفاده مستقیم از این کامپوننت‌ها آزمایش‌ها را کند، شکنانده و وابسته به محیط می‌کند. به گزارش Google Testing Blog (2023)، آزمایش‌های واحد بهخوبی جداشده شده در طول میلی‌ثانیه انجام می‌شوند، و آزمایش‌های انتگرالیونی در ثانیه و دقیقه.

پنج نوع Test Doubles

طبقه‌بندی Gerard Meszaros شامل پنج نوع Test Doubles است که از نظر رفتار و هدف استفاده تفاوت دارند. درک تفاوت بین آن‌ها اساس آزمایش واحد صحیح است.

Dummy

Dummy — شیءی است که به متد مورد آزمایش ارسال می‌شود اما هرگز استفاده نمی‌شود. Dummy فقط برای برآوردن امضای متد مورد نیاز است. در Kotlin این عموماً null، emptyList() یا یک شیء با جایگزین‌هاست. Dummy نباید هیچ منطقی داشته باشد — اگر فراخوانده شود، آزمایش باید شکست بخورد.

Fake

Fake — یک پیاده‌سازی ساده شده اما کاربردی از یک رابط است. بر خلاف Mock و Stub، Fake شامل منطق کسب وکار واقعی است، اما به صورت ساده شده. مثال کلاسیک — InMemoryUserRepository که داده‌ها را به جای پایگاه داده در HashMap ذخیره می‌کند. Fake زمانی استفاده می‌شود که منطق وابسته به وضعیت بدون سربار زیرساخت واقعی آزمایش شود.

نوعکاربردمثال
Dummyپر کردن پارامترnull، شیء خالی
Fakeپیاده‌سازی ساده شده کاربردیInMemoryRepository
Stubبازگرداندن مقدار ثابتwhen(api.getUser()).thenReturn(user)
Spyثبت فراخوانی‌ها برای بررسیverify(spy).save(user)
Mockبررسی تعاملverify(mock).sendEmail(email)

Stub

Stub مقادیر از پیش تعیین شده را برای فراخوانی‌های مشخص بازمی‌گرداند. Stub بررسی نمی‌کند که آیا فراخوانده شده است — آن فقط داده توسیله می‌کند. در Mockito، Stub از طریق when(method).thenReturn(value) ایجاد می‌شود. Stub برای آزمایش زمانی ایدهآل است که وابستگی باید مقدار مشخصی را بازگرداند، اما واقعیت فراخوانی مهم نیست.

Spy

Spy — یک پیچش در اطراف یک شیء واقعی است که تمامی فراخوانی‌ها را برای بازبینی بعدی ثبت می‌کند. بر خلاف Mock، Spy فراخوانی‌ها را به شیء واقعی ارجاع می‌دهد، اما امکان بررسی وقوع آن‌ها را فراهم می‌کند. در Mockito، Spy از طریق spy(realObject) ایجاد می‌شود. Spy برای mocking جزئی مفید است زمانی که می‌خواهید از یک شیء واقعی استفاده کنید اما برخی فراخوانی‌ها را بررسی کنید.

Mock

Mock — یک شیء با چشم‌انتظارات فراخوانی از پیش تعریف شده است. Mock بررسی می‌کند که آیا متدهای مشخصی با آرگمان‌های مشخصی و در ترتیب مشخصی فراخوانده شده‌اند. بر خلاف Stub، Mock بر بازبینی رفتار تمرکز دارد، نه بر بازگرداندن داده. Mock قوی‌ترین و پراستفاده‌ترین نوع Test Double در توسعه موبایل است.

Mock و Stub: تفاوت‌های کلیدی

تفاوت بین Mock و Stub حتی در بین توسعه‌دهندگان مبتدی نیز سترگی ایجاد می‌کند. تفاوت اصلی در هدف است: Stub وضعیت را بررسی می‌کند (state verification)، Mock رفتار را بررسی می‌کند (behavior verification).

Stub به این سؤال پاسخ می‌دهد: «آیا کد نتیجه صحیحی را بازگرداند؟». Mock به این سؤال پاسخ می‌دهد: «آیا کد روش‌های مناسب را با آرگمان‌های مناسب فراخواند؟». در توسعه موبایل، Stub زمانی استفاده می‌شود که نتیجه مهم است (مثلاً، داده از رپوزیتوری)، و Mock زمانی که عوارض جانبی مهم هستند (مثلاً، ارسال ایمیل، نوشتن در پایگاه داده).

kotlin
// Stub: بررسی وضعیت
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: بررسی رفتار
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

مثال‌های Test Doubles در Kotlin

مثال‌های عملی هر پنج نوع Test Doubles در Kotlin با استفاده از MockK — محبوبترین کتاخانه mocking برای پروژه‌های Android.

Fake: InMemoryUserRepository

kotlin
class InMemoryUserRepository : UserRepository {
    private val store = mutableMapOf<String, User>()

    override fun save(user: User) {
        store[user.email] = user
    }

    override fun findByEmail(email: String): User? {
        return store[email]
    }
}

Stub + Mock: آزمایش UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: بازگرداندن پاسخ ثابت API
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: بررسی که کاربر ذخیره شده است
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: آزمایش با پارامتر استفاده نشده

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: Context در داخل Logger استفاده نشده است
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

کی کدام نوع را در توسعه موبایل استفاده کنیم

انتخاب نوع Test Double بستگی به این دارد که دقیقاً چه چیزی آزمایش می‌شود: وضعیت، رفتار یا انتگرالیون. در توسعه موبایل بر روی Android و iOS توصیه‌های زیر شکل گرفته‌اند.

برای ViewModel و UseCase

در آزمایش ViewModel، از Mock برای وابستگی‌هایی که عوارض جانبی ایجاد می‌کنند (رپوزیتوری‌ها، تحلیل، ناوبری) و از Stub برای وابستگی‌هایی که داده بازمی‌گردانند (مشتریان API، ContentProvider) استفاده کنید. این امکان را فراهم می‌کند که ViewModel هم سناریوهای موفق و هم سناریوهای خطا را به صورت صحیح مدیریت کند.

برای Repository و لایه داده

در سطح Repository، Fake (پیاده‌سازی‌های پایگاه داده in-memory) و Stub (پاسخ‌های ثابت API) ترجیح دارند. Fake امکان آزمایش منطق کش و حافظه موقت و حالت آفلاین را بدون تنظیمات SQLite فراهم می‌کند. Stub کدهای مختلف HTTP را شبیه‌سازی می‌کند: 200، 404، 500، timeout.

  • آزمایش‌های واحد منطق کسب وکار — Mock برای تمامی وابستگی‌های خارجی، Dummy برای پارامترهای استفاده نشده
  • آزمایش‌های انتگرالیونی — Fake به جای Mock (بررسی کنیم که کامپوننت‌ها با هم کار می‌کنند)
  • آزمایش‌های UI — Stub برای پاسخ‌های API (از طریق MockWebServer یا WireMock)
  • آزمایش‌های کش — Fake برای پایگاه داده (in-memory به جای Room/SQLite)
  • آزمایش‌های ناهمگامی — Mock با پشتیبانی از کوروتین (MockK + Turbine برای Flow)

اشتباهات رایج در استفاده از جایگزین‌ها

استفاده نادرست از Test Doubles — یکی از شایع‌ترین علت‌های آزمایش‌های شکنانده است که در هر بازنویسی می‌شکنند.

Over-mocking: استفاده از حد Mock

شایع‌ترین اشتباه — mocking هر چیز. اگر هر وابستگی در آزمایش با Mock جایگزین شود، آزمایش از بررسی رفتار واقعی باز می‌ایستد. Mock فقط برای وابستگی‌های خارجی (شبکه، پایگاه داده، سیستم پرونده، خدمات سیستمی) باید باشد. کامپوننت‌های داخلی برنامه (Value Object، data class، ابزارهای ساده) نباید جایگزین شوند.

Under-specification: مشخصات ناکافی

دومین اشتباه — ایجاد Mock بدون تعریف چشم‌انتظارات. اگر متدی بدون every / when فراخوانده شود، Mock مقدار پیش‌فرض را بازمی‌گرداند (null، 0، false). این می‌تواند به آزمایش‌های مثبت کاذب منجر شود زمانی که Mock ساکت null را بازمی‌گرداند و آزمایش این را به عنوان رفتار صحیح تفسیر کند.

Over-verification: بازبینی از حد

سومین اشتباه — بررسی هر فراخوانی هر Mock. Verify فقط برای فراخوانی‌هایی استفاده شود که از نظر منطق کسب وکار حیاتی هستند. بازبینی از حد آزمایش‌ها را شکنانده می‌کند: تغییر ترتیب فراخوانی‌ها در کد تولید بدون تغییر رفتار آزمایش‌ها را می‌شکند.

پرسش‌های متداول

تفاوت بین Mock و Stub چیست؟

Stub داده‌ها را بازمی‌گرداند و وضعیت را بررسی می‌کند (چه چیزی بازگشته شد،) و Mock رفتار را بررسی می‌کند (چه متدهایی فراخوانده شدند). Stub = «X را بازگردان»، Mock = «بررسی کن که Y با آرگمان Z فراخوانده شد». در آزمایش‌های واقعی، یک شیء چندان هم همچون Stub و هم همچون Mock عمل می‌کند.

کی به جای Mock از Fake استفاده کنیم؟

Fake به Mock ترجیح دارد زمانی که منطق وابسته به وضعیت آزمایش می‌شود: کش، حالت آفلاین، ترانزاکشن‌ها. Fake (پیاده‌سازی in-memory) امکان آزمایش این سناریوها را بدون فراخوانی‌های verify شکنانده فراهم می‌کند. Mock برای بررسی ارسال داده مناسب‌تر است: تحلیل، اعلام پش، ایمیل.

کدام کتاخانه Test Doubles برای Android بهتر است؟

برای پروژه‌های Android در Kotlin، MockK توصیه می‌شود. آن از کوروتین‌ها، تابع‌های suspend، sealed class و تابع‌های extension را بدون تنظیمات اضافی پشتیبانی می‌کند. برای پروژه‌های Java، استاندارد هنوز Mockito است — محبوبترین کتاخانه با مستندات گسترده.

چگونه Kotlin Flow را با Test Doubles آزمایش کنیم؟

برای آزمایش Kotlin Flow، از کتاخانه Turbine در کنار MockK استفاده کنید. Turbine بررسی تولید Flow را ساده می‌کند: می‌توان ترتیب مقادیر، پایان جریان و استثناها را بررسی کرد. Stub برای Flow flowOf(value) را بازمی‌گرداند، Mock بررسی می‌کند که Flow جمع‌آوری شده است.

آیا استفاده از Test Doubles در آزمایش‌های UI مجاز است؟

بله، اما در سطح پاسخ‌های API، نه کامپوننت‌های UI. کتاخانه‌های MockWebServer (OkHttp) و WireMock امکان جایگزینی پاسخ‌های HTTP را در آزمایش‌های UI فراهم می‌کنند. کامپوننت‌های UI (Compose، SwiftUI Views) نباید جایگزین شوند — رفتار آن‌ها از طریق آزمایش‌های screenshot و Espresso آزمایش می‌شود.

نتایج

  • Test Doubles — اصطلاحی کلی برای پنج نوع شیء جایگزین: Mock، Stub، Fake، Spy، Dummy
  • Mock رفتار را بررسی می‌کند (verify)، Stub داده بازمی‌گرداند (thenReturn)، Fake مانند یک پیاده‌سازی واقعی ساده شده کار می‌کند
  • Spy شیء واقعی را احاطه می‌کند و فراخوانی‌ها را ثبت می‌کند، Dummy پارامترهای استفاده نشده را پر می‌کند
  • طبقه‌بندی Gerard Meszaros — طبقه‌بندی کانونی که در تمامی چارچوب‌های mocking مدرن استفاده می‌شود
  • برای پروژه‌های Kotlin MockK، برای Java — Mockito، برای iOS — Cuckoo یا OHHTTPStubs توصیه می‌شود
  • اشتباهات رایج: over-mocking (جایگزینی هر چیز)، under-specification (چشم‌انتظارات مشخص نشده)، over-verification (بازبینی از حد)
  • Fake در آزمایش منطق با وضعیت به Mock ترجیح دارد — کش، حالت آفلاین و ترانزاکشن‌ها

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

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

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

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