العربية
  • اختبار
  • اختبار e2e
  • اختبار التكامل
  • تطوير
  • إنتاجية
  • jest
  • puppeteer

دليل سريع لكتابة اختبارات من البداية إلى النهاية باستخدام jest-puppeteer

تقدم هذه المقالة دليلًا سريعًا لكتابة اختبارات فعالة من البداية إلى النهاية باستخدام jest-puppeteer، مع التركيز على عملية الإعداد وواجهات برمجة التطبيقات المستخدمة بشكل شائع والسيناريوهات العملية للاختبارات باستخدام تطبيق قائمة مهام بسيط كمثال.

Yijun
Yijun
Developer

كجزء من التزامنا بضمان جودة Logto والتحسين المستمر، نستخدم jest-puppeteer للاختبارات الآلية من البداية إلى النهاية. وهذا يسمح لنا بإجراء تعديلات سريعة على تطوير Logto دون انقطاع.

في هذه المقالة، سنشارك تجربتنا في كتابة نصوص اختبار jest-puppeteer بطريقة فعالة باستخدام تطبيق قائمة مهام بسيط كمثال. هدفنا هو مساعدتك في البدء بسرعة في كتابة كود اختبار jest-puppeteer لمشاريعك الخاصة.

مقدمة إلى الاختبارات من البداية إلى النهاية باستخدام jest-puppeteer

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

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

Puppeteer هو مكتبة JavaScript تم تطويرها بواسطة فريق Chrome وتوفر واجهة برمجة تطبيقات عالية المستوى للتحكم في المتصفحات بدون رأس Chrome أو Chromium. وهذا يجعلها اختيارًا مثاليًا لأتمتة التفاعلات مع المتصفح في الاختبارات من البداية إلى النهاية.

jest-puppeteer هو إعداد مسبق لـ Jest يتيح الاختبارات من البداية إلى النهاية باستخدام Puppeteer. يوفر واجهة برمجة تطبيقات بسيطة لإطلاق مثيلات المتصفح الجديدة والتفاعل مع صفحات الويب من خلالها.

الآن بعد أن أصبحت لديك فكرة أساسية عن الأدوات الأساسية، دعنا نتعرف على إعداد بيئة الاختبار الخاصة بك.

إعداد بيئة الاختبار الخاصة بك

إعداد بيئة الاختبار لإجراء الاختبارات من البداية إلى النهاية باستخدام jest-puppeteer هو عملية بسيطة تتضمن ثلاث خطوات رئيسية:

  1. تثبيت التبعية

في مشروعك (أو قم بإنشاء مشروع جديد)، افتح الطرفية وقم بتشغيل:

ثم اذهب إلى node_modules/puppeteer وقم بتثبيت Chromium (الذي يحتاجه Puppeteer):

  1. تكوين Jest

بعد ذلك، تحتاج إلى تكوين Jest للعمل بسلاسة مع Puppeteer.

قم بإنشاء ملف تكوين Jest (مثل jest.config.js) في الدليل الجذري لمشروعك إذا لم يكن لديك واحد بالفعل. في ملف التكوين هذا، حدد jest-puppeteer كإعداد مسبق:

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

  1. كتابة الاختبارات الخاصة بك

قم بإنشاء ملفات اختبار في مشروعك، عادةً ما تكون مسماة بامتداد .test.js. سيكتشف Jest هذه الملفات تلقائيًا وينفذها.

إليك مثال من مستندات Jest:

ثم تنفيذ الأمر لتشغيل الاختبار:

باتباع هذه الخطوات الثلاث، سيكون لديك بيئة اختبار مجهزة جيدًا لإجراء الاختبارات من البداية إلى النهاية باستخدام jest-puppeteer.

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

واجهة برمجة التطبيقات المستخدمة بشكل شائع

في الخطوات القادمة، سنتعمد على واجهات برمجة التطبيقات المقدمة من Jest و Puppeteer و jest-puppeteer لمساعدتنا في اختباراتنا.

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

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

لأن واجهات برمجة Puppeteer لم يتم تصميمها في الأصل للاختبار، قد يكون استخدامهم في كتابة الاختبارات تحديًا. لتبسيط عملية كتابة اختبارات Puppeteer، يتضمن jest-puppeteer مكتبة مضمنة تُسمى expect-puppeteer. توفر مجموعة من واجهات برمجة التطبيقات البسيطة وسهلة الاستخدام. يتم توضيح المكتبة بالتفصيل في وثائقها، والتي تقدم ميزات مفيدة مختلفة، كما هو موضح في الأمثلة أدناه:

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

حان الوقت الآن للبدء في تعلم كيفية كتابة كود الاختبار باستخدام تطبيق قائمة المهام البسيط لدينا.

تطبيق قائمة المهام البسيط

بافتراض أن لدينا تطبيق قائمة مهام بسيط يعمل على http://localhost:3000، فإن الكود HTML الرئيسي لهذا التطبيق هو كما يلي:

عند إجراء الاختبارات من البداية إلى النهاية، نسعى لتغطية السيناريوهات التالية:

  1. عند الوصول إلى عنوان URL الخاص بالتطبيق، يجب تحميل التطبيق وبياناته بشكل صحيح.
  2. يجب أن يكون زر التحقق الخاص بالعنصر قابلًا للنقر عليه.
  3. يجب أن يؤدي النقر على الرابط الخارجي داخل ملاحظات العنصر إلى فتح الرابط في علامة تبويب جديدة.
  4. يمكن إضافة عنصر من النموذج.

في وقت لاحق، سنكتب كود الاختبار للتحقق من هذه السيناريوهات.

التحقق من تحميل التطبيق وبياناته

نعتبر أن التطبيق قد تم تحميله بنجاح عندما يتم تلبية الشروط التالية:

  1. يجب أن يوجد عنصر بوحدة تعريف "app" على الصفحة بعد الوصول إلى عنوان URL الخاص بالتطبيق.
  2. يجب أن يتم عرض البيانات داخل التطبيق بشكل صحيح.

لذا، قد كتبنا كود الاختبار التالي:

في الكود:

  • page.goto يعادل إدخال "http://localhost:3000" في المتصفح، مما يسمح لك بالتنقل إلى أي عنوان URL.
  • page.waitForSelector يُستخدم للانتظار لمُطابقة محدد CSS معين مع عنصر معين، مع وقت انتظار افتراضي يبلغ 30 ثانية.
  • expect(page).toMatchElement من مكتبة expect-puppeteer، إنه مشابه لـ page.waitForSelector، ولكنه يدعم أيضًا مطابقة النص داخل العنصر. بالإضافة إلى ذلك، فإن الوقت الافتراضي لـ toMatchElement هو 500 مللي ثانية فقط.

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

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

عادةً، هناك طريقتان شائعتان لتحقيق ذلك:

  1. زيادة وقت الانتظار للتحقق

من رسالة الخطأ، يمكننا رؤية أن وقت الانتظار الافتراضي لـ toMatchElement مُحدد على 500 مللي ثانية. يمكننا زيادة وقت الانتظار عن طريق إضافة خيار "timeout" إلى الدالة هكذا:

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

لذلك، نستخدم هذه الطريقة فقط عندما نكون متأكدين من وقت الانتظار المطلوب، مثل في السيناريوهات مثل "ظهور تلميح الأدوات بعد تمرير المؤشر فوق عنصر لأكثر من 2 ثانية.”

  1. انتظار الانتهاء من طلب الشبكة قبل إجراء التحقق

هذه الطريقة الصحيحة. على الرغم من أننا قد لا نعرف كم من الوقت سيستغرق الطلب البيانات، لكن الانتظار لانتهاء طلب الشبكة دائمًا ما يكون اختيارًا آمنًا. في هذه النقطة، يمكننا استخدام page.waitForNavigation({ waitUntil: 'networkidle0' }) للانتظار حتى تكتمل طلبات الشبكة:

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

