أستمع الى المقال

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

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

ماهو الاختبار Testing:

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

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

مكتبات اختبار متوافقة مع كوتلن:

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

  • JUnit: هو أحد أطر الاختبار Test Framework الأكثر شيوعًا لبرامج جافا، ويمكن استخدامه بسهولة مع البرامج المكتوبة عبر شفرة كوتلن.
  • Kotest: تم إنشاء أداة الاختبار هذه خصيصًا لـ كوتلن، وتستفيد بالتالي من ميزات لغة كوتلن.

تقنية تطوير موجه بالاختبار Test Driven Development:

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

يُفضل الكثير من المطورين، كتابة الاختبارات قبل حتى كتابة شفرة البرنامج. هذا يكون بمعرفة ماذا يكون البرنامج الذي نطوره والنتائج المتوقعة منه. سنقوم بكتابة هذه الاختبارات، لاختبار هذه التوقعات. بالتأكيد الشفرة التي نكتبها لن تجتاز الاختبارات في أول مرة. تسمى هذه التقنية، بـ تطوير موجه بالاختبار (TDD). يمكن إيجاد وصف كامل لـ TDD على غوغل بكتابة (“Test Driven Development”).

هرم الاختبار Test Pyramid:

هناك أنواع مختلفة من الاختبارات في هندسة البرمجيات، وكلها تخدم أغراضًا مختلفة. المفهوم الرئيسي الذي يمثل أنواعًا مختلفة من الاختبارات هو هرم الاختبار:

يساعدنا هرم الاختبار هذا، في فهم مستويات الاختبار المختلفة. لنُلقِ نظرة سريعة على كل المستويات في الهرم بدءًا من قاعدته:

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

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

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

سنقوم بإجراء هذا الإختبار عمليًا في هذا الدرس.

  • اختبار التكامل Integration Testing:

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

  • الاختبار الشامل End-to-end testing:

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

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

  • اختبار واجهة المستخدم UI testing:

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

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

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

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

إجراء اختبار الوحدات عمليًا:

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

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

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

إجراء اختبار الوحدات عبر مكتبة JUnit:

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

دمج المكتبة في المشروع عبر استخدام Gradle:

إذا استخدمنا أداة البناء Gradle لبناء مشروعنا، سنقوم بإضافة السطر التالية إلى ملف build.gradle:

dependencies {

    testImplementation ‘org.junit.jupiter:junit-jupiter:5.8.2’

}

ويُمثل الرقم 5.8.2، رقم الإصدار الحالي. يمكننا دائمًا التحقق من رقم الإصدار في صفحة المكتبة في مستودع Maven، كما رأينا في درس أداة البناء Gradle.

ونضيف أيضًا في آخر ملف build.gradle، الكتلة التالية:

test {

    useJUnitPlatform()

}

بدون هذه الكتلة، لن تستطيع أداة Gradle تشغيل شفرات الاختبار التي نكتبها.

برنامج آلة حاسبة بسيط:

الآن، لنقم بإنشاء آلة حاسبة بسيطة لإجراء العمليات الحسابية الأساسية باستخدام الأعداد الصحيحة Integers، والتي يمكننا اختبارها بسهولة. سنُنشئ صنف نسميه Calculator في مجلد kotlin يُمثل هذه الحاسبة، ويحتوي على أربعة دوال تقوم بالعمليات الحسابية التالية: الجمع Addition والطرح Subtraction والضرب Multiplication والقسمة Division. 

كل واحدة من هذه الدوال، تستقبل معاملين من نوع الأعداد الصحيحة Int، وتعيد نتيجة عدد صحيح Int، حسب العملية الحسابية التي تقوم بها، كالتالي:

الدالة الأولى في الصنف Calculator، هي الدالة add والتي ستقوم بإرجاع قيمة جمع العددين اللذين يتم إرسالهما إليها. ثم دالة subtract، ستقوم بإرجاع قيمة طرح العددين اللذين يتم إرسالهما إليها. ثم multiply، ستُرجع قيمة ضرب العددين. وأخيرًا، دالة divide والتي ستتأكد أولًا عبر استخدام كتلة if، من أن العدد الثاني ليس صفر. فإذا كان العدد الثاني يساوي صفر، ستقوم بإيقاف عمل البرنامج وإرجاع استثناء (سنتحدث عن الاستثناءات Exceptions في الدرس القادم). أما إذا لم يكن يساوي صفر، ستقوم بقسمة العددين وإرجاع القيمة.

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

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

إنشاء صنف الاختبار:

