كيف تعرف ما يجب اختباره عند كتابة اختبارات الوحدة؟[مغلق]

StackOverflow https://stackoverflow.com/questions/62625

  •  09-06-2019
  •  | 
  •  

سؤال

باستخدام C#، أحتاج إلى فصل دراسي يسمى User يحتوي على اسم مستخدم وكلمة مرور وعلامة نشطة والاسم الأول واسم العائلة والاسم الكامل وما إلى ذلك.

يجب أن تكون هناك طرق لذلك مصادقة و يحفظ مستخدم.هل أكتب فقط اختبارًا للطرق؟وهل أحتاج حتى إلى القلق بشأن اختبار الخصائص نظرًا لأنها من أهم أدوات ضبط .Net؟

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

المحلول

العديد من الردود الرائعة على هذا موجودة أيضًا على سؤالي:"بداية TDD - التحديات؟حلول؟توصيات؟"

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

لا اعلم اين سابدأ؟

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

اختبر فقط ما تتوقعه

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

اختبار شيء واحد فقط

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

آمل أن يكون هذا يعني أنه يمكننا الانتقال من "الحاصلين والمستوطنين" :)

نصائح أخرى

اختبر الكود الخاص بك، وليس اللغة.

اختبار الوحدة مثل:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

يكون مفيدًا فقط إذا كنت تكتب مترجمًا وكانت هناك فرصة غير صفرية أن يكون لديك instanceof الطريقة لا تعمل.

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

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

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

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

لم أكن أعرف كيف أبدأ في ترميزه، نظرًا لوجود العديد من خيارات الدفع المختلفة.يمكن أن تكون الفاتورة 100 دولار ولكن العميل قام بتحويل 99 دولارًا فقط.ربما تكون قد أرسلت فواتير مبيعات إلى أحد العملاء ولكنك قمت بالشراء أيضًا من هذا العميل.لذلك بعته بـ 300 دولار لكنك اشتريته بـ 100 دولار.يمكنك أن تتوقع أن يدفع لك عميلك 200 دولار لتسوية الرصيد.وماذا لو بعت بمبلغ 500 دولار ولكن العميل يدفع لك 250 دولار فقط؟

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

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

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

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

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

بعد لقد كتبت الاختبار، وبدأت في ترميز طريقة الدفع (جزء من فئة BankHeader).في الترميز، لم أهتم إلا بالرمز لإجراء الاختبار الأول.ولم أفكر بعد في السيناريوهات الأخرى الأكثر تعقيدا.

لقد أجريت الاختبار الأول وأصلحت خطأً صغيرًا حتى نجح الاختبار.

ثم بدأت في كتابة الاختبار الثاني، هذه المرة مع خصم الدفع.بعد أن كتبت الاختبار قمت بتعديل طريقة الدفع لدعم الخصومات.

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

ثم شقت طريقي إلى السيناريوهات الأكثر تعقيدًا.

1) فكر في سيناريو جديد

2) اكتب اختبارًا لهذا السيناريو

3) قم بإجراء هذا الاختبار الفردي لمعرفة ما إذا كان سينجح

4) إذا لم يحدث ذلك، فسوف أقوم بتصحيح الكود وتعديله حتى يتم تمريره.

5) أثناء تعديل الكود، واصلت تشغيل جميع الاختبارات

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

أنا متأكد من أن استخدام اختبار الوحدة قد وفر لي بضعة أيام (أو أسابيع) من البرمجة ويضمن بشكل أو بآخر صحة طريقتي.

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

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

فيما يلي بعض الاختبارات التي قمت بإنشائها لاختبار طريقة الدفع الخاصة بي.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

إذا كانت تافهة حقًا، فلا تهتم بالاختبار.على سبيل المثال، إذا تم تنفيذها على هذا النحو؛

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

من ناحية أخرى، إذا كنت تفعل شيئًا ذكيًا (مثل تشفير وفك تشفير كلمة المرور في أداة getter/setter)، فاختبره.

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

يبدو أن هذا السؤال هو سؤال حول أين يمكن رسم الخط الفاصل بين الأساليب التي يتم اختبارها وتلك التي لا يتم اختبارها.

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

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

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

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

رأيي هو أن بضع دقائق من الوقت "المهدر" في تغطية جميع الأساليب باختبارات الوحدة، حتى تلك التافهة، قد توفر أيامًا من الصداع في المستقبل وخسارة المال/سمعة العمل وخسارة وظيفة شخص ما.

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

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

إجابة قانونية أخرى.أعتقد أن هذا من رون جيفريز:

اختبر فقط الكود الذي تريد العمل به.

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

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

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

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

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

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

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

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

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

في التسلسل الهرمي للميراث، تأكد من اختبار LSP امتثال.

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

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

إذا كانت طريقتا المصادقة والحفظ تستخدمان الخصائص، فسوف تلمس اختباراتك الخصائص بشكل غير مباشر.طالما أن الخصائص توفر فقط إمكانية الوصول إلى البيانات، فلن يكون الاختبار الصريح ضروريًا (إلا إذا كنت تريد تغطية بنسبة 100%).

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

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

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

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

