TDD في Swift: كيفية كتابة كود لا يتعطل

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

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

ابدأ الدورة

«الاختبار أولاً — ثم الكود»: لماذا يعمل هذا في Swift؟

\n\n

هل سبق لك أن أعدت كتابة نفس الوحدة للمرة الثالثة لأنك «كنت بحاجة إلى إطلاقها بشكل عاجل»؟ أو هل واجهت خطأ في الإنتاج كان من الأسهل منعه في مرحلة الكتابة؟ إذا كان الأمر كذلك — مرحباً بك في عالم TDD (التطوير الموجه بالاختبارات).

\n\n

التطوير عبر الاختبارات ليس مجرد منهجية عصرية. إنها طريقة تفكير تجبرك على صياغة كيف يجب أن يتصرف الكود قبل أن تكتبه. في Swift، مع كتابتها الصارمة للأنواع وإطار العمل القوي XCTest، يتحول TDD إلى درع موثوق ضد الانحدارات والانهيارات غير المتوقعة.

\n\n

في هذه المقالة، سنشرح TDD بأمثلة واقعية، ونكتب اختبارات للطبقة الشبكية ونموذج البيانات، والأهم من ذلك — سنفهم كيفية تطبيق هذه الممارسة دون ألم. هيا بنا!

\n\n

أحمر-أخضر-إعادة هيكلة: سحر الدورة

\n\n

أساس TDD هو دورة من ثلاث خطوات:

\n\n
    \n
  • أحمر (Red) — نكتب اختباراً لا ينجح (لأن الكود غير موجود بعد).
  • \n
  • أخضر (Green) — نكتب الحد الأدنى من الكود ليجتاز الاختبار.
  • \n
  • إعادة هيكلة (Refactor) — نحسّن الكود دون تغيير سلوكه (الاختبارات تحمي).
  • \n
\n\n

يبدو بسيطاً؟ لنجرب على Swift.

\n\n

الاختبار الأول: التحقق من نموذج User

\n\n

لنفترض أن لدينا نموذج مستخدم بسيط. نريده أن يقوم بتنسيق الاسم الكامل بشكل صحيح. أولاً — الاختبار:

\n\n
import XCTest\n@testable import MyApp\n\nfinal class UserTests: XCTestCase {\n    \n    func testFullName_GivenFirstNameAndLastName_ReturnsFormattedString() {\n        // ترتيب\n        let user = User(firstName: "آنا", lastName: "كارنينا")\n        \n        // تنفيذ\n        let fullName = user.fullName()\n        \n        // تأكيد\n        XCTAssertEqual(fullName, "آنا كارنينا", "يجب أن يتكون الاسم الكامل من الاسم الأول واسم العائلة مفصولين بمسافة")\n    }\n}\n
\n\n

نشغّل — يفشل الاختبار (أحمر). المترجم يشتكي من أن User غير موجود. رائع، هذا هو المقصود.

\n\n

الآن نكتب الحد الأدنى من الكود لاجتياز الاختبار:

\n\n
struct User {\n    let firstName: String\n    let lastName: String\n    \n    func fullName() -> String {\n        return "\\(firstName) \\(lastName)"\n    }\n}\n
\n\n

نشغّل — أخضر! يمكننا الانتقال إلى إعادة الهيكلة. على سبيل المثال، إضافة معالجة السلاسل الفارغة أو الاسم الأوسط. لكن لا تتعجل — الاختبارات ستخبرك عندما يكون ذلك ضرورياً.

\n\n

اختبار الطبقة الشبكية غير المتزامنة

\n\n

واحدة من أكثر المشاكل شيوعاً في تطوير iOS هي العمل مع الشبكة. TDD مفيد هنا بشكل خاص، لأنه يفصل بوضوح المنطق عن استدعاءات الشبكة.

\n\n

كتابة بروتوكول وكائن mock

\n\n

أولاً، نحدد كيف يجب أن تبدو خدمتنا:

\n\n
protocol WeatherServiceProtocol {\n    func fetchWeather(for city: String, completion: @escaping (Result<Weather, Error>) -> Void)\n}\n
\n\n

الآن — الاختبار. لا نريد الاعتماد على API حقيقي، لذلك ننشئ mock:

\n\n
class MockWeatherService: WeatherServiceProtocol {\n    var shouldReturnError = false\n    var mockWeather: Weather?\n    \n    func fetchWeather(for city: String, completion: @escaping (Result<Weather, Error>) -> Void) {\n        if shouldReturnError {\n            completion(.failure(NetworkError.serverError))\n        } else if let weather = mockWeather {\n            completion(.success(weather))\n        }\n    }\n}\n\nfinal class WeatherViewModelTests: XCTestCase {\n    \n    func testFetchWeather_Success_UpdatesModel() {\n        // ترتيب\n        let mockService = MockWeatherService()\n        mockService.mockWeather = Weather(temperature: 25.0, condition: "مشمس")\n        let viewModel = WeatherViewModel(service: mockService)\n        let expectation = XCTestExpectation(description: "انتظار تحميل الطقس")\n        \n                

المدونات

توصيات الكتب