Stub: چیست، انواع و کاربرد در تست‌نویسی

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

Stub (استاب، جایگزین) — یک شیء تستی است که به جای پیاده‌سازی واقعی، پاسخ‌های از پیش تعیین‌شده به فراخوانی‌های متد برمی‌گرداند. در توسعه موبایل، استاب‌ها ماژول تحت测试 را از درخواست‌های شبکه، پایگاه داده و سیستم فایل جدا می‌کنند و امکان بررسی منطق بدون پیکربندی محیط را فراهم می‌کنند. بر خلاف mock، استاب رفتار را بررسی نمی‌کند — فقط داده ارائه می‌دهد. جزئیات بیشتر در مقاله مارتین فاولر درباره test doubles.

نکات اصلی

  • Stub — جایگزینی که مقادیر مشخصی به فراخوانی‌های متد بدون منطق برمی‌گرداند
  • ایزوله‌سازی — استاب‌ها وابستگی‌های واقعی را قطع می‌کنند: API، پایگاه داده، فایل‌ها، سنسورها
  • تفاوت با Mock — استاب فراخوانی‌ها را تأیید نمی‌کند، فقط پاسخ را جایگزین می‌کند
  • Android — MockWebServer (OkHttp) به عنوان استاب برای HTTP، MockK.constantAnswer برای Kotlin
  • iOS — OCMock و پروتکل‌های Swift با پیاده‌سازی‌های تستی به عنوان استاب

Stub چیست و چه تفاوتی با سایر test doubles دارد؟

Stub — یک شیء جایگزین است که وابستگی واقعی را در تست جایگزین می‌کند و مقادیر از پیش تعیین‌شده را به فراخوانی‌های مشخص برمی‌گرداند. این اصطلاح در طبقه‌بندی Gerard Meszaros (2007) در کتاب «xUnit Test Patterns» معرفی شد. Stub به دسته test doubles تعلق دارد — اشیایی که در طول تست اجزای واقعی را جایگزین می‌کنند. هدف اصلی استاب تأمین داده‌های قابل پیش‌بینی برای بلوک مورد آزمایش و حذف عدم قطعیت سیستم‌های خارجی است.

اصل کار — تست قبل از اجرا استاب را پیکربندی می‌کند: «وقتی متد getUsers() فراخوانی شد، این لیست کاربران را برگردان». Stub شامل منطق تجاری نیست، توالی فراخوانی‌ها را بررسی نمی‌کند و تاریخچه مراجعات را ثبت نمی‌کند. آن فقط به جای مؤلفه واقعی می‌ایستد و آنچه به آن گفته شده را برمی‌گرداند. در زمینه تست Android این به این معناست که کلاینت OkHttp درخواست واقعی به سرور نمی‌فرستد، بلکه پاسخ را از MockWebServer که به عنوان استاب پیکربندی شده دریافت می‌کند.

  • Stub — داده برمی‌گرداند، فراخوانی‌ها را بررسی نمی‌کند
  • Mock — داده برمی‌گرداند و رفتار را بررسی می‌کند (verify)
  • Fake — پیاده‌سازی ساده‌شده با منطق واقعی
  • Spy — شیء واقعی را می‌پیچد و فراخوانی‌ها را ثبت می‌کند
  • Dummy — منتقل می‌شود اما استفاده نمی‌شود (null، شیء خالی)

زمان استفاده — استاب‌ها برای تست لایه UI (ViewModel, Presenter) و منطق تجاری (UseCase, Interactor) بهینه هستند، جایی که باید واکنش به داده‌های خاص را بررسی کرد: لیست خالی، سرور خطای 500 برگرداند، توکن منقضی شده باشد. هر موردی که تست به حالت ورودی مشخصی نیاز دارد — وظیفه‌ای برای stub است. برای هر سناریوی تستی پیکربندی مخصوص استاب ایجاد می‌شود که تست‌ها را خوانا و قابل پیش‌بینی می‌کند.

طبقه‌بندی test doubles بر اساس Meszaros

Gerard Meszaros (2007) در کتاب «xUnit Test Patterns» پنج نوع test doubles را مشخص کرد: dummy, stub, spy, mock, fake. هر نوع وظیفه خود را حل می‌کند. Dummy — منتقل می‌شود اما استفاده نمی‌شود. Stub — داده برمی‌گرداند. Spy — فراخوانی‌ها را ثبت می‌کند. Mock — رفتار را بررسی می‌کند. Fake — شامل منطق ساده‌شده است. درک این طبقه‌بندی به برنامه‌نویس کمک می‌کند ابزار مناسب را برای هر سناریوی تستی انتخاب کند.

استاب‌ها در تست برنامه‌های موبایل کجا استفاده می‌شوند

