Fake — ما هو، الغرض منه وكيفية استخدامه في الاختبار

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

Fake هو تطبيق مبسط وعامل لتبعية تتصرف كمكون حقيقي، لكنها تستخدم تخزينا في الذاكرة أو آليات خفيفة أخرى بدلا من البنية التحتية للإنتاج. على عكس stub، يحتوي fake على منطق أعمال حقيقي — فرز، تصفية، تجميع — ولكن دون تأثيرات خارجية. قاعدة بيانات في الذاكرة بدلا من Room أو HashMap بدلا من SharedPreferences هي أمثلة كلاسيكية. المزيد من التفاصيل في تصنيف Martin Fowler لـ test doubles.

الرئيسية

  • Fake — تطبيق مبسط وعامل مع منطق حقيقي ولكن دون تبعيات خارجية
  • تخزين في الذاكرة — مستودع fake يخزن البيانات في HashMap بدلا من قاعدة البيانات
  • الفرق عن Stub — stub يعيد بيانات ثابتة، fake يحتوي على منطق قابل للتنفيذ
  • Android — InMemoryUserRepository كـ Fake لاختبار ViewModel و UseCase
  • iOS — FakeNetworkSession مع URLProtocol وبيانات اختبار بدلا من خادم حقيقي

ما هو Fake ولماذا هو مطلوب في الاختبار؟

Fake هو تطبيق كامل لكن خفيف لواجهة، مناسب للاختبار. تم تقديم المصطلح بواسطة Gerard Meszaros (2007) في كتاب «xUnit Test Patterns.» على عكس stub، الذي يعيد إجابات محددة مسبقا، يحتوي fake على كود قابل للتنفيذ: يمكنه فرز قائمة، تصفية حسب شرط، عد السجلات. الفرق الوحيد عن تطبيق الإنتاج هو أن fake يعمل مع بيانات في الذاكرة ولا يقوم بعمليات إدخال/إخراج حقيقية.

الميزة الرئيسية هي السرعة. يتم تنفيذ الاختبارات باستخدام fake خلال أجزاء من الثانية لأنه لا يوجد وصول إلى القرص أو الشبكة أو قاعدة البيانات. يعمل HashMap في الذاكرة أسرع 100–1000 مرة من Room أو CoreData. في نفس الوقت، يختبر fake منطق الأعمال الحقيقي: الفرز، التصفية، التجميع — كل ما لا يستطيع stub اختباره لأنه يعيد فقط ما قيل له. يمنح fake الثقة في أن الكود يعالج البيانات بشكل صحيح، بدلا من مجرد تلقي إجابة محددة مسبقا.

متى يكون Fake أفضل من Stub

Fake أفضل من Stub — إذا كان المكون قيد الاختبار يقوم بعمليات متعددة على البيانات (استرجاع، تصفية، فرز، حفظ)، فإن stub سيتطلب تكوين كل استدعاء على حدة. يحتوي fake على المنطق داخله — الاختبار ببساطة يستدعي الطرق ويتحقق من النتيجة. في IT Sectr، نستخدم fakes لجميع المستودعات في اختبارات الوحدة: مستودع fake مع HashMap يغطي 90% من السيناريوهات دون إعداد Mockito أو MockK.

Fake vs Stub vs Mock: متى تختار ماذا

معيار الاختيار — حدد ما يتحقق منه الاختبار: الحالة أم التفاعل. إذا كان الاختبار يتحقق من الحالة (نتيجة العمل) ويستخدم منطقا — استخدم fake. إذا كان الاختبار يحتاج فقط إلى بيانات إدخال دون منطق — يكفي stub. إذا كان الاختبار يتحقق من أن طريقة تم استدعاؤها — استخدم mock. خلط أنواع test doubles في اختبار واحد يعقد الفهم ويزيد الهشاشة.

المعيارFakeStubMock
يحتوي على منطقنعم (مبسط)لالا
السرعةعاليةقصوىعالية
التحقق من السلوكغير مباشرلانعم (verify)
الصيانةفئة واحدة لكل واجهةتكوين لكل اختبارتكوين لكل اختبار
الواقعيةعالية (الكود يعمل)منخفضة (بيانات ثابتة)متوسطة
خطر النتائج الإيجابية الخاطئةمنخفضمتوسطعالٍ (اختبارات هشة)

