SyntaxHighlighter

الأحد، 12 يونيو، 2011

اختبار الوحدات Unit Testing

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

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

لنأخذ مثالا لتطبيق بسيط للجمع بين رقمين حيث يدخل المستخدم رقمين وعند الضغط على زر يعرض ناتج الجمع

protected void btnAdd_Click(object sender, EventArgs e)

{

    double a, b;

 

    a = Convert.ToDouble(TextBox1.Text);

    b = Convert.ToDouble(TextBox2.Text);

    Label1.Text = Convert.ToString(a + b);

}

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

public class Calculator
{
    public double Add(string a, string b)
    {
        var ad = Convert.ToDouble(a);
        var bd = Convert.ToDouble(b);
 
        return Add(ad, bd);
    }
 
    public double Add(double a, double b)
    {
        return a + b;
    }
 }

وعندها يمكن كتابة الاختبار كالتالي

[TestFixture]
public class CalculatorTest
{
    [Test]
    public void AddOneAndOneShouldReturnTwo()
    {
        var calc = new Calculator();
 
        Assert.AreEqual(calc.Add("1", "1"), 2);
    }
}

هذا المثال يستخدم C# و NUnit ولكن الفكرة مشتركة بين المكتبات واللغات المختلفة ولكن الانطباع الأول قد يكون تم تحويل 5 أسطر من الكود إلى 20 فكيف الادعاء بأن Unit Testing تساعد في زيادة الإنتاجية. يعتمد هذا الافتراض على أن الوقت الأكبر في تطوير البرمجيات يكون في طباعة الأحرف بينما أي شخص قضى أكثر من عدة ساعات مطورا يعلم أن المشكلة تكمن في تقصي وتصحيح الأخطاء Debugging أو إدخال تعديلات وعندها تظهر فوائد الاختبارات الأوتوماتيكية. وكلما زاد تعقيد التطبيق والمزايا الملحقة به كلما زادت باضطراد فائدة الاختبارات. وحتى مع هذا الشكل من المنطق نستطيع ملاحظة فائدتان.

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

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

على الرغم من بساطة المثال الذي كتبناه ولكنه يمثل الهيكل العام لاختبارات الوحدات والتي تتكون عادة من ثلاث مراحل يشار لها باختصار AAA أو Arrange Act Assert

في المرحلة الأولى مرحلة الترتيب Arrange يتم التجهيز للاختبار وفي حالتنا لم يعدو التجهيز انشاء نسخة جديدة من Class. في مرحلة الفعل Act يتم تنفيذ المنطق المراد اختباره وفي المرحلة الأخيرة المسماة التأكد Assert يتم مقارنة نتيجة الفعل بنتائج معرفة مسبقا.

هناك مجموعة من القواعد العامة لكتابة اختبار وحدة جيد لتكوين مظلة الأمان المنشودة لتحقق الاختبارات غرضها.

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

§ الدقة ، فالاختبارات يفترض فيها أن تكون المرجعية فإذا كانت غير دقيقة فإنها تفقد معناها.

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

نظرا لأهمية اختبارات الوحدات في نظم تطوير التطبيقات الحديثة فإن Frameworks أصبحت تصمم لتسهيل الاختبار كما أصبح ذلك من معايير جودتها فعلى سبيل المثال ASP.NET WebForms يعتبر مشكلة عند الاختبار وخاصة عند الاعتماد على HTTPContext أو Session عامة حيث أنها Sealed Classes وغير معتمدة على Interfaces وذلك يصعب من عملية Mocking لفصل الاعتمادات الخارجية ، كما أنه لا يشجع على الفصل بين المنطق وواجهة التطبيق وذلك يمنع الاختبار كما رأينا في بداية المقال. ولذا فإن ASP.NET MVC راعى ذلك في تصميمه لتسهيل الاختبار والتشجيع على الفصل بين الواجهة والمنطق باستخدام MVC Pattern.

أهمية الاختبارات تتضاعف مع لغات البرمجة الديناميكية مثل Ruby أو Python حيث إنها تكون بمقام الCompiler في التأكد من صحة العمليات وبالتالي يمكن الجمع بين الإنتاجية العالية في Dynamic Languages وأمان Static Languages.

لبدء كتابة الاختبارات يمكنك البحث عن مكتبة ال Unit Testing المستخدمة في اللغة التي تستخدمها والمتاحة تقريبا لكل اللغات تحت مظلة xUnit Frameworks والتي تتبع مكتبة JUnit التي كتبها Kent Beck الأب الروحي لاختبارات الوحدات.

ليست هناك تعليقات:

إرسال تعليق