استاب‌ها برای درخواست‌های شبکه

درخواست‌های شبکه — رایج‌ترین سناریوی استفاده از استاب‌ها. برنامه تماس‌های HTTP به API انجام می‌دهد و در تست باید واکنش به پاسخ‌های مختلف را بررسی کرد: JSON موفق، خطای 401 (غیرمجاز)، تایم‌اوت، آرایه خالی. MockWebServer (OkHttp) در Android و URLProtocol (iOS) به عنوان استاب عمل می‌کنند و پاسخ‌های HTTP از پیش تعیین‌شده را بدون اتصال واقعی به سرور برمی‌گردانند. این کار تست‌ها را از ثانیه به میلی‌ثانیه افزایش می‌دهد.

پایگاه داده — Room (Android) و CoreData (iOS) نسخه‌های in-memory دارند، اما پیکربندی آنها همچنان زمان‌بر است. Stub به جای مخزن، لیست‌های از پیش آماده Entity را بدون دست زدن به پایگاه داده برمی‌گرداند. این به ویژه برای تست ViewModel مؤثر است، جایی که باید مرتب‌سازی، فیلتر یا تبدیل داده‌ها را بررسی کرد. تست صرف‌نظر از حجم داده در میلی‌ثانیه اجرا می‌شود.

سرویس‌های سیستمی — LocationManager, SensorManager, SharedPreferences به دستگاه واقعی یا شبیه‌ساز نیاز دارند. Stub برای LocationProvider مختصات مشخصی و برای SensorManager مقادیر ثابت شتاب‌سنج را برمی‌گرداند. در iOS مشابه آن CLLocationManager با پیاده‌سازی تستی delegate است. بدون استاب‌ها چنین تست‌هایی به دستگاه فیزیکی با شرایط خاص نیاز دارند.

سیستم فایل و کش — بارگذاری تصاویر، ذخیره پاسخ‌ها، کار با فایل‌های پیکربندی — همه این عملیات به وضعیت دیسک وابسته هستند. Stub برای FileManager یا ImageCache بدون خواندن فایل‌های واقعی موفقیت/خطا برمی‌گرداند. این کار سقوط‌های کاذب تست‌ها را به دلیل عدم تطابق مسیرها یا مجوزها در ماشین‌های مختلف توسعه‌دهندگان حذف می‌کند.

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

تقسیم مسئولیت — سه نوع test doubles وظایف مختلفی را حل می‌کنند. Stub: «به من داده بده». Mock: «بررسی کن که مرا صدا زدند». Fake: «من مثل واقعی کار می‌کنم، فقط ساده‌تر». این تفاوت برای خوانایی تست‌ها حیاتی است: اگر تست از mock در جایی که stub نیاز است استفاده کند، با فراخوانی‌های verify نامرتبط با سناریوی مورد آزمایش بارگذاری می‌شود.

ویژگیStubMockFake
هدفتأمین دادهبررسی تعاملپیاده‌سازی ساده‌شده
منطقنداردندارددارد (اما ساده)
تأییدندارددارد (verify)غیرمستقیم (از طریق وضعیت)
انعطاف‌پذیریکم — پاسخ‌های ثابتمتوسطزیاد — منطق تطبیق می‌یابد
سرعتحداکثرزیادمتوسط
مثالMockWebServer JSON برمی‌گرداندMockito.verify(repository).save()InMemoryRepository با HashMap

قاعده عملی — اگر تست بررسی می‌کند که مؤلفه مورد آزمایش چه داده‌ای دریافت کرده — از stub استفاده کنید. اگر تست بررسی می‌کند که آیا مؤلفه متد وابستگی را با آرگومان‌های صحیح فراخوانی کرده — از mock استفاده کنید. اگر فقط می‌خواهید پایگاه داده را با یک جدول هش جایگزین کنید — این fake است. مخلوط کردن انواع در یک تست آن را شکننده می‌کند: با تغییر پیاده‌سازی باید هم stub و هم منطق verify را بازنویسی کنید.

الگوی ضد: Stub با verify

Stub با verify — یک اشتباه رایج که توسعه‌دهنده استاب را پیکربندی می‌کند و سپس verify(stub).method() را اضافه می‌کند. Stub بنا بر تعریف نباید تأیید شود — برای تأیید از mock استفاده می‌شود. اگر نیاز به بررسی اینکه متد با آرگومان‌های خاص فراخوانی شده دارید، از Mockito.mock() به جای Mockito.stub() استفاده کنید. این جداسازی هدف تست را برای سایر توسعه‌دهندگان روشن نگه می‌دارد.

پیاده‌سازی استاب‌ها در Android با MockWebServer و MockK

