كيف يقوم go test بتشغيل جميع اختبارات المشروع: تحليل تحت الغطاء

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

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

ابدأ الدورة

كيفية تشغيل جميع الاختبارات في مشروع Go وماذا يحدث "تحت الغطاء"؟

\n

كل مطور Go قام مرة واحدة على الأقل في حياته بكتابة الأمر السحري go test ./... في الطرفية وشاهد علامات الاختيار الخضراء تجري عبر الشاشة. ولكن هل تساءلت يومًا عما يحدث بالفعل في تلك اللحظة؟ لماذا يتم تشغيل الاختبارات بهذه الطريقة وليس بطريقة أخرى؟ ولماذا أحيانًا يتخطى go test بصمت اختباراتك الجديدة؟

\n

في هذه المقالة، لن نجيب فقط على السؤال الشائع من Stack Overflow (حيث حصل على آلاف المشاهدات)، بل سنلقي نظرة تحت غطاء مترجم Go ونتعرف على كيفية عمل آلية الاختبار. هيا بنا!

\n\n

لماذا go test ./... هو السحر؟

\n

لنبدأ بالأساسيات. الأمر go test بدون وسائط يقوم بتشغيل الاختبارات فقط في الحزمة الحالية. ولكن إذا كان لديك مشروع من عشرات الحزم (وهذا هو الحال في الإنتاج الفعلي)، فإن تصفحها يدويًا يشبه التعذيب في العصور الوسطى. هنا يأتي دور ./....

\n

هذا النمط يعني: "ابحث عن جميع الحزم بشكل تكراري، بدءًا من الدليل الحالي". يقوم Go باجتياز شجرة الدلائل، متجاهلاً vendor والوحدات النمطية، ويجمع قائمة بجميع الحزم. لكل منها، يقوم بتشغيل نسخة منفصلة من go test.

\n

ولكن ماذا لو كنت تريد تشغيل الاختبارات فقط في أدلة معينة؟ على سبيل المثال، فقط في ./internal/...؟ يعمل بنفس الطريقة: go test ./internal/....

\n

هام: إذا كنت تستخدم Go Modules (وهو المعيار منذ Go 1.16+)، فإن ./... يعمل فقط ضمن نطاق الوحدة النمطية الحالية. لاختبار جميع الوحدات النمطية في مساحة العمل، سيتعين عليك استخدام go test ./... في كل منها أو إعداد go.work.

\n\n

وما هي "الحزمة" في سياق go test؟

\n

يعمل Go بمفهوم الحزمة — مجموعة من ملفات .go في دليل واحد. ملفات الاختبار (باللاحقة _test.go) تعتبر أيضًا جزءًا من الحزمة، ولكن يتم تجميعها بشكل منفصل. عند تشغيل go test، يقوم Go بتجميع ملف ثنائي للاختبار يتضمن:

\n
    \n
  • الكود المصدري للحزمة (ولكن فقط ما يتم استخدامه في الاختبارات)
  • \n
  • كود الاختبارات نفسها
  • \n
  • غلاف خاص لوقت التشغيل للتشغيل والتقرير
  • \n
\n

يتم تشغيل هذا الملف الثنائي، وينفذ الاختبارات ويعرض النتيجة. العملية برمتها منفصلة لكل حزمة.

\n\n

كيف يقرر Go أي الاختبارات يجب تشغيلها؟

\n

عند كتابة go test ./...، يقوم Go بتنفيذ الخطوات التالية:

\n
    \n
  1. اجتياز نظام الملفات — يجد Go جميع الدلائل التي تحتوي على ملفات .go.
  2. \n
  3. التصفية — يتم تجاهل دلائل vendor و testdata والمجلدات المخفية (التي تبدأ بـ .).
  4. \n
  5. التجميع — يتم إنشاء ملف ثنائي مؤقت لكل حزمة.
  6. \n
  7. التشغيل — يتم تنفيذ الملف الثنائي، ويتم جمع النتائج.
  8. \n
  9. التخزين المؤقت — إذا لم يتغير الكود، يستخدم Go ذاكرة التخزين المؤقت ويعرض (cached).
  10. \n
\n

النقطة الأخيرة هي سبب شائع للدهشة. لقد غيرت الاختبار، وتقوم بتشغيل go test ./...، فيقول "ok (cached)". لماذا؟ لأن Go يخزن النتائج مؤقتًا بناءً على تجزئة ملفات الإدخال. إذا قمت بتغيير تعليق فقط أو استيراد غير مستخدم، فإن التجزئة لا تتغير، ولا يتم إعادة تشغيل الاختبار.

\n

نصيحة مفيدة: يمكنك إعادة تعيين ذاكرة التخزين المؤقت قسرًا باستخدام الأمر go clean -testcache. أو استخدم العلم -count=1 الذي يجبر Go على تجاهل ذاكرة التخزين المؤقت: go test -count=1 ./....

\n\n

التوازي: لماذا الاختبارات ليست دائمًا أسرع؟

\n

افتراضيًا، يقوم go test بتشغيل الاختبارات ضمن حزمة واحدة بشكل تسلسلي. ولكن بين الحزم — بالتوازي! يتم تحديد عدد الحزم المتزامنة بواسطة العلم -p (الافتراضي يساوي عدد وحدات المعالجة المركزية).

\n

يبدو هذا رائعًا، ولكن هناك فارق بسيط: ه

المدونات

توصيات الكتب