سؤال

أستخدم Assert.Fail كثيرًا عند القيام بـ TDD.عادةً ما أعمل على اختبار واحد في كل مرة، ولكن عندما أحصل على أفكار لأشياء أريد تنفيذها لاحقًا، أكتب بسرعة اختبارًا فارغًا حيث يشير اسم طريقة الاختبار إلى ما أريد تنفيذه كنوع من قائمة المهام.للتأكد من أنني لا أنسى أنني وضعت Assert.Fail() في الجسم.

عند تجربة xUnit.Net وجدت أنهم لم ينفذوا Assert.Fail.بالطبع يمكنك دائمًا Assert.IsTrue(false) ولكن هذا لا يعبر عن نيتي أيضًا.حصلت على انطباع بأن Assert.Fail لم يتم تنفيذه عن قصد.هل تعتبر هذه ممارسة سيئة؟إذا كان الأمر كذلك لماذا؟


Martin Meredith هذا ليس بالضبط ما أفعله.أقوم بكتابة اختبار أولاً ثم تنفيذ التعليمات البرمجية لجعله يعمل.عادة أفكر في عدة اختبارات في وقت واحد.أو أفكر في اختبار للكتابة عندما أعمل على شيء آخر.وذلك عندما أكتب اختبار فشل فارغًا للتذكر.بحلول الوقت الذي بدأت فيه كتابة الاختبار، كنت أعمل على الاختبار أولاً بدقة.

jimmeh التي تبدو فكرة جيدة.لا تفشل الاختبارات التي تم تجاهلها ولكنها تظهر في قائمة منفصلة.يجب أن تجرب ذلك.

Matt Howells فكرة رائعة.ينقل NotImplementedException النية بشكل أفضل من Accept.Fail() في هذه الحالة

Mitch Wheat هذا ما كنت أبحث عنه.يبدو أنه تم استبعاده لمنع إساءة استخدامه بطريقة أخرى أسيء إليه.

هل كانت مفيدة؟

المحلول

بالنسبة لهذا السيناريو، بدلاً من استدعاء Assert.Fail، أقوم بما يلي (في C# / NUnit)

[Test]
public void MyClassDoesSomething()
{
    throw new NotImplementedException();
}

وهو أكثر وضوحًا من Assert.Fail.

يبدو أن هناك اتفاقًا عامًا على أنه من الأفضل استخدام تأكيدات أكثر وضوحًا من Assert.Fail().يتعين على معظم الأطر تضمينها لأنها لا تقدم بديلاً أفضل.على سبيل المثال، توفر NUnit (وغيرها) خاصية ExceptedExceptionAttribute لاختبار ما إذا كانت بعض التعليمات البرمجية تطرح فئة معينة من الاستثناءات.ومع ذلك، من أجل اختبار ما إذا كانت الخاصية الموجودة في الاستثناء قد تم تعيينها على قيمة معينة، لا يمكن للمرء استخدامها.بدلاً من ذلك عليك اللجوء إلى Assert.Fail:

[Test]
public void ThrowsExceptionCorrectly()
{
    const string BAD_INPUT = "bad input";
    try
    {
        new MyClass().DoSomething(BAD_INPUT);
        Assert.Fail("No exception was thrown");
    }
    catch (MyCustomException ex)
    {
         Assert.AreEqual(BAD_INPUT, ex.InputString); 
    }
}

تجعل طريقة xUnit.Net Assert.Throws هذا الأمر أكثر دقة دون الحاجة إلى طريقة Assert.Fail.من خلال عدم تضمين طريقة Assert.Fail()، تشجع xUnit.Net المطورين على البحث عن بدائل أكثر وضوحًا واستخدامها، ودعم إنشاء تأكيدات جديدة عند الضرورة.

نصائح أخرى

لقد تم استبعاده عمدا.هذا هو رد براد ويلسون عن سبب عدم وجود Assert.Fail():

نحن لم نغفل هذا، في الواقع.أجد Assert.fail عكازًا يعني أنه ربما يكون هناك تأكيد مفقود.في بعض الأحيان يكون هذا هو الطريقة التي يتم بها تنظيم الاختبار ، وأحيانًا لأن التأكيد يمكن أن يستخدم تأكيدًا آخر.

لقد استخدمت دائمًا Assert.Fail() للتعامل مع الحالات التي اكتشفت فيها أن الاختبار يجب أن يفشل من خلال منطق يتجاوز مقارنة القيمة البسيطة.كمثال:

try 
{
  // Some code that should throw ExceptionX
  Assert.Fail("ExceptionX should be thrown")
} 
catch ( ExceptionX ex ) 
{
  // test passed
}

وبالتالي فإن عدم وجود Assert.Fail() في الإطار يبدو وكأنه خطأ بالنسبة لي.أقترح تصحيح فئة Assert لتشمل طريقة Fail()، ثم إرسال التصحيح إلى مطوري إطار العمل، بالإضافة إلى أسباب إضافته.

أما بالنسبة لممارستك لإنشاء اختبارات تفشل عمدًا في مساحة العمل الخاصة بك، لتذكير نفسك بتنفيذها قبل الالتزام بها، فهذا يبدو بمثابة ممارسة جيدة بالنسبة لي.

أستخدم MbUnit لاختبار وحدتي.لديهم خيار تجاهل الاختبارات، التي تظهر باللون البرتقالي (بدلاً من الأخضر أو ​​الأحمر) في مجموعة الاختبار.ربما تحتوي xUnit على شيء مشابه، وهذا يعني أنك لا تحتاج حتى إلى وضع أي تأكيد في الطريقة، لأنها ستظهر بلون مختلف بشكل مزعج مما يجعل من الصعب تفويتها؟

يحرر:

في MbUnit يتم ذلك بالطريقة التالية:

[Test]
[Ignore]
public void YourTest()
{ } 

هذا هو النمط الذي أستخدمه عند كتابة اختبار للتعليمات البرمجية التي أرغب في طرح استثناء حسب التصميم:

[TestMethod]
public void TestForException()
{
    Exception _Exception = null;

    try
    {
        //Code that I expect to throw the exception.
        MyClass _MyClass = null;
        _MyClass.SomeMethod();
        //Code that I expect to throw the exception.
    }
    catch(Exception _ThrownException)
    {   
        _Exception = _ThrownException
    }
    finally
    {
        Assert.IsNotNull(_Exception);
        //Replace NullReferenceException with expected exception.
        Assert.IsInstanceOfType(_Exception, typeof(NullReferenceException));
    }
}

IMHO هذه طريقة أفضل لاختبار الاستثناءات عبر استخدام Assert.Fail().والسبب في ذلك هو أنني لا أقوم باختبار الاستثناء على الإطلاق فحسب، بل أقوم أيضًا باختبار نوع الاستثناء.أدرك أن هذا مشابه لإجابة Matt Howells لكن IMHO باستخدام الكتلة الأخيرة أكثر قوة.

من الواضح أنه لا يزال من الممكن تضمين أساليب تأكيد أخرى لاختبار سلسلة إدخال الاستثناءات وما إلى ذلك.سأكون ممتنا لتعليقاتكم ووجهات نظركم حول نمطي.

شخصيًا، ليس لدي أي مشكلة في استخدام مجموعة الاختبار كقائمة مهام كهذه طالما أنك ستتمكن في النهاية من كتابة الاختبار قبل قمت بتنفيذ التعليمات البرمجية لتمرير.

بعد قولي هذا، كنت أستخدم هذا الأسلوب بنفسي، على الرغم من أنني الآن أجد أن القيام بذلك يقودني إلى طريق كتابة الكثير من الاختبارات مقدمًا، وهو ما يشبه بطريقة غريبة المشكلة العكسية المتمثلة في عدم كتابة الاختبارات على الإطلاق:ينتهي بك الأمر إلى اتخاذ قرارات بشأن التصميم في وقت مبكر جدًا IMHO.

وبالمناسبة، في MSTest، يستخدم قالب الاختبار القياسي Assert.Inconclusive في نهاية عيناته.

AFAIK تم تصميم إطار عمل xUnit.NET ليكون خفيف الوزن للغاية، وقد قاموا بقطع الفشل عمدًا، لتشجيع المطور على استخدام شرط فشل واضح.

تخمين جامح:يهدف حجب Assert.Fail إلى منعك من التفكير في أن الطريقة الجيدة لكتابة كود الاختبار هي بمثابة كومة ضخمة من السباغيتي تؤدي إلى Assert.Fail في الحالات السيئة.[تعديل للإضافة:أجوبة الآخرين تؤكد ذلك على نطاق واسع، ولكن مع الاقتباسات]

نظرًا لأن هذا ليس ما تفعله، فمن المحتمل أن يكون xUnit.Net مفرطًا في الحماية.

أو ربما يعتقدون فقط أنه نادر جدًا وغير متعامد لدرجة أنه غير ضروري.

أفضل تنفيذ وظيفة تسمى ThisCodeHasNotBeenWrittenYet (في الواقع شيء أقصر، لسهولة الكتابة).لا يمكنك إيصال النية بشكل أكثر وضوحًا من ذلك، ولديك مصطلح بحث دقيق.

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

باستخدام الكود الجيد أفعل عادةً ما يلي:

void goodCode() {
     // TODO void goodCode()
     throw new NotSupportedOperationException("void goodCode()");
}

باستخدام رمز الاختبار أفعل عادةً ما يلي:

@Test
void testSomething() {
     // TODO void test Something
     Assert.assert("Some descriptive text about what to test")
}

إذا كنت تستخدم JUnit، ولا ترغب في الحصول على الفشل، ولكن الخطأ، فعادةً ما أفعل:

@Test
void testSomething() {
     // TODO void test Something
     throw new NotSupportedOperationException("Some descriptive text about what to test")
}

احذر Assert.Fail وتأثيرها المفسد في جعل المطورين يكتبون اختبارات سخيفة أو معطلة.على سبيل المثال:

[TestMethod]
public void TestWork()
{
    try {
        Work();
    }
    catch {
        Assert.Fail();
    }
}

وهذا أمر سخيف، لأن محاولة الالتقاط زائدة عن الحاجة.يفشل الاختبار إذا ألقى استثناءً.

أيضًا

[TestMethod]
public void TestDivide()
{
    try {
        Divide(5,0);
        Assert.Fail();
    } catch { }
}

إذا تم كسر هذا، فسوف ينجح الاختبار دائمًا مهما كانت نتيجة وظيفة Divide.مرة أخرى، يفشل الاختبار إذا وفقط إذا ألقى استثناءً.

لماذا تستخدم Assert.Fail لقول أنه يجب طرح الاستثناء؟هذا غير ضروري.لماذا لا تستخدم فقط سمة ExceptedException؟

إذا كنت تكتب اختبارًا يفشل، ثم تكتب الكود الخاص به، فاكتب الاختبار.هذا ليس تطويرًا قائمًا على الاختبار.

من الناحية الفنية، لن تكون هناك حاجة إلى Assert.fail() إذا كنت تستخدم التطوير القائم على الاختبار بشكل صحيح.

هل فكرت في استخدام قائمة المهام، أو تطبيق منهجية GTD على عملك؟

اختبار MS لديه تأكيد.فشل () ولكن لديها أيضا تأكيد.غير حاسم().أعتقد أن الاستخدام الأنسب لـ Assert.Fail() هو إذا كان لديك بعض المنطق المضمن الذي قد يكون من الصعب وضعه في تأكيد، على الرغم من أنني لا أستطيع حتى التفكير في أي أمثلة جيدة.بالنسبة للجزء الأكبر، إذا كان إطار الاختبار يدعم شيئًا آخر غير Assert.Fail() فاستخدمه.

أعتقد أنه يجب عليكم أن تسألوا أنفسكم ما الذي يجب أن يفعله الاختبار (المقدم).

أولاً، تكتب (مجموعة) اختبارًا بدون تنفيذ.ربما، أيضًا سيناريوهات الأيام الممطرة.

يجب أن تفشل جميع هذه الاختبارات، لتكون اختبارات صحيحة:لذلك تريد تحقيق شيئين:1) التحقق من صحة التنفيذ الخاص بك؛2) تأكد من صحة اختبارات وحدتك.

الآن، إذا قمت بإجراء TDD مقدمًا، فأنت تريد تنفيذ جميع اختباراتك، وكذلك أجزاء NYI.يتم اجتياز نتيجة التشغيل التجريبي الإجمالي إذا:1) تنجح جميع الأشياء التي تم تنفيذها 2) فشلت جميع أشياء NYI

بعد كل شيء، سيكون إغفال اختبار الوحدة إذا نجحت اختبارات الوحدة الخاصة بك في حين لا يوجد تنفيذ، أليس كذلك؟

تريد أن ينتهي بك الأمر بشيء يشبه بريد اختبار التكامل المستمر الخاص بك والذي يتحقق من جميع التعليمات البرمجية المطبقة وغير المطبقة، ويتم إرساله في حالة فشل أي تعليمات برمجية مطبقة، أو نجاح أي تعليمات برمجية غير مطبقة.وكلاهما نتائج غير مرغوب فيها.

فقط اكتب [تجاهل] الاختبارات لن تقوم بالمهمة.ولا يوجد تأكيد يوقف فشل التأكيد الأول، ولا يقوم بتشغيل خطوط اختبارات أخرى في الاختبار.

الآن، كيف يمكن تحقيق ذلك بعد ذلك؟أعتقد أن الأمر يتطلب تنظيمًا أكثر تقدمًا للاختبار الخاص بك.ويتطلب الأمر آلية أخرى ثم تؤكد لتحقيق هذه الأهداف.

أعتقد أنه يتعين عليك تقسيم اختباراتك وإنشاء بعض الاختبارات التي تعمل بشكل كامل ولكن يجب أن تفشل، والعكس صحيح.

تتمثل الأفكار في تقسيم اختباراتك على مجموعات متعددة، واستخدام مجموعة من الاختبارات (الاختبارات المطلوبة في mstest قد تؤدي المهمة).

ومع ذلك، فإن إنشاء CI الذي يتم إرساله بالبريد إن لم تفشل جميع الاختبارات في قسم جامعة نيويورك ليس بالأمر السهل والمباشر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top