مقدمة للاختبار

نشرت: 2021-06-16

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

ما نوع الاختبارات الموجودة؟

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

1. الاختبارات اليدوية

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

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

2. اختبارات التكامل

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

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

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

3. الاختبارات الشاملة (E2E)

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

4. اختبارات الوحدة

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

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

لماذا يجب علينا حتى الاختبار؟

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

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

كود قوي

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

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

قانون غال : لقد وجد دائمًا أن النظام المعقد الذي يعمل قد تطور من نظام بسيط يعمل.

يبدو أيضًا أن الاقتراح العكسي صحيح: النظام المعقد المصمم من الصفر لا يعمل أبدًا ولا يمكن جعله يعمل. عليك أن تبدأ من جديد ، بدءًا من نظام بسيط يعمل.

إعادة البناء بأمان

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

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

كيف لا نكتب الاختبارات؟

من المهم أن تعرف كيف لا تكتب اختبارات الوحدة ، كما هو الحال في كيفية كتابتها.

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

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

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

كيف نكتب الاختبارات؟

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

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

يتكون كل اختبار من 3 أقسام "أ" (نمط AAA) :

  1. يرتب
  2. فعل
  3. يجزم

1. الترتيب

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

عادةً ما يكون الرمز الموجود في هذا القسم أكبر من الرمزين الآخرين مجتمعين .

أحد أنماط التصميم التي يجب أن تكون مفيدة بشكل خاص للحفاظ على هذا القسم صغيرًا هو Object Mother . نمط التصميم هذا مشابه جدًا لـ Factory ، لكنه يحتوي على طرق أكثر تحديدًا لإنشاء كائنات مُعدة مسبقًا لك. بينما يمكن أن يكون Factory القياسي طريقة مثل createCar(carDescription) ، فإن ObjectMother سيكون له طرق مثل createRedFerrari() أو createBlackTesla() أو createBrokenYugo() .

2. قانون

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

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

3. تأكيد

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

Stubs vs Mocks

يستخدم معظم المطورين المصطلحين mock و stub بالتبادل ، ولكن هناك اختلافات.

كعب الروتين لا يمكن أن يفشل في الاختبار ، يمكن وهمية.

تستند Stubs على الدولة ، فإنها ترجع القيم المشفرة. "ما النتيجة التي حصلت عليها؟"

السخريات تعتمد على السلوك ، وتستخدمها للتحقق من كيفية مرور سلوكك من خلالها. "كيف حصلت على النتيجة؟"

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

اختبر شيئًا واحدًا فقط.

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

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

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

اختبارات التسمية

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

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

  • RegisterServiceShould.createNewAccountWhenEmailIsNotTaken
  • RegisterServiceTest.whenEmailIsFree_createNewAccount
  • خدمة التسجيل والاختبار. if_freeEmail_when_userCreatesAccount_then_create

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

اختبارات نظيفة

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

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

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

متى يجب أن نكتب الاختبارات؟

مسترشدين بمبادئ TDD ، يجب أن نكتب الاختبارات قبل كتابة كود جديد .

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

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

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

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

افكار اخيرة

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

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