لماذا الاختبارات "الخضراء" ليست نصراً بعد؟
\n\nتخيل: لقد كتبت مئة اختبار وحدة، جميعها تمر بعلامة خضراء، خط أنابيب CI سعيد، والمدير راضٍ. ولكن بعد شهر، ينهار وظيفة حرجة في الإنتاج. قصة مألوفة؟ غالباً ما تكمن المشكلة في أننا نختبر سيناريوهات خاطئة أو نتخطى فروعاً كاملة من الكود.
\n\nتغطية الكود بالاختبارات (code coverage) ليست مجرد مقياس عصري للتقرير. إنها مصباحك اليدوي في غرفة مظلمة من عدم اليقين. عندما بدأت العمل مع C#، اعتقدت أن تغطية 80% هي المثالية. لكنني أدركت لاحقاً: يمكن أن يكون لديك تغطية 100% للأسطر ومع ذلك تفوت خطأ في منطق التفرع. لذلك سنفهم اليوم كيفية قياس التغطية في C# بشكل صحيح، وما هي الأدوات التي تساعد حقاً، وأي المقاييس يجب تتبعها.
\n\nما هي تغطية الكود ولماذا هي مهمة لمطور C#؟
\n\nببساطة، تغطية الكود تُظهر النسبة المئوية للكود المصدري لتطبيقك الذي تم تنفيذه أثناء تشغيل الاختبارات. إذا كتبت اختباراً يستدعي الدالة CalculateDiscount()، لكنه لا يتحقق من الفرع if (isPremiumUser)، فإن هذا الفرع يبقى "غير مغطى".
بالنسبة لمطور C#، التغطية هي مؤشر لأمرين:
\n- \n
- الثقة في إعادة الهيكلة — هل يمكن تغيير الكود دون الخوف من كسر شيء ما؟ \n
- اكتمال الاختبارات — هل نسينا الحالات الحدودية؟ \n
لكن هناك فارق مهم: تغطية الكود بالاختبارات في C# لا تضمن عدم وجود أخطاء. إنها تخبر فقط أن أسطراً معينة تم تنفيذها. لهذا السبب لا ننظر فقط إلى النسبة المئوية، بل إلى مقاييس محددة.
\n\nالمقاييس الرئيسية لتغطية الكود في .NET
\n\nعند تشغيل تحليل التغطية في C#، ستواجه عدة أنواع من المقاييس. دعنا نحلل كل منها بمثال على كود حقيقي.
\n\nLine Coverage (تغطية الأسطر)
\nأبسط مقياس. يحسب عدد أسطر الكود التي تم تغطيتها بالاختبارات. مثال:
\n\npublic int Add(int a, int b)\n{\n if (a < 0) // سطر 1\n return 0; // سطر 2\n return a + b; // سطر 3\n}\n\nإذا كان اختبارك يستدعي Add(5, 3)، فسيتم تنفيذ السطرين 1 و 3. سيبقى السطر 2 غير مغطى. ستظهر تغطية الأسطر 66% (2 من 3 أسطر).
Branch Coverage (تغطية التفرعات)
\nمقياس أكثر دقة. يأخذ في الاعتبار جميع المسارات الممكنة للتنفيذ. في مثالنا، هناك مساران: عندما a < 0 وعندما لا يكون كذلك. إذا اختبرت فقط حالة الرقم الموجب، ستكون تغطية التفرعات 50%.
غالباً ما تكشف تغطية التفرعات "الثغرات" في الاختبارات. أوصي بالسعي لتحقيق 80%+ وفقاً لهذا المقياس تحديداً.
\n\nCyclomatic Complexity وعلاقتها بالتغطية
\nعلى الرغم من أنه ليس مقياس تغطية مباشراً، إلا أن التعقيد الحلقي يوضح عدد الاختبارات التي يجب كتابتها للتغطية الكاملة. كلما زاد تعقيد الدالة، زادت الحاجة إلى اختبارات أكثر. إذا رأيت دالة بتعقيد حلقي 15 وتغطية 90% — فمن المحتمل أنك لا تختبر جميع السيناريوهات.
\n\nأدوات قياس تغطية الكود في C#
\n\nفي نظام .NET البيئي، هناك عدة أدوات قوية. سأخبرك عن الثلاثة الأكثر شيوعاً التي عملت بها بنفسي.
\n\nCoverlet — مجاني ومدمج في .NET
\nإذا كنت تستخدم .NET 6+، فإن Coverlet متاح بالفعل عبر NuGet. هذه أداتي المفضلة لـ CI/CD لأنها لا تتطلب تثبيت برامج إضافية.
\n\nالتثبيت:
\ndotnet add package coverlet.msbuild\ndotnet add package coverlet.collector\n\nالتشغيل مع تقرير:
\ndotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover\n\nستحصل على ملف coverage.opencover.xml، والذي يمكن تصوره باستخدام ReportGenerator:
dotnet tool install -g dotnet-reportgenerator-globaltool\nreportgenerat