النمط المعاكس: Fake ليس fake — خطأ شائع عندما يطلق المطور على كائن fake وهو في الواقع stub أو mock. إذا كان InMemoryUserRepository الخاص بك لا يحتوي على منطق (تصفية، فرز) — فهو ليس fake، بل stub مع تخزين في الذاكرة. يختلف fake عن stub بوجود منطق قابل للتنفيذ. إذا كان مستودع fake ببساطة يعيد ما وضع فيه ولا يعالج البيانات — استخدم mock أو stub.

قاعدة عملية لاختيار Test Double

توصية عملية — ابدأ بـ fake لكل مستودع أو خدمة. إذا تجاوز fake 50 سطرا — قسمه إلى عدة فئات. إذا لم يكن هناك حاجة لـ fake على الإطلاق (الاختبار يتحقق فقط من سيناريو واحد ببيانات ثابتة) — استخدم stub. إذا كان الاختبار يتحقق من أن طريقة تم استدعاؤها بمعاملات محددة — استخدم mock. لا تحسن الاختيار مسبقا: اكتب fake، وإذا تبين أنه زائد عن الحاجة، استبدله بـ stub في اختبار محدد.

إنشاء كائنات Fake على Android لـ Room و Retrofit

مستودع fake لـ Room — مثال نموذجي لـ fake على Android. يستخدم تطبيق الإنتاج لـ UserRepository واجهة Room DAO مع استعلامات SQLite. النسخة fake تخزن البيانات في MutableList أو HashMap وتنفذ نفس الطرق: getUser(id)، saveUser(user)، deleteUser(id). يحتوي fake على منطق البحث والتصفية والفرز — نفس مستودع الإنتاج، لكن دون SQL. هذا يسمح باختبار ViewModel و UseCase دون إعداد قاعدة بيانات Room.

kotlin
class FakeUserRepository : UserRepository {

    private val users = mutableListOf<User>()

    override suspend fun getUser(id: String): User? {
        return users.find { it.id == id }
    }

    override suspend fun saveUser(user: User) {
        val index = users.indexOfFirst { it.id == user.id }
        if (index >= 0) users[index] = user
        else users.add(user)
    }

    override suspend fun search(query: String): List<User> {
        return users.filter {
            it.name.contains(query, ignoreCase = true)
        }
    }
}

Fake لواجهة Retrofit API — بدلا من MockWebServer (وهو stub، ليس fake)، يمكنك إنشاء تطبيق ApiService يعيد البيانات من مجموعة في الذاكرة. الفرق: MockWebServer يعترض HTTP ويعيد JSON، بينما يعمل fake ApiService على مستوى واجهة Kotlin دون تسلسل. fake أسرع (لا يوجد تحليل JSON) وأسهل في التصحيح (يعمل في نفس العملية، مقيد بالنوع). مناسب للاختبارات حيث دلالات HTTP (رموز الحالة، الرؤوس) ليست مهمة.

FakeSharedPreferences للاختبارات السريعة

— سيناريو شائع آخر. SharedPreferences في الإنتاج يكتب على القرص عبر commit/apply. النسخة fake تخزن أزواج المفتاح-القيمة في HashMap وتعيد البيانات فورا. تدعم نفس الطرق: getString، putString، getInt، putInt، clear. لـ Jetpack DataStore، النظير هو FakeDataStore مع تخزين في الذاكرة. هذه الـ fakes تسرع الاختبارات بعشرات المرات لأنه لا توجد عمليات كتابة على القرص.

تطبيقات Fake على iOS مع تخزين في الذاكرة

