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

المُقدّمة

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

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

يمكن اختبار مكونات التفاعل بعدة طرق مختلفة ، مقسمة على نطاق واسع إلى مجموعتين:

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

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

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

ما هو الاختبار؟

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

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

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

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

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

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

مناهج الاختبار

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

الاختبار اليدوي

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

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

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

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

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

الاختبار الآلي

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

هناك العديد من الأساليب لكتابة الاختبارات الآلية:

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

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

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

أنواع الاختبارات

حتى الآن ، نظرنا في الاختبارات على مستوى عالٍ. حان الوقت لمناقشة الأنواع المختلفة من الاختبارات التي يمكن كتابتها.

هناك ثلاثة أنواع من اختبارات التطبيق الأمامية:

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

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

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

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

إذا كنت ترغب في فهم أساسيات الاختبار الشامل باستخدام Cypress - اقرأ "اختبار شامل في JavaScript باستخدام Cypress"!

مزايا وعيوب الاختبار

في حين أن الاختبار مهم ويجب إجراؤه ، كالمعتاد ، إلا أن له مزايا وعيوب.

المزايا

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

عيوب

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

نظرة عامة على اختبار الوحدة

حتى الآن ، ألقينا نظرة على الاختبار بشكل عام. حان الوقت الآن للتعمق في كل ما يتعلق باختبار الوحدة وكيفية كتابة اختبارات الوحدة في تطبيقات React!

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

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

إيجابيات اختبارات الوحدة

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

سلبيات اختبارات الوحدة

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

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

وحدة اختبار تطبيق رد فعل - مشروع تجريبي

لنلقِ نظرة على مثال واقعي لوحدة تختبر تطبيق React!

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

إعداد مشروع

create-react-app الأمر الذي أنشأه فريق React ، هو أفضل طريقة للبدء في إنشاء تطبيق React واقعي وواسع النطاق لأنه جاهز للاستخدام ويعمل بسهولة مع مكتبة اختبار الدعابة. إذا قمت بفتح ملف package.json ملف ، ستجد أن لدينا دعمًا افتراضيًا لـ دعابة و مكتبة اختبار التفاعل في ال setupTests.js ملف. هذا يلغي حاجتنا إلى تثبيت Jest يدويًا في مشروعنا إذا احتجنا إلى ذلك!

إذا لم تكن قد استخدمته بالفعل - قم بتشغيله باستخدام npx، والذي سيقوم بتثبيته لاستخدامه لاحقًا:

$ npx create-react-app react-unit-tests

إذا كانت الأداة مثبتة لديك بالفعل ، فأنشئ تطبيق React وقم بتسميته react-unit-tests:

$ create-react-app react-unit-tests

ملحوظة: npx يستخدم أحدث إصدار من create-react-app، في حين أن الإصدار المثبت عالميًا قد لا يفعل ذلك. يُنصح عمومًا بتشغيل الأداة npx لضمان أحدث الإصدارات ، إلا إذا كنت تريد استخدام إصدار آخر عن قصد.

بعد ذلك ، ندخل إلى دليل المشروع ونبدأ خادم التطوير:

$ cd react-unit-tests && npm start
// OR
$ cd react-unit-tests && yarn start

سيؤدي هذا إلى إخراج تطبيقنا الذي تم إنشاؤه حديثًا في المتصفح في localhost:3000.

ملحوظة: الميزة المفيدة هنا هي ذلك يتم دعم إعادة التحميل السريع افتراضيًا، لذلك ليست هناك حاجة للاستمرار في إعادة تحميل المتصفح لمجرد رؤية التغييرات الجديدة أو التثبيت يدويًا nodemon أو مكتبات مماثلة.

بناء مكون العداد

في مجلة src دليل مشروعنا ، قم بإنشاء ملف جديد يسمى Counter.js. في Counter.jsسنحدد جميع أجزاء المكون. سوف يحتوي على وظائف مختلفة للعداد ، بما في ذلك increment(), decrement(), restart()و switchSign()، مما يعكس قيمة العد من سالب إلى موجب عند النقر عليه. يتم إنشاء هذه الوظائف لمعالجة قيمة العد الأولية (التي يتم تمريرها كخاصية):


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount((prev) => prev + 1);
  };

  const decrement = () => {
    setCount((prev) => prev - 1);
  };

  const restart = () => {
    setCount(0);
  };

  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
        Count: <h3>{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

ثم قم بالتحديث App.js:


import "./App.css";
import Counter from "./Counter";

function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}

export default App;

الآن ، يمكننا عرض تطبيق العداد على المتصفح:

الدليل النهائي لاختبار الوحدة في تطبيقات React باستخدام Jest واختبار React-Test PlatoBlockchain Data Intelligence. البحث العمودي. منظمة العفو الدولية.

إنشاء اختبارات للمكونات

لنقم بإنشاء ملف اختبار يسمى Counter.test.js لتمثيل اختبار مكون العداد. تأكد من حذف App.test.js بحيث لا ينتج عنه نتائج غير مرغوب فيها أثناء إجراء الاختبارات.

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

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