في حالة الأعداد الصحيحة البسيطة التي تنطبق أيضًا - ماذا يحدث إذا قمت بالتمرير لفترة طويلة بدلاً من الأعداد الصحيحة؟هذا هو سبب كتابتك UT لـ :)

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

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

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

تبدو طريقتا Authenticate() وSave() كمرشحتين جيدتين للاختبار.

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

إن كتابة الاختبارات بعد ذلك أمر مؤلم للغاية، ولكنه ممكن.

إليك ما سأفعله في منصبك:

  1. اكتب مجموعة أساسية من الاختبارات التي تختبر الوظيفة الأساسية.
  2. احصل على Ncover وقم بتشغيله في اختباراتك.من المحتمل أن تبلغ نسبة تغطية الاختبار حوالي 50% في هذه المرحلة.
  3. استمر في إضافة الاختبارات التي تغطي حالاتك الطرفية حتى تحصل على تغطية تبلغ حوالي 80%-90%

من المفترض أن يمنحك هذا مجموعة عمل جيدة من اختبارات الوحدة التي ستكون بمثابة حاجز جيد ضد الانحدارات.

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

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

لا تختبر رمز العمل (النموذجي) بشكل واضح.لذا، إذا كانت أدوات الضبط والحروف الخاصة بك هي مجرد "قيمة الخاصية = قيمة" و"قيمة خاصية الإرجاع"، فليس من المنطقي اختبارها.

حتى الحصول على / تعيين يمكن أن يكون له عواقب غريبة، اعتمادًا على كيفية تنفيذها، لذلك يجب التعامل معها على أنها طرق.

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

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

لذا نعم، لا داعي للقلق بشأن اختبار الخصائص.

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

أود أن أكتب اختبارًا لأي شيء تكتبه رمزًا يمكن اختباره خارج واجهة المستخدم الرسومية.

عادةً، أي منطق أكتبه يحتوي على أي منطق أعمال أضعه داخل طبقة أخرى أو طبقة منطق أعمال.

ثم من السهل القيام بكتابة الاختبارات لأي شيء يفعل شيئًا ما.

أولاً، اكتب اختبار وحدة لكل طريقة عامة في "طبقة منطق الأعمال" الخاصة بك.

إذا كان لدي فصل مثل هذا:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

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

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

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

هناك الكثير من الأساليب الأخرى التي يمكنك اتباعها، وهي التطوير المدفوع بالسلوك (BDD)، وهو أكثر تعقيدًا وليس مكانًا رائعًا للبدء بمهارات اختبار الوحدة الخاصة بك.

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

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

أوصي TestDriven.Net أو ريشاربر حيث يمكن دمجهما بسهولة في Visual Studio.

حسنًا، إذا كنت تعتقد أنه يمكن أن ينكسر، فاكتب اختبارًا له.عادةً لا أقوم باختبار setter/getter، ولكن لنفترض أنك قمت بإنشاء واحد لـ User.Name، الذي يربط الاسم الأول والأخير، سأكتب اختبارًا لذلك إذا قام شخص ما بتغيير ترتيب الاسم الأخير والاسم الأول، على الأقل سيعرف لقد غير شيئًا تم اختباره.

الجواب الكنسي هو "اختبار أي شيء يمكن أن ينكسر". إذا كنت متأكدًا من أن الخصائص لن تنكسر ، فلا تختبرها.

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

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

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

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

كود يحتوي على الكثير من الاختبارات ولكن لم يتم اختبار التغطية الصغيرة بشكل جيد.ومع ذلك، فإن التعليمات البرمجية ذات التغطية 100% ولكن دون اختبار الحدود وحالات الخطأ ليست رائعة أيضًا.

تريد تحقيق التوازن بين التغطية العالية (90% كحد أدنى) وبيانات الإدخال المتغيرة.

تذكر أن تقوم باختبار "القمامة الموجودة"!

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

أنت بحاجة إلى تصميم اختباراتك بحيث تبلغ دائمًا عن حالات الفشل أو البيانات غير المتوقعة/غير المرغوب فيها!

إنه يجعل الكود الخاص بنا أفضل ...فترة!

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

في الروح الحقيقية لاختبار الوحدة، هذه الاختبارات هي لا هناك في المقام الأول "لاختبار" المزيد من التعليمات البرمجية الخاصة بنا؛أو للحصول على تغطية أفضل للكود بنسبة 90%-100%.هذه كلها فوائد هامشية كتابة الاختبارات أولا.المردود الكبير هو أن كود الإنتاج الخاص بنا قد تمت كتابته بشكل أفضل بكثير بسبب العملية الطبيعية لـ TDD.

للمساعدة في توصيل هذه الفكرة بشكل أفضل، قد يكون ما يلي مفيدًا في القراءة:

النظرية المعيبة لاختبارات الوحدة
تطوير البرمجيات الهادفة

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

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