Fake في Swift — يُبنى من خلال البروتوكولات. تنفذ فئة الإنتاج البروتوكول مع منطق حقيقي (CoreData، URLSession). تنفذ البنية fake نفس البروتوكول مع تخزين في الذاكرة ومنطق مبسط. Swift هي لغة ذات دلالات قيمة، لذا فإن البنى fake غير قابلة للتغيير وآمنة في الاختبارات متعددة الخيوط. هذا يعطي ميزة على نظائر Android: لا حاجة لمزامنة الوصول إلى البيانات في الذاكرة.

swift
protocol UserRepositoryProtocol {
    func getUser(id: String) async -> User?
    func saveUser(user: User) async
}

final class FakeUserRepository: UserRepositoryProtocol {
    private var storage: [String: User] = [:]

    func getUser(id: String) async -> User? {
        return storage[id]
    }

    func saveUser(user: User) async {
        storage[user.id] = user
    }
}

final class UserViewModelTests: XCTestCase {
    func test_save_and_load() async {
        let fake = FakeUserRepository()
        let vm = UserViewModel(repository: fake)
        let user = User(id: "1", name: "Alice")

        await vm.saveUser(user)
        let loaded = await vm.getUser(id: "1")

        XCTAssertEqual(loaded?.name, "Alice")
    }
}

Fake لـ CoreData — في مشاريع iOS، يمكنك إنشاء NSPersistentContainer في الذاكرة عن طريق تعيين description.type = NSInMemoryStoreType. هذا مكدس CoreData كامل، لكن يعمل في الذاكرة. يسمح هذا fake باختبار NSFetchRequest و المسندات والفرز دون إنشاء ملف SQLite. السرعة: اختبارات CoreData في الذاكرة تعمل أسرع 5–10 مرات من النظير على القرص. العيب: تحتاج إلى إعداد NSManagedObjectModel في كل مرة.

FakeURLProtocol — فئة فرعية من URLProtocol لاعتراض طلبات الشبكة على iOS. يتم تسجيلها عبر URLProtocol.registerClass(fakeProtocol). داخليا تحتوي على قاموس URL -> Data في الذاكرة وتعيد البيانات دون طلب حقيقي. الفرق عن stub: يمكن لـ FakeURLProtocol التحقق من جسم الطلب والرؤوس وإرجاع ردود مختلفة حسب بيانات الإدخال. هذا هو fake لأنه يحتوي على منطق توجيه الطلبات.

أنماط استخدام Fake في مشاريع الجوال

Fake كـ Test Fixture — ضع فئات fake في وحدة اختبار مشتركة (androidTest/sharedTest أو TestSupport). كل الاختبارات في المشروع تستخدم نفس InMemoryUserRepository. هذا يلغي ازدواجية إعداد كائنات mock في كل اختبار ويضمن سلوكا موحدا. تغيير منطق fake يحدث كل الاختبارات في وقت واحد. في IT Sectr، نخزن فئات fake في sharedTest/java/com/itSectr/fake/ ونضمّنها عبر implementation project(:sharedTest).

Fake مع بيانات محددة مسبقا — غالبا ما تحتاج الاختبارات إلى مستودع يحتوي بالفعل على بعض السجلات. الحل: طريقة مصنع fakeWithData(vararg items) أو طريقة مدمجة addDefaultData(). ينشئ المصنع fake، يملؤه ببيانات نموذجية ويعيد كائنًا جاهزا للاستخدام. هذا يقلل من التكرار في الاختبارات: بدلا من إعداد استدعاءات mock، الاختبار ببساطة يستدعي FakeUserRepository.withUsers(alice, bob).

Fake مع عد الاستدعاءات — أحيانا تحتاج إلى التحقق ليس فقط من الحالة ولكن أيضا من عدد الاستدعاءات. يمكن أن يحتوي fake على عدادات: saveCallCount، getUserCallCount. يتحقق الاختبار من العداد بعد التنفيذ. هذا حل وسط بين fake النقي (التحقق من الحالة) و mock (التحقق من التفاعل). العدادات لا تتحقق من الوسائط أو ترتيب الاستدعاءات — فقط العدد. للتحقق من الوسائط، استخدم mock.

