اختار المؤلف Vets Who Code لتلقي تبرع كجزء من برنامج Write for DOnations.
مقدمة
الحصول على تغطية اختبار قوية ضروري لبناء الثقة في تطبيق الويب الخاص بك. Jest هو برنامج تشغيل اختبارات JavaScript يوفر موارد لكتابة الاختبارات وتشغيلها. مكتبة اختبارات رياكت توفر مجموعة من مساعدي الاختبارات التي تهيئ اختباراتك بناءً على تفاعلات المستخدم بدلاً من تفاصيل تنفيذ المكونات. كل من Jest و React Testing Library مدمجين مسبقًا مع Create React App ويتماشون مع المبدأ التوجيهي الذي يفيد بأن اختبار التطبيقات يجب أن يشبه كيف سيتم استخدام البرنامج.
في هذا البرنامج التعليمي، ستختبر الشفرة الحية والتفاعلات في مشروع عيني يحتوي على عناصر واجهة مستخدم متنوعة. ستستخدم Jest لكتابة وتشغيل الاختبارات الوحدوية، وستنفذ مكتبة اختبارات رياكت كمكتبة مساعدة لنموذج DOM (Document Object Model) للتعامل مع التفاعل مع المكونات.
المتطلبات المسبقة
لإكمال هذا البرنامج التعليمي، ستحتاج إلى:
-
Node.js الإصدار 14 أو أحدث مثبت على جهازك المحلي. لتثبيت Node.js على macOS أو Ubuntu 18.04، اتبع الخطوات الموجودة في كيفية تثبيت Node.js وإنشاء بيئة تطوير محلية على macOS أو القسم التثبيت باستخدام PPA من كيفية تثبيت Node.js على Ubuntu 18.04.
-
يجب أن يكون لديك إصدار
npm
5.2 أو أعلى على جهازك المحلي، الذي ستحتاج إليه لاستخدام Create React App وnpx
في المشروع العيني. إذا لم تقم بتثبيتnpm
بجانبNode.js
، فافعل ذلك الآن. بالنسبة لنظام Linux، استخدم الأمرsudo apt install npm
.- لتعمل حزم
npm
في هذا البرنامج التعليمي، قم بتثبيت حزمةbuild-essential
. بالنسبة لنظام Linux، استخدم الأمرsudo apt install build-essential
.
- لتعمل حزم
-
Git يجب أن يكون مثبتًا على جهازك المحلي. يمكنك التحقق مما إذا كان Git مثبتًا على جهاز الكمبيوتر الخاص بك أو القيام بعملية التثبيت لنظام التشغيل الخاص بك باستخدام كيفية تثبيت Git على Ubuntu 20.04.
-
المعرفة بـ React، التي يمكنك تطويرها باستخدام سلسلة كيفية البرمجة في React.js. نظرًا لأن المشروع المثالي قد تم تهيئته مسبقًا باستخدام Create React App، فليس عليك تثبيتها بشكل منفصل.
-
المعرفة بـ Jest كمشغل اختبارات أو إطار عمل مفيدة ولكن غير مطلوبة. نظرًا لأن Jest مُعبأة مسبقًا مع Create React App، فليس عليك تثبيتها بشكل منفصل.
الخطوة 1 — إعداد المشروع
في هذه الخطوة، ستقوم بنسخ مشروع عينة وتشغيل مجموعة الاختبارات. يستخدم المشروع العيني ثلاثة أدوات رئيسية: Create React App و Jest و React Testing Library. يُستخدم Create React App لبدء تطبيق React ذو صفحة واحدة. يُستخدم Jest كمشغل اختبارات، وتوفر React Testing Library مساعدات اختبار لتنظيم الاختبارات حول تفاعلات المستخدم.
للبدء، ستقوم بنسخ تطبيق React مُعد مسبقًا من GitHub. ستعمل مع تطبيق دليل الكلاب، وهو مشروع عيني يستفيد من واجهة برمجة التطبيقات للكلاب لبناء نظام بحث وعرض لمجموعة من صور الكلاب بناءً على فصيلة محددة.
لنسخ المشروع من Github، افتح الطرفية الخاصة بك وقم بتشغيل الأمر التالي:
سترى إخراجًا مشابهًا لهذا:
OutputCloning into 'doggy-directory'...
remote: Enumerating objects: 64, done.
remote: Counting objects: 100% (64/64), done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 64 (delta 21), reused 55 (delta 15), pack-reused 0
Unpacking objects: 100% (64/64), 228.16 KiB | 3.51 MiB/s, done.
قم بالتغيير إلى مجلد doggy-directory
:
قم بتثبيت تبعيات المشروع:
سيقوم أمر npm install
بتثبيت جميع تبعيات المشروع المُعرفة في ملف package.json
.
بعد تثبيت التبعيات، يمكنك إما عرض النسخة المنشورة للتطبيق أو يمكنك تشغيل التطبيق محليًا باستخدام الأمر التالي:
إذا اخترت تشغيل التطبيق محليًا، سيتم فتحه على http://localhost:3000/
. سترى الإخراج التالي في الطرفية:
OutputCompiled successfully!
You can now view doggy-directory in the browser.
Local: http://localhost:3000
On Your Network: http://network_address:3000
بعد التشغيل، ستبدو الصفحة الرئيسية للتطبيق بهذا الشكل:
تم تثبيت تبعيات المشروع، والتطبيق يعمل الآن. فيما بعد، افتح محطة أخرى وقم بتشغيل الاختبارات بالأمر التالي:
الأمر npm test
يبدأ الاختبارات في وضع المراقبة التفاعلية مع Jest كمشغل اختبارات له. عندما يكون في وضع المراقبة، تعيد الاختبارات تشغيلها تلقائيًا بعد تغيير ملف. ستقوم الاختبارات بالتشغيل كلما قمت بتغيير ملف وستخبرك ما إذا كان هذا التغيير قد نجح في الاختبارات.
بعد تشغيل npm test
للمرة الأولى، سترى هذا الإخراج في المحطة:
OutputNo tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press q to quit watch mode.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press Enter to trigger a test run.
الآن بعد أن لديك تطبيق المثال ومجموعة الاختبارات الجارية، يمكنك بدء الاختبار على صفحة الهبوط.
الخطوة 2 — اختبار صفحة الهبوط
بشكل افتراضي، سيبحث Jest عن الملفات التي تحمل امتداد .test.js
والملفات التي تحمل امتداد .js
في مجلدات __tests__
. عندما تقوم بإجراء تغييرات على ملفات الاختبار ذات الصلة، سيتم اكتشافها تلقائيًا. مع تعديل حالات الاختبار، سيتم تحديث الإخراج تلقائيًا. الملف التجريبي الذي تم إعداده لمشروع العينة doggy-directory
مهيأ مع كود أدنى قبل إضافة نماذج الاختبار. في هذه الخطوة، ستقوم بكتابة اختبارات للتحقق من أن صفحة الهبوط في التطبيق ستحمل قبل القيام بعملية بحث.
افتح src/App.test.js
في محرر النصوص الخاص بك لرؤية الكود التالي:
A minimum of one test block is required in each test file. Each test block accepts two required parameters: the first argument is a string representing the name of the test case; the second argument is a function that holds the expectations of the test.
داخل الدالة، هناك طريقة render
التي يوفرها مكتبة اختبار رياكت لتقديم مكونك إلى DOM. مع المكون الذي تريد اختباره مقدماً إلى DOM بيئة الاختبار، يمكنك الآن البدء في كتابة الكود للتأكد من الوظيفة المتوقعة.
ستضيف كتلة اختبار إلى الدالة render
التي ستختبر ما إذا كانت صفحة الهبوط تقدم بدقة قبل أي استدعاء للواجهة البرمجية أو اختيارات تتم. أضف الكود المظلل أسفل الدالة render
:
تُستخدم دالة expect
في كل مرة تريد فيها التحقق من نتيجة معينة، وتقبل وسيطة واحدة تمثل القيمة التي ينتجها كودك. معظم دوال expect
مقترنة بدالة matcher للتأكد من شيء معين بشأن قيمة معينة. لمعظم هذه التأكيدات، ستستخدم دوال مقارنة إضافية يوفرها jest-dom لتسهيل التحقق من الجوانب الشائعة الموجودة في DOM. على سبيل المثال، .toHaveTextContent
هو مقارن الدالة لدالة expect
في السطر الأول، بينما getByRole("heading")
هو المحدد لالتقاط عنصر DOM.
توفر مكتبة اختبار رياكت كائن screen
كطريقة مريحة للوصول إلى الاستعلامات اللازمة للتأكد من صحة بيئة DOM الاختبارية. بشكل افتراضي، توفر مكتبة اختبار رياكت استعلامات تسمح لك بتحديد العناصر داخل DOM. هناك ثلاث فئات رئيسية من الاستعلامات:
getBy*
(الأكثر استخدامًا)queryBy*
(المستخدم عند اختبار غياب عنصر دون رمي خطأ)findBy*
(المستخدم عند اختبار الشفرة الغير متزامنة)
يُعتبر كل نوع من الاستعلامات له غرض محدد سيُوضح لاحقًا في البرنامج التعليمي. في هذه الخطوة، ستركز على استعلام getBy*
، وهو نوع الاستعلام الأكثر شيوعًا. لرؤية قائمة شاملة لمختلف تفاصيل الاستعلام، يُمكنك مراجعة “ورقة غش React” cheatsheet للبحث.
أدناه صورة موضحة لصفحة هبوط دليل الكلاب تشير إلى كل قسم يغطيه الاختبار الأول (على تقديم صفحة الهبوط):
توقع كل دالة expect
ضد التالي (الموضح في الصورة الموضحة أعلاه):
- تتوقع أن يحتوي العنصر بدور heading على تطابق جزئي لـ دليل الكلاب.
- تتوقع أن يحتوي إدخال الاختيار على قيمة عرض دقيقة اختر فصيلة.
- تتوقع أن يكون زر البحث معطلًا لأن لم يتم اختيار أي شيء.
- تتوقع وجود صورة العنصر المكاني في المستند لأنه لم يتم إجراء بحث.
عند الانتهاء، احفظ ملف src/App.test.js
. نظرًا لتشغيل الاختبارات في وضع المراقبة، ستُسجّل التغييرات تلقائيًا. إذا لم تُسجّل التغييرات تلقائيًا، قد تحتاج إلى إيقاف وإعادة تشغيل مجموعة الاختبارات.
الآن، عندما تنظر إلى اختباراتك في الطرفية، سترى الناتج التالي:
Output PASS src/App.test.js
✓ renders the landing page (172 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.595 s, estimated 5 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
في هذه الخطوة، كتبت اختبارًا أوليًا للتحقق من العرض الأولي لصفحة الهبوط لدليل Doggy. في الخطوة التالية، ستتعلم كيفية تزييف استدعاء API لاختبار الشفرة الغير متزامنة.
الخطوة 3 — تزييف طريقة fetch
في هذه الخطوة، ستستعرض أحد الطرق لتزييف طريقة fetch
في جافاسكريبت. بينما هناك طرق عديدة لتحقيق ذلك، سيستخدم هذا التنفيذ أساليب spyOn
و mockImplementation
في Jest.
عند الاعتماد على واجهات برمجة التطبيقات الخارجية، هناك فرصة لأن تتوقف واجهة برمجة التطبيقات أو تأخذ وقتًا طويلاً لإرجاع استجابة. يوفر تزييف طريقة fetch
بيئة متسقة ومتوقعة، مما يمنحك المزيد من الثقة في اختباراتك. يُعتبر آلية تزييف واجهة برمجة التطبيقات ضرورية لتشغيل الاختبارات بشكل صحيح عند استخدام واجهة برمجة تطبيقات خارجية.
ملاحظة: في محاولة لتبسيط هذا المشروع، ستقوم بتقليد طريقة الاسترجاع. ومع ذلك، يُنصح باستخدام حل أكثر قوة مثل Mock Service Worker (MSW) عند تقليد الكود اللازم للأكواد الغير متزامنة في أكواد أكبر جاهزة للإنتاج.
افتح src/mocks/mockFetch.js
في محرر النصوص لمراجعة كيفية عمل طريقة mockFetch
:
تُعيد طريقة mockFetch
كائنًا يشبه بشكل كبير هيكل ما يعود به استدعاء fetch
في الرد على استدعاءات API داخل التطبيق. تعتبر طريقة mockFetch
ضرورية لاختبار الوظائف غير المتزامنة عبر منطقتين في تطبيق Doggy Directory: القائمة المنسدلة التي تملأ قائمة السلالات واستدعاء API لاسترجاع صور الكلاب عندما يتم البحث.
أغلق src/mocks/mockFetch.js
. الآن بعد أن فهمت كيفية استخدام طريقة mockFetch
في اختباراتك، يمكنك استيرادها إلى ملف الاختبار الخاص بك. سيتم تمرير الدالة mockFetch
كوسيط لطريقة mockImplementation
وسيتم استخدامها بمثابة تنفيذ مزيف لواجهة fetch.
في src/App.test.js
، أضف الأسطر المظللة من الكود لاستيراد طريقة mockFetch
:
هذا الكود سيعد ويفك تنفيذ التقليد حتى تبدأ كل اختبار من منصة متساوية.
jest.spyOn(window, "fetch");
ينشئ دالة تقليدية ستتتبع استدعاءات طريقة fetch
المرتبطة بالمتغير العالمي window في DOM.
.mockImplementation(mockFetch);
يقبل دالة سيتم استخدامها لتنفيذ الأسلوب المزيف. نظرًا لأن هذا الأمر يعتمد تنفيذ fetch
الأصلي، فإنه سيتم تشغيله كلما تم استدعاء fetch
داخل رمز التطبيق.
عند الانتهاء، احفظ ملف src/App.test.js
.
الآن، عندما تنظر إلى اختباراتك في الطرفية، ستتلقى الإخراج التالي:
Output console.error
Warning: An update to App inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at App (/home/sammy/doggy-directory/src/App.js:5:31)
18 | })
19 | .then((json) => {
> 20 | setBreeds(Object.keys(json.message));
| ^
21 | });
22 | }, []);
23 |
...
PASS src/App.test.js
✓ renders the landing page (429 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.178 s, estimated 2 s
Ran all test suites related to changed files.
يُخطرك التحذير بأن حدث تحديث للحالة عندما لم يكن متوقعًا. ومع ذلك، يشير الإخراج أيضًا إلى أن الاختبارات قامت بتحاكي بنجاح الطريقة fetch
.
في هذه الخطوة، قمت بتقليد الطريقة fetch
ودمجت هذه الطريقة في مجموعة اختبار. على الرغم من أن الاختبار يمر، إلا أنه لا يزال عليك معالجة التحذير.
الخطوة 4 — إصلاح تحذير act
في هذه الخطوة، ستتعلم كيفية إصلاح تحذير act
الذي ظهر بعد التغييرات في الخطوة 3.
يحدث تحذير act
لأنك قمت بتقليد الطريقة fetch
، وعندما يتم تثبيت المكون، فإنه يقوم بعملية استدعاء API لالتقاط قائمة السلالات. تُخزن قائمة السلالات في متغير حالة يملأ عنصر الـ option
داخل مدخل الاختيار.
الصورة أدناه توضح كيفية مظهر مدخل الاختيار بعد إجراء استدعاء API ناجح لملء قائمة السلالات:
يتم إطلاق التحذير لأن الحالة مُضبَّطة بعد انتهاء تقديم كتلة الاختبار المكوّن.
لإصلاح هذه المشكلة، أضف التعديلات المُظلَّلة التالية إلى حالة الاختبار في src/App.test.js
:
تُخبر كلمة المفتاح async
Jest بأن الكود الغير متزامن يعمل نتيجة للمكالمة إلى واجهة برمجة التطبيقات التي تحدث عند تحميل المكوّن.
A new assertion with the findBy
query verifies that the document contains an option with the value of husky
. findBy
queries are used when you need to test asynchronous code that is dependent on something being in the DOM after a period of time. Because the findBy
query returns a promise that gets resolved when the requested element is found in the DOM, the await
keyword is used within the expect
method.
عند الانتهاء، احفظ التغييرات التي تم إجراؤها في src/App.test.js
.
مع الإضافات الجديدة، ستلاحظ الآن أن تحذير act
لم يعد موجودًا في اختباراتك:
Output PASS src/App.test.js
✓ renders the landing page (123 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.942 s, estimated 2 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
في هذه الخطوة، تعلمت كيفية إصلاح التحذير act
الذي يمكن أن يحدث عند العمل مع الكود غير المتزامن. في الخطوة التالية، ستضيف حالة اختبار ثانية للتحقق من الوظائف التفاعلية لتطبيق دليل الكلاب.
الخطوة 5 — اختبار ميزة البحث
في الخطوة النهائية، ستكتب حالة اختبار جديدة للتحقق من ميزة البحث وعرض الصور. ستستفيد من مجموعة متنوعة من الاستعلامات وطرق واجهة برمجة التطبيقات لتحقيق التغطية الاختبارية السليمة.
ارجع إلى الملف src/App.test.js
في محرر النصوص الخاص بك. في أعلى الملف، قم بإستيراد مكتبة المساعد user-event
والطريقة اللازمة للإنتظار waitForElementToBeRemoved
بشكل غير متزامن إلى ملف الاختبار بإستخدام الأوامر المميزة التالية:
ستقوم باستخدام هذه الاستيرادات لاحقًا في هذا القسم.
بعد طريقة الاختبار test()
الأولية، قم بإضافة كتلة جديدة للاختبار الغير متزامن وقم بتقديم عنصر App
باستخدام الكود التالي:
بعد تقديم العنصر، يمكنك الآن إضافة وظائف تتحقق من الميزات التفاعلية لتطبيق دليل الكلاب.
لا تزال في src/App.test.js
، قم بإضافة كتل الكود المميزة ضمن الطريقة test()
الثانية:
الجزء المميز أعلاه سيحاكي اختيار فصيلة كلب والتحقق من أن القيمة الصحيحة معروضة.
استعلم العنصر المحدد باستخدام الدالة getByRole
وقم بتعيينه إلى المتغير select
.
بنفس الطريقة التي قمت بها بتصحيح تحذير act
في الخطوة 4، استخدم الاستعلام findByRole
للانتظار حتى يظهر الخيار cattledog
في المستند قبل المتابعة مع التحقق من المزيد من الادعاءات.
الكائن userEvent
المستورد في وقت سابق سيحاكي تفاعلات المستخدم الشائعة. في هذا المثال، تختار طريقة selectOptions
الخيار cattledog
الذي انتظرته في السطر السابق.
السطر الأخير يؤكد أن المتغير select
يحتوي على قيمة cattledog
المحددة أعلاه.
القسم التالي الذي ستقوم بإضافته إلى كتلة الاختبارات في جافاسكريبت test()
سيبدأ طلب البحث للعثور على صور الكلاب استنادًا إلى السلالة المحددة وتأكيد وجود حالة التحميل.
أضف السطور المظللة:
استعلم الكائن getByRole
يحدد زر البحث ويسند ذلك إلى المتغير searchBtn
.
يتحقق العملي toBeDisabled
jest-dom من أن زر البحث غير معطل عندما يتم اختيار سلالة.
تحاكي الطريقة click
على كائن userEvent
النقر على زر البحث.
الدالة waitForElementToBeRemoved
المساعدة الكاملة المستوردة مسبقًا ستنتظر ظهور واختفاء رسالة Loading أثناء قيام مكالمة API البحث. queryByText
داخل استدعاء waitForElementToBeRemoved
تتحقق من غياب عنصر دون إلقاء خطأ.
الصورة أدناه تُظهر الحالة التحميل التي ستُعرَض عندما يكون البحث قيد التقدم:
بعد ذلك، أضف الكود الجافا سكريبت التالي للتحقق من عرض الصورة وعدد النتائج:
استعلام getAllByRole
سيختار كل صور الكلاب ويعينها إلى متغير dogImages
. الاستعلام بالصيغة *AllBy*
يعيد مصفوفة تحتوي على عناصر متعددة تطابق الدور المحدد. الصيغة *AllBy*
تختلف عن الصيغة ByRole
، التي يمكن أن تعيد فقط عنصرًا واحدًا.
كانت التنفيذ المشابه لـ fetch
يحتوي على عنواني URL للصور ضمن الاستجابة. باستخدام جهاز المطابقة toHaveLength
في Jest، يمكنك التحقق من أن هناك صورتين معروضتين.
ستقوم الاستعلام getByText
بالتحقق من ظهور العدد الصحيح من النتائج في الزاوية اليمنى.
تتحقق التأكيدات باستخدام المطابقين toHaveAccessibleName
من أن النص البديل المناسب مرتبط بالصور الفردية.
A completed search displaying images of the dog based on the breed selected along with the number of results found will look like this:
عندما تجمع جميع قطع الشفرة الجديدة لجافا سكريبت، سيبدو ملف App.test.js
كالتالي:
احفظ التغييرات التي تم إجراؤها في src/App.test.js
.
عندما تستعرض الاختبارات الخاصة بك، سيكون الإخراج النهائي في الطرفية الآن على النحو التالي:
Output PASS src/App.test.js
✓ renders the landing page (273 ms)
✓ should be able to search and display dog image results (123 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.916 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
في هذه الخطوة النهائية، قمت بإضافة اختبار يتحقق من وظائف البحث والتحميل والعرض لتطبيق دليل الكلاب. بعد كتابة التأكيد النهائي، تعرف الآن أن تطبيقك يعمل.
الاستنتاج
خلال هذا البرنامج التعليمي، كتبت حالات اختبار باستخدام Jest ومكتبة اختبار React ومطابقات jest-dom. من خلال البناء التدريجي، كتبت اختبارات بناءً على كيفية تفاعل المستخدم مع واجهة المستخدم. كما تعلمت الفروق بين الاستعلامات getBy*
، findBy*
، وqueryBy*
وكيفية اختبار الشفرات البرمجية غير المتزامنة.
لمعرفة المزيد حول المواضيع المذكورة أعلاه، تفضل بالاطلاع على الوثائق الرسمية لـ Jest، مكتبة اختبار React، و jest-dom. يمكنك أيضًا قراءة أخطاء شائعة مع مكتبة اختبار React لكنت سي. دود للتعرف على أفضل الممارسات عند العمل مع مكتبة اختبار React. للمزيد حول استخدام اختبارات الصورة الشاملة داخل تطبيق React، تحقق من كيفية كتابة اختبارات الصورة الشاملة.