توقع النقر على زر محدد

بعد ذلك، نحن نختبر وظيفة النقر على زر التحقق داخل عنصر.

في تطبيق المثال، لاحظنا أن جميع العناصر تشترك في نفس الهيكل. أزرار التحقق الخاصة بهم لديها محدد CSS li[class$=item] > button، والنص على الزر دائمًا "تحقق". هذا يعني أننا لا يمكننا تحديد زر التحقق لأي عنصر مباشرة. لذلك، نحن بحاجة إلى حل جديد.

لنقم بالنقر على زر "اقرأ وثيقة البدء الخاصة بـ Logto". يمكننا تقسيم هذا إلى خطوتين:

  1. أولًا، الحصول على مرجع لهذا العنصر المحدد.
  2. ثم، انقر على زر التحقق الموجود داخل هذا العنصر.

كما يظهر في الكود، نحن نستخدم دالة toMatchElement لمطابقة العنصر مع itemName مُحددًا بـ "اقرأ وثيقة البدء الخاصة بـ Logto". ثم، نستخدم مرجع readDocItem للنقر على زر النص 'تحقق' الموجود تحته.

من المهم ملاحظة أنه عند الحصول على readDocItem، يتم استخدام محدد CSS هو li[class$=item]:has(div[class$=itemName]). هذا المحدد يطابق عنصر الجذور li للعنصر، وليس itemName داخله، لأننا نعتزم النقر على الزر button تحت علامة li لاحقًا.

استخدام expect(readDocItem).toClick مشابه لـ toMatchElement. في الكود المثل، نحن نمرر { text: 'تحقق' } لمطابقة إضافية للمحتوى النصي للزر. ومع ذلك، يمكنك اختيار ما إذا كنت تريد مطابقة النصي للزر بناءً على احتياجاتك.

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

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

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

في الكود، نستخدم toClick للنقر على الرابط داخل itemNotes.

بعد ذلك، نستخدم browser.waitForTarget لالتقاط علامة تبويب تم فتحها حديثًا مع عنوان URL "https://docs.logto.io/docs/tutorials/get-started".

بعد الحصول على علامة التبويب، نستخدم target.page() للحصول على نسخة من صفحة مستند Logto ومزيد من التحقق مما إذا كانت الصفحة قد تم تحميلها.

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

توقع إنشاء عنصر من النموذج

الآن، نريد اختبار وظيفة إضافة عنصر.

نحتاج إلى ملء اسم العنصر وملاحظات العنصر في النموذج، والنقر على زر "إضافة" والتحقق مما إذا كان المحتوى الذي أضفناه يظهر في القائمة:

ستلاحظ أننا نستخدم دالة expect(page).toFill(inputSelector, content) لملء محتوى النموذج. تقوم هذه الوظيفة باستبدال كل المحتوى في المدخل بـ المحتوى.

إذا كنت ترغب في إضافة بعض الأحرف إلى المدخل دون استبدال المحتوى الحالي، يمكنك استخدام page.type(selector, content) للقيام بإضافة المحتوى المطلوب مباشرةً إلى المدخل.

ومع ذلك، عندما يكون لدينا العديد من الحقول لملء النموذج لدينا، فإن استدعاء toFill عدة مرات ليس مريحًا. في هذه الحالة، يمكننا استخدام الطريقة التالية لملء كل المحتوى في مكالمة واحدة:

المفتاح المقدم من الكائن هو السمة "name" لعنصر المدخل.

الملخص

لقد تناولنا مثالا بسيطًا لتقديم متطلبات اختبار شائعة وأساليب كتابة كود المطابقة عند استخدام jest-puppeteer للاختبارات من البداية إلى النهاية. نأمل أن يساعدك ذلك في التمكن بسرعة من أساسيات كتابة كود اختبار jest-puppeteer.