سؤال

أنا متأكد من أن معظمكم يكتب الكثير من الاختبارات الآلية وأنكم واجهتم أيضًا بعض المخاطر الشائعة عند اختبار الوحدة.

سؤالي هو هل تتبع أي قواعد سلوكية لكتابة الاختبارات لتجنب المشاكل في المستقبل؟لنكون أكثر تحديدًا:ما هي خصائص اختبارات الوحدة الجيدة أو كيف تكتب اختباراتك؟

يتم تشجيع الاقتراحات الحيادية للغة.

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

المحلول

اسمحوا لي أن أبدأ بتوصيل المصادر - اختبار الوحدة العملية في Java باستخدام JUnit (هناك إصدار مع C#-Nunit أيضًا..ولكن عندي هذا ..ملحد في معظم الأحيان.مُستَحسَن.)

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

  • تلقائي :يجب أن يكون استدعاء الاختبارات وكذلك التحقق من نتائج النجاح/الفشل تلقائيًا
  • شامل:تغطية؛على الرغم من أن الأخطاء تميل إلى التجمع حول مناطق معينة في التعليمات البرمجية، تأكد من اختبار جميع المسارات والسيناريوهات الرئيسية.استخدم الأدوات إذا كان عليك معرفة المناطق غير المختبرة
  • قابل للتكرار:يجب أن تؤدي الاختبارات إلى نفس النتائج في كل مرة.كل مرة.يجب ألا تعتمد الاختبارات على معلمات لا يمكن التحكم فيها.
  • مستقل:مهم جدا.
    • ينبغي الاختبارات اختبار شيء واحد فقط في الوقت.لا بأس من التأكيدات المتعددة طالما أنها تختبر جميعًا ميزة/سلوكًا واحدًا.عندما يفشل الاختبار، يجب أن يحدد موقع المشكلة.
    • الاختبارات لا ينبغي أن تعتمد على بعضها البعض - معزول.لا توجد افتراضات حول ترتيب تنفيذ الاختبار.تأكد من "السجل النظيف" قبل كل اختبار باستخدام الإعداد/التفكيك بشكل مناسب
  • احترافي:على المدى الطويل، سيكون لديك قدر كبير من كود الاختبار مثل الإنتاج (إن لم يكن أكثر)، لذلك اتبع نفس معيار التصميم الجيد لرمز الاختبار الخاص بك.فئات الأساليب المدروسة جيدًا بأسماء تكشف النية، وعدم التكرار، والاختبارات بأسماء جيدة، وما إلى ذلك.

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

تحديث 2010-08:

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

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

نصائح أخرى

  1. لا تكتب اختبارات عملاقة. كما تقترح "الوحدة" في "اختبار الوحدة"، اجعل كل واحدة منها كما هي الذري و معزول بقدر الإمكان.إذا لزم الأمر، قم بإنشاء شروط مسبقة باستخدام كائنات وهمية، بدلاً من إعادة إنشاء الكثير من بيئة المستخدم النموذجية يدويًا.
  2. لا تختبر الأشياء التي من الواضح أنها تعمل. تجنب اختبار الفئات من مورد خارجي، خاصة المورد الذي يوفر واجهات برمجة التطبيقات الأساسية لإطار العمل الذي تقوم بالبرمجة فيه.على سبيل المثال، لا تختبر إضافة عنصر إلى فئة Hashtable الخاصة بالمورد.
  3. فكر في استخدام أداة تغطية التعليمات البرمجية مثل NCover للمساعدة في اكتشاف الحالات المتطورة التي لم تختبرها بعد.
  4. حاول كتابة الاختبار قبل التطبيق. فكر في الاختبار باعتباره أكثر من المواصفات التي سيلتزم بها التنفيذ الخاص بك.راجع.وأيضًا التطوير المبني على السلوك، وهو فرع أكثر تحديدًا من التطوير المبني على الاختبار.
  5. كن متسقا. إذا كنت تكتب فقط اختبارات لبعض التعليمات البرمجية الخاصة بك، فلن يكون ذلك مفيدًا.إذا كنت تعمل في فريق، وبعض أو كل الآخرين لا يكتبون الاختبارات، فهذا ليس مفيدًا أيضًا.أقنع نفسك والجميع بأهمية (و توفير الوقت خصائص) للاختبار، أو لا تهتم.

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

قوانين Womp الخمسة لاختبارات الكتابة:


1.استخدم أسماء طرق اختبار وصفية طويلة.

   - Map_DefaultConstructorShouldCreateEmptyGisMap()
   - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
   - Dog_Object_Should_Eat_Homework_Object_When_Hungry()

2.اكتب اختباراتك في ترتيب/تصرف/تأكيد النمط.

  • على الرغم من أن هذه الاستراتيجية التنظيمية كانت موجودة لفترة من الوقت ودعت العديد من الأشياء ، إلا أن إدخال اختصار "AAA" مؤخرًا كان وسيلة رائعة للوصول إلى هذا الأمر.إن جعل جميع الاختبارات الخاصة بك متسقة مع نمط AAA يجعلها سهلة القراءة والصيانة.

3.قم دائمًا بتقديم رسالة فشل مع التأكيدات الخاصة بك.

Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
processing events was raised by the XElementSerializer");
  • ممارسة بسيطة ولكنها مجزية توضح ما فشل في تطبيق الركض الخاص بك.إذا لم تقدم رسالة، فستحصل عادةً على شيء مثل "المتوقع صحيح، كان خاطئًا" في مخرجات الفشل، مما يجعلك مضطرًا لقراءة الاختبار لمعرفة الخطأ.

4.التعليق على سبب الاختبار – ما هو الافتراض التجاري؟

  /// A layer cannot be constructed with a null gisLayer, as every function 
  /// in the Layer class assumes that a valid gisLayer is present.
  [Test]
  public void ShouldNotAllowConstructionWithANullGisLayer()
  {
  }
  • قد يبدو هذا واضحًا ، لكن هذه الممارسة ستحمي سلامة اختباراتك من الأشخاص الذين لا يفهمون السبب وراء الاختبار في المقام الأول.لقد رأيت العديد من الاختبارات تتم إزالة أو تعديلها على ما يرام ، ببساطة لأن الشخص لم يفهم الافتراضات بأن الاختبار كان يتحقق.
  • إذا كان الاختبار تافهة أو أن اسم الطريقة وصفي بما فيه الكفاية ، فقد يكون من الممكن ترك التعليق.

5.يجب أن يعيد كل اختبار دائمًا حالة أي مورد يلمسه

  • استخدم Mocks حيثما أمكن ذلك لتجنب التعامل مع الموارد الحقيقية.
  • يجب أن يتم التنظيف على مستوى الاختبار.يجب ألا يكون للاختبارات أي اعتماد على ترتيب التنفيذ.

ضع هذه الأهداف في الاعتبار (مقتبسة من كتاب أنماط اختبار xUnit من تأليف Meszaros)

  • يجب أن تقلل الاختبارات من المخاطر ، وليس تقديمها.
  • يجب أن تكون الاختبارات سهلة التشغيل.
  • يجب أن تكون الاختبارات سهلة الحفاظ عليها حيث يتطور النظام من حولهم

بعض الأشياء لتسهيل ذلك:

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

لا تنس أنه يمكنك إجراء اختبار التكامل مع إطار عمل xUnit الخاص بك أيضًا ولكن احتفظ باختبارات التكامل واختبارات الوحدة منفصلة

يجب عزل الاختبارات.لا ينبغي أن يعتمد اختبار واحد على آخر.علاوة على ذلك، لا ينبغي أن يعتمد الاختبار على أنظمة خارجية.وبعبارة أخرى، اختبار لك الكود، وليس الكود الذي يعتمد عليه الكود الخاص بك. يمكنك اختبار تلك التفاعلات كجزء من التكامل أو الاختبارات الوظيفية.

بعض خصائص اختبارات الوحدة الرائعة:

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

  • عند إعادة البناء، يجب ألا تفشل أي اختبارات.

  • يجب أن تتم الاختبارات بسرعة كبيرة بحيث لا تتردد أبدًا في إجرائها.

  • يجب أن تجتاز جميع الاختبارات دائمًا؛لا توجد نتائج غير حتمية.

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

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


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

أنا لا أتبع هذا النهج.بدلا من ذلك، أستخدم تركيبات الاختبار لكل سيناريو.إليك مثال تقريبي:

[TestFixture]
public class StackTests
{
    [TestFixture]
    public class EmptyTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
        }

        [TestMethod]
        [ExpectedException (typeof(Exception))]
        public void PopFails()
        {
            _stack.Pop();
        }

        [TestMethod]
        public void IsEmpty()
        {
            Assert(_stack.IsEmpty());
        }
    }

    [TestFixture]
    public class PushedOneTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
            _stack.Push(7);
        }

        // Tests for one item on the stack...
    }
}

ما تبحث عنه هو تحديد سلوكيات الفصل قيد الاختبار.

  1. التحقق من السلوكيات المتوقعة.
  2. التحقق من حالات الخطأ.
  3. تغطية كافة مسارات التعليمات البرمجية داخل الفصل.
  4. ممارسة جميع وظائف الأعضاء داخل الفصل.

الهدف الأساسي هو زيادة ثقتك في سلوك الفصل.

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

هث.

هتافات،

روب

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

أنا أحب اختصار Right BICEP مما سبق ذكره اختبار الوحدة العملية كتاب:

  • يمين:هي النتائج يمين?
  • ب:هل كل بالظروف المحيطة صحيحة؟
  • أنا:هل يمكننا التحقق أناالعلاقات العكسية؟
  • ج:هل يمكننا ذلك؟ جروس التحقق من النتائج باستخدام وسائل أخرى؟
  • ه:هل يمكننا القوة هظروف rror أن يحدث؟
  • ص:نكون صخصائص الأداء ضمن الحدود؟

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

الاختبارات الجيدة يجب أن تكون قابلة للصيانة.

لم أكتشف تمامًا كيفية القيام بذلك في البيئات المعقدة.

تبدأ جميع الكتب المدرسية في عدم ملاءمة عندما تبدأ قاعدة التعليمات البرمجية في الوصول إلى مئات من 1000 أو ملايين من كود.

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