Fake مع Callback — لاختبار السيناريوهات غير المتزامنة، يمكن أن يقبل fake رد اتصال في كل استدعاء: beforeGetUser، afterSaveUser. هذا يسمح بمحاكاة التأخيرات والأخطاء أو التحقق من الحالات الوسيطة. هذا النهج مفيد لاختبار حالات التحميل في واجهة المستخدم: fake يتوقف لمدة 100 مللي ثانية، والاختبار يتحقق من أن الشاشة تظهر مؤشر التحميل. رد الاتصال غير موجود في الإنتاج — هذه وظيفة اختبارية بحتة.

الأسئلة المتكررة

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

Fake يحتوي على منطق عملي — يفلتر، يرتب، يحصي. Stub فقط يعيد إجابات محددة مسبقا دون منطق. إذا كان للكائن تفرعات (if/else, when) — فهو fake. إذا كان يحتوي فقط على قيم إرجاع — فهو stub. Fake أغلى في الصيانة لكنه يعطي اختبارات أكثر واقعية.

متى يمكن أن يكون fake ضارًا؟

عندما لا يتطابق منطق fake مع منطق الإنتاج. مثلا، FakeUserRepository يستخدم بحثا حساسا لحالة الأحرف، بينما نسخة الإنتاج غير حساسة لحالة الأحرف. الاختبار ينجح، لكن في الواقع يوجد خطأ. الحل: اختبر منطق fake بشكل منفصل أو استخدم fakes فقط للواجهات ذات المنطق البسيط (عمليات CRUD). للمنطق المعقد، اكتب اختبارات تكامل مع قاعدة بيانات حقيقية.

هل fake هو نفس قاعدة البيانات في الذاكرة؟

قاعدة بيانات في الذاكرة هي أحد أنواع fake. ينشئ Room.inMemoryDatabaseBuilder() SQLite في الذاكرة يتصرف مثل قاعدة بيانات الإنتاج. هذا fake كامل. لكن fake يمكن أن يكون أيضا على مستوى المستودع (بدون SQL) وعلى مستوى الشبكة (FakeApiService). قاعدة البيانات في الذاكرة هي حالة خاصة من fake حيث يكون المنطق أقرب ما يمكن إلى الحقيقي.

هل يمكن الجمع بين Fake و Mock في اختبار واحد؟

نعم، ولكن بحذر. Fake للمستودع (البيانات)، Mock لـ AnalyticsTracker (التحقق من الأحداث). فصل حسب الطبقات: fake لطبقة البيانات، mock لطبقة التحليلات/التسجيل. لا تجعل كائنا واحدا fake و mock في نفس الوقت — هذا ينتهك مبدأ المسؤولية الفردية ويسبب ارتباكا في الاختبار.

كيف تختبر Fake نفسه؟

اختبر fake بنفس الاختبارات التي تختبر بها تطبيق الإنتاج. إذا كان لديك UserRepositoryTest يتحقق من save، get، delete — قم بتشغيله مرتين: مع FakeUserRepository ومع RealUserRepository. هذا يضمن أن fake يكرر سلوك فئة الإنتاج. إذا بدأ fake في التصرف بشكل مختلف — فشل الاختبار في كلا التطبيقين.

الخلاصة

  • Fake — تطبيق مبسط وعامل لتبعية مع منطق أعمال حقيقي وتخزين في الذاكرة
  • الفرق عن Stub — fake يحتوي على منطق (تصفية، فرز)، stub فقط يعيد بيانات
  • السرعة — fake يعمل أسرع 100–1000 مرة من تطبيق الإنتاج دون عمليات إدخال/إخراج
  • Android — InMemoryUserRepository، FakeDataStore، Room في الذاكرة عبر Room.inMemoryDatabaseBuilder
  • iOS — fake قائم على البروتوكولات، CoreData في الذاكرة، FakeURLProtocol لاعتراض HTTP
  • أفضل ممارسة — ضع fakes في وحدة اختبار مشتركة واستخدمها في جميع اختبارات المشروع
  • اختبر fake — قم بتشغيل نفس الاختبارات على fake وتطبيق الإنتاج للتحقق من الاتساق

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

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

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

اقرأ أيضًا