مقدمة: لماذا TDD ليس دينًا، بل طوق نجاة
\n\nتخيل أنك تصلح أسلاكًا كهربائية في منزل قديم. أنت تطفئ القاطع الرئيسي، لكنك لا تزال تخشى وضع يديك في صندوق التوزيع. هكذا يشعر المطور تقريبًا عندما يُجري تغييرات في مشروع بدون اختبارات. خاصة في Go - لغة حيث الكتابة الثابتة تعطي شعورًا زائفًا بالأمان.
\n\nالتطوير من خلال الاختبارات (TDD) في Go ليس موضة عابرة ولا طقسًا طائفيًا. إنها منهجية تحول الخوف من إعادة الهيكلة إلى ثقة. وفقًا لدراسة IBM، تُظهر المشاريع التي تستخدم TDD عيوبًا أقل بنسبة 40%. وفي Go، حيث يقوم المترجم بالفعل باستبعاد نصف الأخطاء، يكمل TDD القضاء على الـ 50% المتبقية.
\n\nفي هذه المقالة، لن نلقي محاضرات. سنأخذ مهمة حقيقية: سنكتب خدمة HTTP صغيرة لتقصير الروابط. وسنمر بمسار TDD بأكمله - من الاختبار الأحمر إلى الأخضر وإعادة الهيكلة. هيا بنا!
\n\nما هو TDD حقًا؟
\n\nإذا كنت قد سمعت عن Red-Green-Refactor - فأنت بالفعل على دراية. إذا لم تكن قد سمعت - تذكر ثلاث خطوات بسيطة:
\n\n- \n
- Red - نكتب اختبارًا يفشل مسبقًا (لأن الوظيفة غير موجودة بعد). \n
- Green - نكتب الحد الأدنى من الكود لاجتياز الاختبار. ليس مثاليًا، بل مجرد كود يعمل. \n
- Refactor - نحسن الكود دون تغيير سلوكه. الاختبارات هي شبكة الأمان لدينا. \n
في Go، هذه الدورة ممتعة بشكل خاص بفضل الحزمة المدمجة testing. لست بحاجة لتثبيت jest أو pytest - كل شيء موجود بالفعل في المكتبة القياسية. لكن العديد من المبتدئين يتجاهلون الاختبارات، معتقدين أن Go "آمنة" بطبيعتها. وهذا خطأ.
لماذا يعمل TDD في Go بشكل أفضل من اللغات الأخرى؟
\n\nمجتمع Go يعشق البساطة. هنا لا يتحول TDD إلى بيروقراطية مع الـ mocks والـ stubs. بدلاً من ذلك، تكتب اختبارات جدولية (table-driven tests) - مدمجة، قابلة للقراءة، ومفهومة. حالة اختبار واحدة - سطر واحد. ونعم، اختبارات Go تعمل بسرعة. ميلي ثانية لمئات الحالات - أمر طبيعي.
\n\nبالمناسبة، في المدونة الرسمية لـ Go تم التأكيد مرارًا: "الاختبارات هي توثيق". إذا كان اختبارك يُقرأ كمواصفات - فأنت تفعل كل شيء بشكل صحيح.
\n\nإعداد البيئة: ما تحتاجه للبدء
\n\nأفترض أن Go مثبتة لديك بالفعل. إذا لم تكن كذلك - brew install go أو apt install golang-go. تحقق من الإصدار: go version. للمقالة، يكفي Go 1.21+.
لننشئ المشروع:
\n\nmkdir url-shortener-tdd\ncd url-shortener-tdd\ngo mod init url-shortener\n\nجاهز. لا أطر عمل - فقط المكتبة القياسية والتطوير من خلال الاختبارات Go.
\n\nنكتب أول اختبار: Red
\n\nلنفترض أننا نريد دالة Shorten(url string) string، التي تحول رابطًا طويلًا إلى تجزئة قصيرة. هي غير موجودة بعد - وهذا رائع. لنكتب الاختبار:
// shortener_test.go\npackage main\n\nimport "testing"\n\nfunc TestShorten(t *testing.T) {\n url := "https://pythonlib.ru/very/long/path?with=params&and=stuff"\n result := Shorten(url)\n\n if len(result) == 0 {\n t.Error("Shorten أعاد سلسلة فارغة")\n }\n\n if len(result) > 10 {\n t.Errorf("توقعت تجزئة قصيرة، حصلت على %d حرفًا", len(result))\n }\n}\n\nنشغل: go test -v. نرى الأحمر (تقنيًا - خطأ في الترجمة). الدالة Shorten غير موجودة. ممتاز، نحن في مرحلة Red.
الحد الأدنى من الكود: Green
\n\nالآن نكتب بالضبط ما يكفي لاجتياز الاختبار. لا تشفير معقد، مجرد تجزئة:
\n\n// shortener.go\npackage main\n\nimport (\n "crypto/sha256"\n "fmt"\n)\n\nfunc Shorten(url string) string {\n hash := sha256.Sum256([]byte(url))\n return fmt.Sprintf("%x", hash[:4]) // نأخذ أول 4 بايتات\n}\n\nنشغل الاختبار مرة أخرى. أخضر؟ رائع. لكن الكود سيء - التجزئة قابلة للتصادم، ونحن لم نتحقق حتى من م