الموك، الـ Stubs والـ Fixtures في Swift: كيفية الاختبار بدون ألم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

الاختبار في Swift: من النظرية إلى التطبيق مع الموك والـ Fixtures

\n\n

تخيل أنك تخبز كعكة ونفد الدقيق لديك. لا يمكنك التحقق مما إذا كانت الكريمة ستنجح حتى تذهب إلى المتجر. يشعر المطور بنفس الشعور تقريبًا عندما يكتب اختبارات وحدة لكود يتفاعل مع الشبكة أو قاعدة البيانات أو التبعيات المعقدة. بدلاً من انتظار خادم حقيقي أو قاعدة بيانات حقيقية، نستخدم mock-الكائنات و fixtures في اختبارات Swift — وهي "بدائل الدقيق" لدينا، والتي تسمح لنا باختبار أجزاء معزولة من الكود هنا والآن.

\n\n

في هذه المقالة، لن نقتصر على سرد التعريفات. سنقوم بتحليل كيفية تطبيق اختبار Swift بشكل صحيح عمليًا، ومتى نستخدم الموك، ومتى نستخدم الـ stubs، وكيف تساعد الـ fixtures في عدم فقدان العقل عند إعداد البيانات. هيا بنا!

\n\n

ما هو mock و stub و fixture؟ فهم المصطلحات

\n\n

قبل كتابة الكود، دعونا نتفق على المفاهيم. في عالم اختبارات الوحدة لنظام iOS، غالبًا ما يتم الخلط بين الموك والـ stubs. تذكر قاعدة بسيطة:

\n\n
    \n
  • Stub (وصلة) — كائن يعيد بيانات محددة مسبقًا. لا يتحقق من كيفية استدعائه. فقط يعيد الرد.
  • \n
  • Mock (محاكاة) — كائن لا يعيد البيانات فحسب، بل يتحقق أيضًا مما إذا تم استدعاء طرق معينة وبأي معاملات. إنه "جاسوس".
  • \n
  • Fixture (بيانات ثابتة) — مجموعة بيانات مُعدة مسبقًا (عادةً JSON أو هيكل) تُستخدم في الاختبارات لإنشاء بيئة يمكن التنبؤ بها.
  • \n
\n\n

في اختبار Swift، غالبًا ما نجمع بين هذه الأساليب. على سبيل المثال، نقوم بتحميل fixture بصيغة JSON، ونقوم بتحليلها، ثم نستبدل طبقة الشبكة بـ mock يتحقق من إرسال الطلب إلى عنوان URL الصحيح.

\n\n

كتابة أول mock في Swift باستخدام البروتوكولات

\n\n

الطريقة الأكثر شيوعًا لإنشاء mock-كائنات في Swift هي استخدام البروتوكولات. في مشروع حقيقي، لديك خدمة تتصل بالشبكة. في الاختبارات، تستبدلها بـ mock.

\n\n

إليك مثال على التجريد:

\n\n
// بروتوكول لطبقة الشبكة\nprotocol NetworkServiceProtocol {\n    func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void)\n}\n\n// التنفيذ الحقيقي (يتصل بالشبكة)\nclass RealNetworkService: NetworkServiceProtocol {\n    func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void) {\n        // URLSession, Alamofire إلخ.\n    }\n}\n\n// Mock للاختبارات\nclass MockNetworkService: NetworkServiceProtocol {\n    var fetchUsersCalled = false\n    var resultToReturn: Result<[User], Error> = .success([])\n\n    func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void) {\n        fetchUsersCalled = true\n        completion(resultToReturn)\n    }\n}
\n\n

الآن في الاختبار يمكننا التحقق من استدعاء الطريقة:

\n\n
func testViewModelFetchesUsers() {\n    let mockService = MockNetworkService()\n    mockService.resultToReturn = .success([User(id: 1, name: "Test")])\n\n    let viewModel = UserViewModel(networkService: mockService)\n    viewModel.loadUsers()\n\n    XCTAssertTrue(mockService.fetchUsersCalled, "يجب استدعاء طريقة fetchUsers")\n}
\n\n

لاحظ: نحن نستخدم حقن التبعية في Swift — نمرر التبعية عبر المُهيئ. هذا هو النمط الأساسي للاختبار.

\n\n

الـ Fixtures: كيف لا تكتب 100500 سطر في الاختبارات

\n\n

عند اختبار تحليل JSON أو العمل مع CoreData، تحتاج إلى بيانات حقيقية. لكن توليدها في كل مرة في الكود هو جحيم. هنا تأتي fixtures في اختبارات Swift للإنقاذ.

\n\n

أنشئ في مجلد الاختبار ملفًا باسم users_fixture.json:

\n\n
[\n    {"id": 1, "name": "Alice", "email": "alice@example.com"},\n    {"id": 2, "name": "Bob", "email": "bob@example.com"}\n]
\n\n

الآن

المدونات

توصيات الكتب