In Counter.test.js، نقوم أولاً باستيراد ملف Counter المكون ، ثم ابدأ الاختبار بامتداد describe() وظيفة لوصف جميع الوظائف المختلفة التي يمكن أن تحدث داخل المكون.

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

يمكن إجراء اختبار مكونات React بطريقة يمكننا من خلالها استخدام عارض اختبار لإنشاء قيمة قابلة للتسلسل بسرعة لشجرة React بدلاً من إنشاء واجهة مستخدم رسومية ، والتي قد تتضمن إنشاء التطبيق الكامل.

اختبار قيمة العداد الأولية

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

أول شيء سنختبره هو قيمة العد الأولي وكيف يتعامل المكوّن مع الخاصية التي تحددها. مع ال it() طريقة ، نتحقق مما إذا كان تطبيق العداد يعرض بالفعل قيمة العد الأولية الدقيقة التي تم تمريرها كخاصية ، وهي 0 في هذه الحالة ، واجتياز وظيفة رد الاتصال التي تصف جميع الإجراءات التي ستحدث داخل الاختبار:


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(screen.getByTestId("count").textContent).toEqual(0);
  });
});

هنا ، استخدمنا ملف screen مثيل من مكتبة React Testing لتصيير المكون لأغراض تجريبية. من المفيد تقديم نسخة وهمية من أحد المكونات المراد اختبارها. ومنذ ذلك الحين

العنصر الذي يحمل count لا بد أن تتغير القيمة ديناميكيًا ، فنحن نستخدم screen.getByTestId() وظيفة للاستماع إليها وجلب قيمتها مع textContent خاصية.

تحقق من دليلنا العملي العملي لتعلم Git ، مع أفضل الممارسات ، والمعايير المقبولة في الصناعة ، وورقة الغش المضمنة. توقف عن أوامر Googling Git وفي الواقع تعلم ذلك!

ملحوظة:screen تقوم الدالة بإرجاع عقدة DOM مطابقة لأي استعلام أو تلقي خطأ إذا لم يتم العثور على عنصر.

ثم، في Counter.js المكون ، سوف نستمع إلى

عنصر أثناء الاختبار عن طريق تعيين data-testid السمة للعنصر بقيمة count:


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  const increment = () => {
    setCount((prev) => prev + 1);
  };
  const decrement = () => {
    setCount((prev) => prev - 1);
  };
  const restart = () => {
    setCount(0);
  };
  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
      	
        Count: <h3 data-testid="count">{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

لاختبار ما إذا كان الأولي count القيمة تساوي 0، نحن نستخدم ال expect() طريقة لوصف ما هو متوقع من الاختبار الذي وضعناه! في حالتنا ، نتوقع أن تكون قيمة العد الأولي 0 لذلك نستخدم ال toEqual() الطريقة ، والتي تُستخدم لتحديد ما إذا كانت قيم كائنين متطابقتين. بدلاً من تحديد هوية الكائن ، فإن toEqual() المطابق بشكل متكرر يتحقق من جميع الحقول من أجل المساواة.

ملحوظة: يركز الاختبار بشكل خاص على المعلومات التي تقدمها ؛ في مثالنا ، هذا هو Counter المكون الذي تلقى ملف initialCount دعم. يشير هذا إلى أنه حتى لو كان ملفًا آخر - فلنقل ، دعنا App.js—دعائم مفقودة في ملف Counter المكون ، سيستمر الاختبار لأنه يركز فقط على Counter.js ولا يعرف كيفية استخدام Counter عنصر. بالإضافة إلى ذلك ، نظرًا لأن الاختبارات مستقلة عن بعضها البعض ، فلن يؤثر عرض نفس المكون مع عناصر مختلفة في الاختبارات الأخرى على أي منهما.

الآن ، يمكننا إجراء الاختبار المحدد:

$ yarn test

يجب أن يفشل الاختبار:

FAIL  src/Counter.test.js
  Counter
    × counter displays correct initial count (75 ms)

  ● Counter › counter displays correct initial count

    expect(received).toEqual(expected) // deep equality

    Expected: 0
    Received: "0"

       5 |   it("counter displays correct initial count", () => {
       6 |     render();
    >  7 |     expect(screen.getByTestId("count").textContent).toEqual(0);
         |                                                     ^
       8 |   });
       9 | });
      10 |

      at Object. (src/Counter.test.js:7:53)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.929 s, estimated 2 s
Ran all test suites related to changed files.

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


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
     
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });
});

الآن ، سوف يجتاز الكود الخاص بنا الاختبار الأول:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.271 s
Ran all test suites related to changed files.

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

يمكن أن يساعد العثور على توازن جيد في ترقية جودة البرنامج ، مع الحد الأدنى من التأثير السلبي على إنتاجيتك وسرعتك.

اختبار زر الزيادة

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

