كيف تلتقط استثناءً في MSTest دون أن تصاب بالجنون؟
\n\nأنت تكتب اختبارات وحدة بلغة C# وتفاجأ فجأة: يجب أن تُلقي طريقتك استثناءً في موقف معين. كيف تتحقق من ذلك؟ إذا كنت قد بحثت في Google عن "How do I use Assert to verify that an exception has been thrown with MSTest"، فأنت لست وحدك - هذا أحد أكثر الأسئلة شيوعًا على Stack Overflow. واليوم سنحل هذه المسألة نهائيًا.
\n\nفي هذه المقالة، سأعرض لكم جميع طرق التحقق من الاستثناءات في MSTest: من السمة الكلاسيكية إلى الطريقة الحديثة Assert.ThrowsException. ستتعلم كيفية كتابة اختبارات نظيفة وموثوقة لن تفشل في أسوأ الأوقات. هيا بنا!
لماذا التحقق من الاستثناءات مهم؟
\n\nتخيل: لديك طريقة Divide(int a, int b). إذا كانت b = 0، يجب أن تُلقي DivideByZeroException. بدون اختبار، لن تعلم بذلك إلا عندما يتعطل التطبيق في بيئة الإنتاج. اختبار الوحدة مع التحقق من الاستثناء هو حبل الأمان الخاص بك.
في MSTest (إطار اختبار الوحدات من Microsoft) هناك عدة طرق. سنستعرض كلًا منها، لكننا سنبدأ بالأحدث والأكثر تفضيلًا.
\n\nالطريقة 1: Assert.ThrowsException (الأكثر صحة)
\n\nبدءًا من MSTest V2 (الإصدار 2.x)، لدينا طريقة مدمجة Assert.ThrowsException<T>. إنها أنيقة، قابلة للقراءة، ومرنة. الصيغة بسيطة:
[TestMethod]\npublic void Divide_ByZero_ThrowsDivideByZeroException()\n{\n // Arrange\n var calculator = new Calculator();\n\n // Act & Assert\n Assert.ThrowsException<DivideByZeroException>(() => calculator.Divide(10, 0));\n}\n\nماذا يحدث هنا؟ نمرر تعبير لامدا باستدعاء الطريقة. يقوم MSTest بتنفيذه داخل كتلة try-catch. إذا لم يتم إلقاء الاستثناء - يفشل الاختبار. إذا تم إلقاء استثناء من نوع مختلف - يفشل أيضًا. فقط الاستثناء المطلوب يؤدي إلى النجاح.
\n\nمثال متقدم: التحقق من رسالة الاستثناء
\n\nأحيانًا تحتاج إلى التأكد ليس فقط من نوع الاستثناء، ولكن أيضًا من رسالته. للقيام بذلك، استخدم التحميل الزائد مع المعامل message أو تحقق من خصائص كائن الاستثناء:
[TestMethod]\npublic void GetUserById_InvalidId_ThrowsWithCorrectMessage()\n{\n // Arrange\n var service = new UserService();\n\n // Act\n var exception = Assert.ThrowsException<ArgumentException>(() => service.GetUserById(-1));\n\n // Assert إضافي\n Assert.AreEqual("Id must be positive", exception.Message);\n}\n\nلاحظ: Assert.ThrowsException يُرجع كائن الاستثناء! هذا يسمح لك بالتحقق من أي من خصائصه: InnerException، Data، StackTrace (على الرغم من أن الأخير لا يتم التحقق منه عادةً).
الطريقة 2: السمة [ExpectedException] (قديمة ولكن لا تزال مستخدمة)
\n\nسابقًا (في MSTest V1) كانت تُستخدم سمة فوق طريقة الاختبار نفسها. كانت تبدو هكذا:
\n\n[TestMethod]\n[ExpectedException(typeof(DivideByZeroException))]\npublic void Divide_ByZero_ThrowsDivideByZeroException_OldWay()\n{\n var calculator = new Calculator();\n calculator.Divide(10, 0);\n}\n\nيبدو مريحًا؟ لكن هناك ثلاث مشاكل:
\n- \n
- لا يمكن التحقق من رسالة الاستثناء. لا تعرف ما إذا كان الاستثناء الصحيح قد أُلقي. \n
- أي استثناء من هذا النوع يعتبر نجاحًا. حتى لو ظهر من سطر كود آخر (مثل في Arrange). \n
- لا يمكن تنفيذ كود بعد الاستثناء. تتوقف طريقة الاختبار فورًا. \n
لذلك في المشاريع الجديدة، استخدم Assert.ThrowsException. السمة [ExpectedException] مُعلَمة على أنها مهملة (deprecated) وقد تُحذف في الإصدارات المستقبلية.
الطريقة 3: try-catch يدوي (للمغامرين)
\n\nأحيانًا (نادرًا جدًا) تحتاج إلى