يمكن للهندسة المعمارية الجيدة التحكم في بعض انفجار التفاعل ، ولكن حتما لأن الأنظمة تصبح أكثر تعقيدًا ، ينمو نظام الاختبار الآلي معه.

هذا هو المكان الذي تبدأ فيه التعامل مع المقايضات:

  • قم باختبار واجهة برمجة التطبيقات الخارجية فقط، وإلا فإن إعادة هيكلة الأجزاء الداخلية تؤدي إلى إعادة صياغة حالة اختبار مهمة.
  • يصبح إعداد كل اختبار وتفكيكه أكثر تعقيدًا حيث يحتفظ النظام الفرعي المغلف بمزيد من الحالة.
  • ينمو التجميع الليلي وتنفيذ الاختبار الآلي إلى ساعات.
  • زيادة أوقات التجميع والتنفيذ تعني أن المصممين لا يقومون أو لن يقوموا بإجراء جميع الاختبارات
  • لتقليل أوقات تنفيذ الاختبار، عليك أن تفكر في إجراء اختبارات التسلسل لتقليل الإعداد والتفكيك

عليك أيضًا أن تقرر:

أين تقوم بتخزين حالات الاختبار في قاعدة التعليمات البرمجية الخاصة بك؟

  • كيف يمكنك توثيق حالات الاختبار الخاصة بك؟
  • هل يمكن إعادة استخدام تركيبات الاختبار لحفظ صيانة حالة الاختبار؟
  • ماذا يحدث عندما يفشل تنفيذ حالة الاختبار الليلية؟من يقوم بالفرز؟
  • كيف يمكنك الحفاظ على الأشياء الوهمية؟إذا كان لديك 20 وحدة تستخدم جميعها نكهة خاصة بها من واجهة برمجة التطبيقات للتسجيل الوهمي، فسيتم تغيير تموجات واجهة برمجة التطبيقات بسرعة.لا تتغير حالات الاختبار فحسب، بل تتغير الكائنات الوهمية العشرين.تمت كتابة هذه الوحدات العشرين على مدى عدة سنوات من قبل العديد من الفرق المختلفة.إنها مشكلة إعادة استخدام كلاسيكية.
  • يدرك الأفراد وفرقهم قيمة الاختبارات الآلية، لكنهم لا يحبون الطريقة التي يقوم بها الفريق الآخر بذلك.:-)