MockWebServer — کتابخانه OkHttp برای ایجاد استاب‌های HTTP در Android و JVM. این کتابخانه یک سرور HTTP محلی در پورت مشخص راه‌اندازی می‌کند که درخواست‌های کلاینت OkHttp را رهگیری کرده و پاسخ‌های از پیش تعیین‌شده برمی‌گرداند. راه‌اندازی سه خط طول می‌کشد: ایجاد سرور، قرار دادن پاسخ در صف (enqueue)، اجرا. تست می‌تواند برای سناریوهای صفحه‌بندی یا تلاش مجدد چندین پاسخ را پشت سر هم در صف قرار دهد.

kotlin
class UserRepositoryTest {

    private val server = MockWebServer()

    fun setup() {
        server.start(8080)
        val client = OkHttpClient.Builder()
            .readTimeout(1, TimeUnit.SECONDS)
            .build()
    }

    fun test_user_list_success() {
        val json = "[{\"id\":1,\"name\":\"Alice\"}]"
        server.enqueue(MockResponse()
            .setBody(json)
            .setResponseCode(200)
        )
        val result = repository.getUsers()
        assertEquals(1, result.size)
    }

    fun teardown() {
        server.shutdown()
    }
}

MockK — جایگزین Mockito برای Kotlin با پشتیبانی درجه یک از کوروتین‌ها، توابع توسعه‌دهنده و کلاس‌های sealed. استاب‌ها در MockK از طریق coEvery (برای توابع suspend) و every (برای توابع معمولی) ایجاد می‌شوند. بر خلاف MockWebServer، MockK متدهای وابستگی جداگانه را جایگزین می‌کند نه کل لایه HTTP را. این برای تست‌های واحد UseCase یا Interactor که وابستگی‌ها انتزاعات مخزن هستند مفید است.

kotlin
interface UserRepository {
    suspend fun getUsers(): List<User>
}

class GetUsersUseCaseTest {

    private val repo = mockk<UserRepository>()

    private val useCase = GetUsersUseCase(repo)

    fun test_empty_list() = runTest {
        coEvery { repo.getUsers() } returns emptyList()

        val result = useCase.invoke()

        assertTrue(result.isEmpty())
        coVerify(exactly = 1) { repo.getUsers() }
    }
}

Best practice — برای تست‌های یکپارچه‌سازی از MockWebServer استفاده کنید (HTTP واقعی را رهگیری می‌کند)، برای تست‌های واحد — MockK (اینترفیس‌ها را جایگزین می‌کند). چیزی را که تست نمی‌کنید جایگزین نکنید: اگر تست Repository را بررسی می‌کند، کلاینت OkHttp درون آن را جایگزین نکنید — از MockWebServer واقعی در سطح HTTP استفاده کنید. این قانون تست‌ها را مرتبط نگه می‌دارد و شکنندگی در بازسازی را کاهش می‌دهد.

پیاده‌سازی استاب‌ها در iOS با OCMock و پروتکل‌ها

پروتکل‌های Swift به عنوان استاب — در رویکرد بومی iOS، استاب از طریق جایگزینی ساختار تستی مطابق با پروتکل وابستگی پیاده‌سازی می‌شود. به جای NetworkService واقعی، تست StubNetworkService را دریافت می‌کند که داده‌های ثابت را برمی‌گرداند. Swift یک زبان با تایپ ایستا است، بنابراین استاب باید با همان پروتکل سرویس واقعی مطابقت داشته باشد. کامپایلر تضمین می‌کند که استاب تمام متدهای مورد نیاز را پیاده‌سازی می‌کند.

swift
protocol NetworkServiceProtocol {
    func fetchUsers() async throws -> [User]
}

struct StubNetworkService: NetworkServiceProtocol {
    let result: Result<[User], Error>

    func fetchUsers() async throws -> [User] {
        try result.get()
    }
}

final class UsersViewModelTests: XCTestCase {
    func test_success_state() async {
        let stub = StubNetworkService(
            result: .success([User(name: "Alice")])
        )
        let vm = UsersViewModel(service: stub)
        await vm.load()
        XCTAssertEqual(vm.users.count, 1)
    }
}

OCMock برای Objective-C — کتابخانه‌ای برای ایجاد استاب و mock در پروژه‌های قدیمی iOS. OCMock از متدهای stub با آرگومان‌ها و مقادیر برگشتی پشتیبانی می‌کند. پروژه‌های مدرن Swift رویکرد مبتنی بر پروتکل با استاب‌های دستی را ترجیح می‌دهند — این کنترل بر هر متد را فراهم می‌کند و به وابستگی‌های خارجی نیاز ندارد. OCMock گزینه‌ای برای پروژه‌هایی باقی می‌ماند که پروتکلیزه کردن همه وابستگی‌ها از نظر اقتصادی مناسب نیست.