نظرًا لأن قيمة الزر ليست ديناميكية ، أي أنها ستظل دائمًا ذات قيمة Increment بداخله ، نستخدم ملف getByRole() طريقة بدلا من getByTestId() للاستعلام عن DOM.

عند استخدام getByRole() طريقة ، دور يصف عنصر HTML.

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

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

أولاً ، نكتب اختبارًا لمعرفة ما إذا كانت قيمة العد تزيد بمقدار 1 من قيمتها الأولية البالغة 0:


import { fireEvent, render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });

  it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue = Number(screen.getByTestId("count").textContent);
    expect(countValue).toEqual(1);
  });
});

وينتج عنه:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (66 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.405 s
Ran all test suites related to changed files.

بعد ذلك ، يمكننا أيضًا كتابة اختبار للتحقق مما إذا كان count كانت القيمة 0 قبل النقر فوق الزر بتحديد اثنين expect() الطرق - واحدة قبل إطلاق حدث النقر والأخرى بعد إطلاق حدث النقر:


it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    let countValue1 = Number(screen.getByTestId("count").textContent);
    expect(countValue1).toEqual(0);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue2 = Number(screen.getByTestId("count").textContent);
    expect(countValue2).toEqual(1);
});

الاختبارات مازالت مجتازة:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (82 ms)
    √ count should increment by 1 if increment button is clicked (60 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.388 s
Ran all test suites related to changed files.

اختبار زر التناقص

بنفس الطريقة التي كتبنا بها اختبار Increment زر ، نحدد اختبار Decrement زر مثل ذلك:


it("count should decrement by 1 if decrement button is clicked", () => {
  render(<Counter initialCount={0} />);
  fireEvent.click(screen.getByRole("button", { name: "Decrement" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(-1);
});

وينتج عنه:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (73 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.346 s
Ran all test suites related to changed files.

اختبار زر إعادة التشغيل

وعلى غرار Increment و Decrement الأزرار ، نحدد اختبار Restart زر مثل ذلك:


it("count should reset to 0 if restart button is clicked", () => {
  render(<Counter initialCount={50} />);
  fireEvent.click(screen.getByRole("button", { name: "Restart" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(0);
});

لغرض الاختبار ، تم تعيين القيمة الأولية على 50 (قيمة عشوائية) ، وعند تشغيل الاختبار ، تنجح الاختبارات الأربعة جميعها:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)
    √ count should increment by 1 if increment button is clicked (57 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (16 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.583 s
Ran all test suites related to changed files.

اختبار زر التبديل

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


it("count invert signs if switch signs button is clicked", () => {
  render(<Counter initialCount={50} />);
  let countValue1 = Number(screen.getByTestId("count").textContent);
  expect(countValue1).toEqual(50);
  fireEvent.click(screen.getByRole("button", { name: "Switch signs" }));
  let countValue2 = Number(screen.getByTestId("count").textContent);
  expect(countValue2).toEqual(-50);
});

وينتج عنه:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (91 ms)
    √ count should increment by 1 if increment button is clicked (72 ms)
    √ count should decrement by 1 if increment button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (19 ms)
    √ count invert signs if switch signs button is clicked (14 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        3.104 s
Ran all test suites related to changed files.

وش! لقد نجحت جميع الاختبارات في تطبيق العداد الخاص بنا.

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

بشكل عام ، نقطة البداية الجيدة للاختبار هي:

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

أفضل الممارسات لاختبار الوحدة

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

  • تجنب الاختبارات غير الضرورية: لا تأتي الاختبارات الجيدة بتوقعات غير ضرورية أو حالات اختبار.
    يمكننا إيجاد فهم أفضل من خلال إلقاء نظرة على الاختبارات أدناه:

test('the success modal is visible', () => {});
test('the success modal has a success message', () => {});

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

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

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

  • ضع منطق الأعمال في وظائف خالصة بدلاً من مكونات واجهة المستخدم.

وفي الختام

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

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

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

  • getByRole() - يستعلم عن عنصر مع ضمان إمكانية الوصول إليه باستخدام الدور والنص الصحيحين
  • getByLabelText() - إنه استعلام ممتاز للتفاعل مع عناصر النموذج ، كما أنه يتحقق من أن تسمياتنا مرتبطة بشكل صحيح بمدخلاتنا عبر سمات for و id
  • getByText() - في حالة عدم توفر أي من الاستعلامات السابقة ، فإن getByText() ستكون الطريقة مفيدة في الوصول إلى العناصر بناءً على النص المرئي للمستخدم
  • getByPlaceholderText(): هذا الاستعلام مفضل جدًا على معرف الاختبار عندما يكون كل ما عليك الاستعلام عن عنصر هو عنصرًا نائبًا.

نأمل أن يكون هذا الدليل مفيدًا لك! يمكنك الوصول إلى المستودع لهذا الدليل والتلاعب بكل ما فيه ، باستخدام هذا الارتباط على جيثب.

الطابع الزمني:

اكثر من ستاكابوز