يمكنني الاستمرار إلى الأبد، لكن وجهة نظري هي:

يجب أن تكون الاختبارات قابلة للصيانة.

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

الطريقة التي أعرف بها اختبارات الوحدة "الجيدة" هي إذا كانت تمتلك الخصائص الثلاث التالية:

  • أنها قابلة للقراءة (التسمية، التأكيدات، المتغيرات، الطول، التعقيد..)
  • أنها قابلة للصيانة (لا يوجد منطق، ليست محددة، قائمة على الحالة، معاد هيكلتها..)
  • إنهم جديرون بالثقة (اختبر الصواب، المعزول، وليس اختبارات التكامل..)
  • اختبار الوحدة يختبر فقط واجهة برمجة التطبيقات الخارجية لوحدتك، ولا يجب عليك اختبار السلوك الداخلي.
  • يجب أن يختبر كل اختبار لـ TestCase طريقة واحدة (واحدة فقط) داخل واجهة برمجة التطبيقات هذه.
    • يجب تضمين حالات الاختبار الإضافية لحالات الفشل.
  • اختبار تغطية الاختبارات الخاصة بك:بمجرد اختبار الوحدة، يجب أن يتم تنفيذ 100% من الخطوط الموجودة داخل هذه الوحدة.

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

أطيب التحيات

لا تفترض أبدًا أن الطريقة التافهة المكونة من سطرين ستنجح.إن كتابة اختبار سريع للوحدة هي الطريقة الوحيدة لمنع الاختبار الفارغ المفقود و/أو علامة الطرح في غير مكانها و/أو خطأ تحديد النطاق الدقيق من إلحاق الضرر بك، حتمًا عندما يكون لديك وقت أقل للتعامل معه أكثر من الآن.

أنا أؤيد إجابة "رحلة"، باستثناء ذلك الاختبارات يجب أن تعتمد على بعضها البعض!!!

لماذا؟

جاف - لا تكرر نفسك - ينطبق على الاختبار أيضًا!يمكن أن تساعد تبعيات الاختبار في 1) توفير وقت الإعداد، 2) حفظ موارد التركيبات، و3) تحديد حالات الفشل.بالطبع، فقط بشرط أن إطار الاختبار الخاص بك يدعم تبعيات من الدرجة الأولى.بخلاف ذلك، أعترف أنهم سيئون.

متابعة http://www.iam.unibe.ch/~scg/Research/JExample/

غالبًا ما تعتمد اختبارات الوحدة على كائنات وهمية أو بيانات وهمية.أحب أن أكتب ثلاثة أنواع من اختبارات الوحدة:

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

النقطة المهمة هي تجنب إعادة التشغيل كل شئ لكي تكون قادرًا على اختبار كل الوظائف.

  • أقوم بتشغيل النوع الثالث في كثير من الأحيان لأن جميع الكائنات/البيانات الوهمية موجودة بالفعل.
  • أقوم بتشغيل النوع الثاني كلما تغير النموذج الخاص بي.
  • أقوم بتشغيل الأول للتحقق من الوظائف الأساسية من حين لآخر، للتحقق من الانحدارات الأساسية.

فكر في نوعين من الاختبارات وعاملهما بشكل مختلف - الاختبار الوظيفي واختبار الأداء.

استخدم مدخلات ومقاييس مختلفة لكل منها.قد تحتاج إلى استخدام برامج مختلفة لكل نوع من الاختبارات.

أستخدم اصطلاح تسمية اختبار ثابتًا موصوفًا بواسطة معايير تسمية وحدة اختبار روي أوشيروف كل أسلوب في فئة حالة اختبار معينة له نمط التسمية التالي MethodUnderTest_Scenario_ExpectedResult.

    قسم اسم الاختبار الأول هو اسم الطريقة الموجودة في النظام قيد الاختبار.
    التالي هو السيناريو المحدد الذي يتم اختباره.
    وأخيرا نتائج هذا السيناريو.

يستخدم كل قسم حالة الجمل العلوي ويتم تحديده بعلامة أقل.

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

أقوم أيضًا بإلحاق معلمات باسم الطريقة إذا تم تحميل الطريقة قيد الاختبار بشكل زائد.

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