URLProtocol برای استاب‌های HTTP — مکانیسم سیستمی iOS برای رهگیری درخواست‌های شبکه از طریق زیرکلاس URLProtocol. تست یک URLProtocol سفارشی ثبت می‌کند که URLSession را رهگیری کرده و پاسخ‌های استاب را برمی‌گرداند. مزیت نسبت به استاب‌های دستی: نیازی به تغییر معماری برنامه نیست — URLSession واقعی باقی می‌ماند اما داده‌ها در سطح پروتکل جایگزین می‌شوند. نقطه ضعف: دیباگ کردن آن نسبت به سرویس استاب صریح دشوارتر است.

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

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

Stub داده‌های از پیش تعیین‌شده را برمی‌گرداند و واقعیت فراخوانی را بررسی نمی‌کند. Mock علاوه بر این تأیید می‌کند که متد با آرگومان‌های صحیح فراخوانی شده (verify). Stub به سوال «چه چیزی برگردانم» و Mock به سوال «آیا فراخوانی شد» پاسخ می‌دهد. برای بررسی وضعیت از stub و برای بررسی تعامل از mock استفاده کنید.

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

Fake زمانی نیاز است که تست به یک پیاده‌سازی کارآمد (هرچند ساده‌شده) نیاز دارد — مثلاً پایگاه داده in-memory به جای Room. Stub برای سناریوهای منفرد با داده‌های از پیش تعیین‌شده مناسب است. اگر همان stub را در 10 تست تکرار می‌کنید — به احتمال زیاد به Fake نیاز دارید. Fake باعث کاهش تکرار می‌شود زیرا منطق در یک کلاس زندگی می‌کند.

آیا می‌توان متدهای استاتیک را استاب کرد؟

در Android — MockK برای اشیاء Kotlin (object) از mockkObject() از جمله متدهای استاتیک کلاس‌های Java از طریق mockkStatic() پشتیبانی می‌کند. در iOS — متدهای استاتیک Swift مستقیماً استاب نمی‌شوند؛ از پروتکل‌ها و DI استفاده کنید تا فراخوانی static را با متد نمونه پروتکل جایگزین کنید. استاب‌های استاتیک بدهی فنی هستند و باید در کد جدید اجتناب شوند.

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

از MockWebServer (OkHttp) استفاده کنید — آن به عنوان یک سرور HTTP محلی کار می‌کند که پاسخ‌ها را در صف (enqueue) قرار می‌دهد. برای Retrofit کافی است URL پایه را به localhost:8080 تغییر دهید. برای Ktor از MockEngine استفاده کنید — مکانیزم داخلی برای جایگزینی HttpStatement. هر دو رویکرد بدون اینترنت واقعی کار می‌کنند و کنترل کامل بر کد وضعیت، بدنه و هدرهای پاسخ می‌دهند.

Stub vs Spy — تفاوت چیست؟

Spy شیء واقعی را می‌پیچد و فراخوانی‌ها را ثبت می‌کند، در حالی که Stub شیء را کاملاً با پاسخ‌های ثابت جایگزین می‌کند. Spy امکان استفاده جزئی از پیاده‌سازی واقعی را می‌دهد (سایر متدها همانطور کار می‌کنند)، اما stub نمی‌دهد. اگر نیاز به بررسی فراخوانی متد دارید اما بخشی از منطق باید اجرا شود — از spy استفاده کنید نه stub.

خلاصه

  • Stub — شیء جایگزینی که پاسخ‌های از پیش تعیین‌شده به فراخوانی‌های متد در طول تست برمی‌گرداند
  • ایزوله‌سازی وابستگی‌ها — استاب‌ها درخواست‌های شبکه، پایگاه داده، سرویس‌های سیستمی و سیستم فایل را جایگزین می‌کنند
  • تفاوت با Mock — استاب فراخوانی‌ها را تأیید نمی‌کند، فقط داده را بدون بررسی رفتار برمی‌گرداند
  • ابزارهای Android — MockWebServer برای HTTP، MockK برای اینترفیس‌های Kotlin با پشتیبانی کوروتین
  • ابزارهای iOS — استاب‌های مبتنی بر پروتکل در Swift، URLProtocol برای HTTP، OCMock برای Objective-C
  • نقش‌ها را مخلوط نکنید — verify به stub اضافه نکنید، از mock برای تأیید فراخوانی استفاده کنید
  • Stub + MockWebServer — رویکرد استاندارد برای تست‌های یکپارچه‌سازی بدون سرور واقعی

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

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

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

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