لاختبار الصنف Calculator، سنقوم بإنشاء صنف آخر باسم CalculatorTest في مجلد kotlin الموجود داخل مجلد test الخاص بمشروعنا. يمكننا القيام بذلك يدويًا، أو عبر النقر بزر الماوس الأيمن فوق اسم الصنف واختيار Generate، ثم Test في القائمة المنسدلة لجعل برنامج IntelliJ يقوم بإنشاء صنف الاختبار، كما تُظهر الصورتين التاليتين:

في النافذة التالية، نختار المكتبة التي سنستخدمها في اختبار الصنف، ثم نكتب اسم صنف الاختبار الذي نُريده وأخيرًا نختار دوال الصنف التي نود اختبارها بوضع علامة صح يسار كل دالة:

سواء أضفنا صنف الاختبار يدوياً أو عبر IntelliJ، يجب أن يكون شكله كالتالي:

  1. تم وضع صنف الاختبار CalculatorTest، في مجلد kotlin داخل مجلد test. وﻷن محتويات المجلد test، عبارة عن شفرات اختبار لشفرات التطبيق الأصلية فقط، فلن يتم دمجها في التطبيق النهائي.
  2. تم تضمين بعض الأصناف التي نحتاجها لكتابة اختبار، من حِزمة api الموجودة في مكتبة JUnit.
  3. نكتب هذا التعليق Test@ على رأس كل دالة لإخبار مكتبة JUnit أن هذه الدالة، هي دالة اختبار وحدات. تعليق Test@ تعليق خاص يسمى Annotation سنستعرضه في وقته لاحقًا. ما يهمنا في هذا الدرس، هو فهم عمله فقط.

كتابة الاختبارات:

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

داخل الدالة ()testAdd، أنشأنا كائن من الصنف Calculator وأسندناه للمتغير calculator، لنتمكن من استدعاء الدالة ()add المراد اختبارها. ثم استدعينا الدالة ()add عبر استخدام الكائن، وأرسلنا لها عددين نريد جمعهما (7 , 3). وأسندنا القيمة التي تُعيدها الدالة ()add، إلى متغير جديد أسميناه result. ثم استدعينا الدالة ()assertEquals من مكتبة JUnit، لفحص النتيجة العائدة من الدالة، مقابل نتيجة جمع العددين والتي تساوي 10 كما نعلم.

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

بنفس الطريقة، سنضيف شفرات اختبار لبقية الدوال. لتكون النتيجة النهائية لصنف الاختبار CalculatorTest، كالتالي:

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

فهي بذلك، تؤكد assert لنا النتيجة أيًا كانت. لذلك تسمى دالة تأكيد، وتوجد في صنف التوكيدات Assertions.

صنف Assertions من مكتبة JUnit:

يحتوي الصنف Assertions في مكتبة الاختبار JUnit على الكثير من دوال التأكيد والتي تتيح لنا اختبار حالات مختلفة. فيما يلي بعض التوكيدات Assertions المفيدة:

الدالة في الصنف Assertionsعمل الدالة
()assertEqualsاختبار ما إذا كانت القيم المرسلة إليها متساوية
()assertTrueاختبار ما إذا كانت القيمة المرسلة إليها تساوي true
()assertFalseاختبار ما إذا كانت القيمة المرسلة إليها تساوي false
()assertNullاختبار ما إذا كانت القيمة المرسلة إليها تساوي null أي قيمة فارغة
()assertNotNullاختبار ما إذا كانت القيمة المرسلة إليها لا تساوي null
()assertThrowsاختبار ما إذا كانت القيمة المرسلة إليها تنتج إستثناء (خطأ) معين

كل واحدة من هذه الدوال، ستُعيد رسالة من نوع البيانات String، والتي سيتم عرضها في حالة فشل الاختبار. يمكن معرفة المزيد عن دوال التأكيد الأخرى في وثائق JUnit 5 الرسمية.

تشغيل الاختبارات:

الآن بعد أن أصبح صنف الاختبار جاهزًا لاختبار كل الدوال، يمكننا تشغيل الاختبار بالضغط على الزر الأخضر الذي يظهر يسار كل دالة اختبار على حدى، أو الزر الأخضر على يسار اسم الصنف CalculatorTest نفسه، كالتالي:

  1. نضغط على الزر الأخضر لتنفيذ اختبار الوحدات الأربعة.
  2. رسالة نصية تُظهر أن الدوال الأربعة نجحت في الاختبار.
  3. الخطوات التي قامت بها أداة Gradle ونتيجة الفحص في تبويب Run.

فشل الاختبارات:

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

عند تنفيذ الاختبار هذه المرة، ستكون النتيجة كالتالي:

فشلت الدالة ()add في تخطي الاختبار، ﻷن المتوقع منها هو النتيجة 10 ولكنها أعادت القيمة صفر. الآن يمكننا الذهاب إلى الدالة وتعديل شفرتها لإصلاح الخطأ.

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

هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.