Test Doubles — ما هي، أنواع البدائل والتطبيق

المؤلف: IT Sectr نُشر: 2026-04-10 وقت القراءة: 9 دق

Test Doubles هي كائنات بديلة تُستخدم في الاختبارات الوحدوية بدلاً من التبعيات الحقيقية. تم تقديم المصطلح بواسطة Gerard Meszaros في كتاب «xUnit Test Patterns» (2007) كمفهوم عام لـ Mock و Stub و Fake و Spy و Dummy. وفقًا Martin Fowler (2024)، تسمح Test Doubles بعزل المكون المختبر عن بيئته، مما يجعل الاختبارات حتمية وسريعة ومستقلة عن الخدمات الخارجية.

النقاط الرئيسية

  • Test Doubles — مصطلح عام لجميع أنواع الكائنات البديلة في الاختبارات
  • Mock يتحقق من التفاعل: أي الأساليب تم استدعاؤها وبأي وسائط
  • Stub يُرجع قيمًا محددة مسبقًا دون التحقق من الاستدعاءات
  • Fake — تطبيق عملي مبسط (مثل قاعدة بيانات في الذاكرة)
  • 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() أو كائنًا مع stubs. يجب ألا يحتوي 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 (تطبيقات قاعدة بيانات في الذاكرة) و Stub (استجابات API ثابتة). يسمح Fake باختبار منطق التخزين المؤقت والوضع دون اتصال دون إعداد SQLite. يحاكي Stub حالات HTTP المختلفة: 200، 404، 500، timeout.

  • الاختبارات الوحدوية لمنطق الأعمال — Mock لجميع التبعيات الخارجية، Dummy للمعاملات غير المستخدمة
  • اختبارات التكامل — Fake بدلاً من Mock (التحقق من أن المكونات تعمل معًا)
  • اختبارات واجهة المستخدم — Stub لاستجابات API (عبر MockWebServer أو WireMock)
  • اختبارات التخزين المؤقت — Fake لقاعدة البيانات (في الذاكرة بدلاً من Room/SQLite)
  • اختبارات التزامن — Mock مع دعم coroutines (MockK + Turbine لـ Flow)

الأخطاء الشائعة عند استخدام البدائل

الاستخدام غير الصحيح لـ Test Doubles هو أحد أكثر الأسباب شيوعًا للاختبارات الهشة التي تنهار مع كل إعادة هيكلة.

الإفراط في mocking: الاستخدام المفرط لـ Mock

الخطأ الأكثر شيوعًا هو mocking كل شيء. إذا تم استبدال كل تبعية في الاختبار بـ Mock، يتوقف الاختبار عن التحقق من السلوك الحقيقي. يجب استخدام Mock فقط للتبعيات الخارجية (الشبكة، قاعدة البيانات، نظام الملفات، خدمات النظام). لا ينبغي استبدال المكونات الداخلية للتطبيق (Value Object، data class، الأدوات البسيطة).

نقص التحديد: تحديد غير كافٍ

الخطأ الثاني هو إنشاء Mock دون تحديد التوقعات. إذا تم استدعاء طريقة بدون every / when، يُرجع Mock قيمة افتراضية (null، 0، false). هذا يمكن أن يؤدي إلى اختبارات إيجابية كاذبة، حيث يُرجع Mock بهدوء قيمة null ويفسر الاختبار هذا كسلوك صحيح.

الإفراط في التحقق: تحقق مفرط

الخطأ الثالث هو التحقق من كل استدعاء لكل Mock. Verify يجب استخدامه فقط للاستدعاءات المهمة بشكل حاسم من منظور منطق الأعمال. التحقق المفرط يجعل الاختبارات هشة: تغيير ترتيب الاستدعاءات في كود الإنتاج يكسر الاختبارات دون تغيير السلوك.

الأسئلة الشائعة

ما الفرق بين Mock و Stub؟

Stub يُرجع البيانات ويتحقق من الحالة (ما تم إرجاعه)، بينما Mock يتحقق من السلوك (أي الأساليب تم استدعاؤها). Stub = «أرجع X»، Mock = «تحقق من أن Y تم استدعاؤه مع الوسيط Z». في الاختبارات الحقيقية، غالبًا ما يعمل كائن واحد كـ Stub و Mock في نفس الوقت.

متى تستخدم Fake بدلاً من Mock؟

Fake أفضل من Mock عند اختبار منطق يعتمد على الحالة: التخزين المؤقت، الوضع دون اتصال، المعاملات. Fake (تطبيق في الذاكرة) يسمح باختبار هذه السيناريوهات دون استدعاءات verify هشة. Mock مناسب أكثر للتحقق من إرسال البيانات: التحليلات، push، البريد الإلكتروني.

ما هي أفضل مكتبة Test Doubles لـ Android؟

لمشاريع Android بلغة Kotlin، يُوصى بـ MockK. يدعم coroutines، ووظائف suspend، والفئات المختومة، ووظائف التوسيع دون إعداد إضافي. لمشاريع Java، يظل Mockito هو المعيار — المكتبة الأكثر شعبية مع وثائق شاملة.

كيفية اختبار Kotlin Flow مع Test Doubles؟

لاختبار Kotlin Flow، استخدم مكتبة Turbine مع MockK. تبسط Turbine التحقق من انبعاثات Flow: يمكنك التحقق من ترتيب القيم، وإتمام التدفق، والاستثناءات. يُرجع Stub لـ Flow flowOf(value)، ويتحقق Mock من أن Flow تم تجميعه.

هل يُسمح باستخدام Test Doubles في اختبارات واجهة المستخدم؟

نعم، لكن على مستوى استجابات API، وليس مكونات واجهة المستخدم. تسمح مكتبات MockWebServer (OkHttp) و WireMock بمحاكاة استجابات HTTP في اختبارات واجهة المستخدم. لا ينبغي استبدال مكونات واجهة المستخدم نفسها (Compose، SwiftUI Views) — يتم اختبار سلوكها من خلال اختبارات لقطات الشاشة و 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
  • الأخطاء الشائعة: الإفراط في mocking (استبدال كل شيء)، نقص التحديد (توقعات غير محددة)، الإفراط في التحقق (verify مفرط)
  • Fake أفضل من Mock عند اختبار منطق بالحالة — التخزين المؤقت، الوضع دون اتصال، والمعاملات

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا