اختبار الوحدة في تطبيقات React الأصلية
نشرت: 2022-03-10يُعد React Native أحد أكثر الأطر استخدامًا لبناء تطبيقات الأجهزة المحمولة. يستهدف هذا البرنامج التعليمي المطورين الذين يرغبون في البدء في اختبار تطبيقات React Native التي يقومون بإنشائها. سنستفيد من إطار عمل اختبار Jest والإنزيم.
في هذه المقالة ، سنتعلم المبادئ الأساسية للاختبار ، ونستكشف مكتبات مختلفة لاختبار أحد التطبيقات ، ونرى كيفية اختبار الوحدات (أو المكونات) لتطبيق React Native. من خلال العمل مع تطبيق React Native ، سنعمل على ترسيخ معرفتنا بالاختبار.
ملاحظة: ستكون المعرفة الأساسية بـ JavaScript و React Native ذات فائدة كبيرة أثناء عملك من خلال هذا البرنامج التعليمي.
ما هو اختبار الوحدة؟
اختبار الوحدة هو مستوى الاختبار الذي يتم من خلاله اختبار المكونات الفردية للبرنامج. نقوم بذلك للتأكد من أن كل مكون يعمل كما هو متوقع. المكون هو أصغر جزء قابل للاختبار من البرنامج.
للتوضيح ، دعنا ننشئ مكون Button
ونحاكي اختبار الوحدة:
import React from 'react'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'; function AppButton({ onPress }) { return ( <TouchableOpacity style={[styles.button, { backgroundColor: colors[color] }]} onPress={onPress} > <Text style={styles.text}>Register</Text> </TouchableOpacity> ); } const styles = StyleSheet.create({ button: { backgroundColor: red; borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, text: { color: #fff } }) export default AppButton;
يحتوي مكون Button
هذا على نص ووظيفة onPress
. دعنا نختبر هذا المكون لمعرفة ماهية اختبار الوحدة.
أولاً ، لنقم بإنشاء ملف اختبار باسم Button.test.js
:
it('renders correctly across screens', () => { const tree = renderer.create(<Button />).toJSON(); expect(tree).toMatchSnapshot(); });
هنا ، نقوم باختبار لمعرفة ما إذا كان مكون Button
الخاص بنا يتم عرضه كما ينبغي على جميع شاشات التطبيق. هذا هو ما يدور حوله اختبار الوحدة: اختبار مكونات التطبيق للتأكد من أنها تعمل كما ينبغي.
اختبار الوحدة في تطبيقات React الأصلية
يمكن اختبار تطبيق React Native باستخدام مجموعة متنوعة من الأدوات ، ومنها ما يلي:
- ويب درايفر
تُستخدم أداة الاختبار مفتوحة المصدر هذه لتطبيقات Node.js أيضًا لاختبار تطبيقات React Native. - كابوس
هذا بأتمتة عمليات الاختبار في المتصفح. وفقًا للوثائق ، "الهدف هو الكشف عن بعض الأساليب البسيطة التي تحاكي إجراءاتtype
(مثلgoto
،click
) ، مع واجهة برمجة تطبيقات تبدو متزامنة مع كل كتلة من البرمجة النصية ، بدلاً من عمليات الاسترجاعات المتداخلة بشدة." - دعابة
هذه واحدة من أكثر مكتبات الاختبار شيوعًا والتي سنركز عليها اليوم. مثل React ، يتم صيانته بواسطة Facebook وتم إنشاؤه لتوفير إعداد "صفر التكوين" للحصول على أقصى أداء. - موكا
Mocha هي مكتبة شهيرة لاختبار تطبيقات React و React Native. لقد أصبح أداة اختبار مفضلة للمطورين نظرًا لمدى سهولة إعداده واستخدامه ومدى سرعته. - ياسمين
وفقًا لوثائقها ، فإن Jasmine هو إطار تطوير يحركه السلوك لاختبار كود JavaScript.
مقدمة في النكتة والإنزيم
وفقًا لوثائقه ، "Jest هو إطار عمل اختبار JavaScript ممتع مع التركيز على البساطة". إنه يعمل بدون تكوين. عند التثبيت (باستخدام مدير الحزم مثل npm أو Yarn) ، يكون Jest جاهزًا للاستخدام ، دون الحاجة إلى عمليات تثبيت أخرى.
Enzyme هو إطار اختبار JavaScript لتطبيقات React Native. (إذا كنت تعمل باستخدام React بدلاً من React Native ، يتوفر دليل.) سنستخدم Enzyme لاختبار وحدات إخراج تطبيقنا. باستخدامه ، يمكننا محاكاة وقت تشغيل التطبيق.
لنبدأ بإعداد مشروعنا. سنستخدم تطبيق Done With It على GitHub. إنه سوق تطبيق React Native. ابدأ باستنساخه ، وانتقل إلى المجلد ، وقم بتثبيت الحزم عن طريق تشغيل ما يلي لـ npm ...
npm install
... أو هذا للغزل:
yarn install
سيقوم هذا الأمر بتثبيت جميع الحزم في تطبيقنا. بمجرد الانتهاء من ذلك ، سنختبر تناسق واجهة مستخدم تطبيقنا باستخدام اللقطات الموضحة أدناه.
اللقطات والتكوين الدعابة
في هذا القسم ، سنختبر لمسات المستخدم وواجهة المستخدم لمكونات التطبيق عن طريق اختبار اللقطات باستخدام Jest.
قبل القيام بذلك ، نحتاج إلى تثبيت Jest وتوابعها. لتثبيت Jest لـ Expo React Native ، قم بتشغيل الأمر التالي:
yarn add jest-expo --dev
يؤدي هذا إلى تثبيت jest-expo
في دليل تطبيقنا. بعد ذلك ، نحتاج إلى تحديث ملف package.json
لدينا للحصول على برنامج نصي للاختبار:
"scripts": { "test" "jest" }, "jest": { "preset": "jest-expo" }
بإضافة هذا الأمر ، نخبر Jest عن الحزمة التي يجب تسجيلها في تطبيقنا وأين.
التالي هو إضافة حزم أخرى إلى تطبيقنا والتي ستساعد Jest في إجراء اختبار شامل. بالنسبة إلى npm ، قم بتشغيل هذا ...
npm i react-test-renderer --save-dev
... وللغزل ، هذا:
yarn add react-test-renderer --dev
لا يزال لدينا القليل من التكوين للقيام به في ملف package.json
الخاص بنا. وفقًا لوثائق Expo React Native ، نحتاج إلى إضافة تكوين transformIgnorePattern
الذي يمنع الاختبارات من التشغيل في Jest عندما يتطابق ملف المصدر مع الاختبار (على سبيل المثال ، إذا تم إجراء اختبار وتم العثور على ملف مشابه في node modules
الخاصة بالمشروع).
"jest": { "preset": "jest-expo", "transformIgnorePatterns": [ "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)" ] }
الآن ، دعنا ننشئ ملفًا جديدًا ، باسم App.test.js
، لكتابة اختبارنا الأول. سنختبر ما إذا كان App
يحتوي على عنصر فرعي واحد في شجرته:
import React from "react"; import renderer from "react-test-renderer"; import App from "./App.js" describe("<App />", () => { it('has 1 child', () => { const tree = renderer.create(<App />).toJSON(); expect(tree.children.length).toBe(1); }); });
الآن ، قم بتشغيل yarn test
أو ما يعادله من npm. إذا كان App.js
يحتوي على عنصر فرعي واحد ، فيجب أن يجتاز اختبارنا ، والذي سيتم تأكيده في واجهة سطر الأوامر.
في الكود أعلاه ، قمنا باستيراد React
و reaction react-test-renderer
، مما يجعل اختباراتنا لـ Expo
. لقد قمنا بتحويل شجرة المكون <App />
إلى JSON ، ثم طلبنا من Jest معرفة ما إذا كان العدد الذي تم إرجاعه من المكونات الفرعية في JSON يساوي ما نتوقعه.
المزيد من اختبارات اللقطة
كما يقول David Adeneye:
"يتأكد اختبار اللقطة من أن واجهة المستخدم (UI) لتطبيق الويب لا تتغير بشكل غير متوقع. إنه يلتقط رمز المكون في وقت ما ، حتى نتمكن من مقارنة المكون في حالة واحدة مع أي حالة أخرى محتملة قد يستغرقها. "
يتم ذلك بشكل خاص عندما يتضمن المشروع أنماطًا عامة يتم استخدامها عبر الكثير من المكونات. لنكتب اختبار لقطة لـ App.js
لاختبار تناسق واجهة المستخدم الخاصة به:
it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); });
it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); });
أضف هذا إلى الاختبارات التي كتبناها بالفعل ، ثم قم بتشغيل yarn test
(أو ما يعادله من npm). إذا نجح اختبارنا ، يجب أن نرى هذا:
PASS src/App.test.js √ has 1 child (16ms) √ renders correctly (16ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 1 total Time: 24s
يخبرنا هذا أن اختباراتنا مرت والوقت الذي استغرقته. ستبدو نتيجتك متشابهة إذا نجحت الاختبارات.
دعنا ننتقل إلى الاستهزاء ببعض الوظائف في Jest.
Mocking API Calls
وفقًا لوثائق Jest:
تسمح لك وظائف Mock باختبار الروابط بين الكود عن طريق محو التنفيذ الفعلي لوظيفة ما ، والتقاط استدعاءات الوظيفة (والمعلمات التي تم تمريرها في تلك الاستدعاءات) ، والتقاط حالات من وظائف المُنشئ عند إنشاء مثيل لها بـ "new" ، والسماح بإجراء اختبار- تكوين الوقت لقيم العودة.
ببساطة ، المحاكاة هي نسخة من كائن أو وظيفة بدون الإجراءات الحقيقية لتلك الوظيفة. إنه يقلد تلك الوظيفة.
تساعدنا Mocks في اختبار التطبيقات بعدة طرق ، لكن الفائدة الرئيسية هي أنها تقلل من حاجتنا إلى التبعيات.
يمكن عادة أداء السحر بإحدى طريقتين. الأول هو إنشاء وظيفة وهمية يتم حقنها في الكود ليتم اختبارها. والآخر هو كتابة دالة وهمية تتجاوز الحزمة أو التبعية المرفقة بالمكون.
تفضل معظم المؤسسات والمطورين كتابة نماذج يدوية تقلد الوظائف وتستخدم بيانات مزيفة لاختبار بعض المكونات.
يشمل React Native fetch
في الكائن العام. لتجنب إجراء مكالمات API حقيقية في اختبار الوحدة الخاصة بنا ، فإننا نسخر منهم. فيما يلي طريقة للاستهزاء بجميع استدعاءات واجهة برمجة التطبيقات الخاصة بنا ، إن لم يكن معظمها ، في React Native ، ودون الحاجة إلى التبعيات:
global.fetch = jest.fn(); // mocking an API success response once fetch.mockResponseIsSuccess = (body) => { fetch.mockImplementationForOnce ( () => Promise.resolve({json: () => Promise.resolve(JSON.parse(body))}) ); }; // mocking an API failure response for once fetch.mockResponseIsFailure = (error) => { fetch.mockImplementationForOnce( () => Promise.reject(error) ); };
هنا ، قمنا بكتابة دالة تحاول جلب API مرة واحدة. بعد القيام بذلك ، تُعيد الوعد ، وعندما يتم حلها ، تُعيد الجسم في JSON. إنه مشابه للاستجابة الوهمية لمعاملة جلب فاشلة - تُرجع خطأً.
يوجد أدناه مكون product
الخاص بتطبيقنا ، والذي يحتوي على كائن product
ويعيد المعلومات props
.
import React from 'react'; const Product = () => { const product = { name: 'Pizza', quantity: 5, price: '$50' } return ( <> <h1>Name: {product.name}</h1> <h1>Quantity: {product.quantity}</h1> <h1>Price: {product.price}</h1> </> ); } export default Product;
لنتخيل أننا نحاول اختبار جميع مكونات منتجنا. الوصول المباشر إلى قاعدة البيانات الخاصة بنا ليس حلاً عمليًا. هذا هو المكان الذي تلعب فيه السخرية. في الكود أدناه ، نحاول محاكاة أحد مكونات المنتج باستخدام Jest لوصف الكائنات الموجودة في المكون.
describe("", () => { it("accepts products props", () => { const wrapper = mount(<Customer product={product} />); expect(wrapper.props().product).toEqual(product); }); it("contains products quantity", () => { expect(value).toBe(3); }); });
نحن نستخدم describe
من Jest لإملاء الاختبارات التي نريد إجراؤها. في الاختبار الأول ، نتحقق لنرى ما إذا كان الكائن الذي نمرره يساوي الدعائم التي سخرنا منها.
في الاختبار الثاني ، نجتاز دعائم customer
للتأكد من أنه منتج وأنه يتوافق مع نماذجنا. عند القيام بذلك ، لا يتعين علينا اختبار جميع مكونات منتجاتنا ، كما يمكننا أيضًا منع الأخطاء في التعليمات البرمجية الخاصة بنا.
الاستهزاء بطلبات API الخارجية
حتى الآن ، كنا نجري اختبارات لاستدعاءات واجهة برمجة التطبيقات مع عناصر أخرى في تطبيقنا. الآن دعنا نسخر من استدعاء خارجي لواجهة برمجة التطبيقات. سنستخدم أكسيوس. لاختبار مكالمة خارجية إلى واجهة برمجة التطبيقات ، يتعين علينا أن نسخر من طلباتنا وكذلك إدارة الردود التي نحصل عليها. سنستخدم axios-mock-adapter
أكسيوس للسخرية من أكسيوس. أولاً ، نحتاج إلى تثبيت axios-mock-adapter
عن طريق تشغيل الأمر أدناه:
yarn add axios-mock-adapter
الشيء التالي الذي يجب فعله هو إنشاء نماذجنا:
import MockAdapter from 'axios-mock-adapter'; import Faker from 'faker' import ApiClient from '../constants/api-client'; import userDetails from 'jest/mockResponseObjects/user-objects'; let mockApi = new MockAdapter(ApiClient.getAxiosInstance()); let validAuthentication = { name: Faker.internet.email(), password: Faker.internet.password() mockApi.onPost('requests').reply(config) => { if (config.data === validAuthentication) { return [200, userDetails]; } return [400, 'Incorrect username and password']; });
هنا ، نحن ApiClient
إليه للاستهزاء ببيانات اعتماد المستخدم. نحن نستخدم حزمة تسمى faker.js لإنشاء بيانات مستخدم مزيفة ، مثل عنوان البريد الإلكتروني وكلمة المرور.
يتصرف المحاكاة كما نتوقع من واجهة برمجة التطبيقات. إذا نجح الطلب ، فسنحصل على رد برمز الحالة 200 لـ OK. وسنحصل على رمز الحالة 400 لطلب سيئ إلى الخادم ، والذي سيتم إرساله مع JSON مع رسالة "اسم المستخدم وكلمة المرور غير صحيحين".
الآن وقد أصبح نموذجنا النموذجي جاهزًا ، فلنكتب اختبارًا لطلب خارجي لواجهة برمجة التطبيقات. كما في السابق ، سنستخدم اللقطات.
it('successful sign in with correct credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(getActions()).toMatchSnapshot(); }); it('unsuccessful sign in with wrong credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'wrong credential')) .catch((error) => { expect(errorObject).toMatchSnapshot(); });
هنا ، نقوم باختبار تسجيل دخول ناجح باستخدام بيانات الاعتماد الصحيحة ، باستخدام JavaScript async await
الاحتفاظ بمدخلاتنا. وفي الوقت نفسه ، تعمل وظيفة authenticateUser
من Jest على مصادقة الطلب والتأكد من مطابقته للقطات السابقة. بعد ذلك ، نقوم باختبار تسجيل دخول غير ناجح في حالة وجود بيانات اعتماد خاطئة ، مثل عنوان البريد الإلكتروني أو كلمة المرور ، ونرسل رسالة خطأ كرد.
الآن ، قم بتشغيل yarn test
أو npm test
. أنا متأكد من أن جميع اختباراتك ستنجح.
دعونا نرى كيفية اختبار المكونات في مكتبة إدارة الدولة ، Redux.
اختبار إجراءات الاختزال والمخفضات باستخدام اللقطات
ليس هناك من ينكر أن Redux هو أحد أكثر مديري الدولة استخدامًا لتطبيقات React. تتضمن معظم الوظائف في Redux dispatch
، وهي إحدى وظائف متجر Redux التي تُستخدم لإحداث تغيير في حالة التطبيق. قد يكون اختبار Redux أمرًا صعبًا لأن actions
Redux تنمو بسرعة من حيث الحجم والتعقيد. مع لقطات Jest ، يصبح هذا أسهل. تنحصر معظم الاختبارات باستخدام Redux في شيئين:
- لاختبار
actions
، نقوم بإنشاءredux-mock-store
ونرسل الإجراءات. - لاختبار المخفضات ، نقوم باستيراد
reducer
حالة وكائن الإجراء إليه.
يوجد أدناه اختبار Redux مع لقطات. سنختبر الإجراءات المرسلة من خلال مصادقة المستخدم عند SIGN-IN
ونرى كيف يتم التعامل مع إجراء LOGOUT
الدخول بواسطة مخفض user
.
import mockStore from 'redux-mock-store'; import { LOGOUT } from '../actions/logout'; import User from '../reducers/user'; import { testUser } from 'jest/mock-objects'; describe('Testing the sign in authentication', () => { const store = mockStore(); it('user attempts with correct password and succeeds', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(store.getActions()).toMatchSnapshot(); }); }); describe('Testing reducers after user LOGS OUT', () => { it('user is returned back to initial app state', () => { expect(user(testUser, { type: LOGOUT })).toMatchSnapshot(); }); });
في الاختبار الأول ، نصف مصادقة تسجيل الدخول وإنشاء متجر وهمي. نقوم بذلك عن طريق استيراد mockStore
من Redux ، ثم استيراد طريقة تسمى testUser
من Jest لمساعدتنا في السخرية من مستخدم. بعد ذلك ، نختبر عندما يقوم المستخدم بتسجيل الدخول بنجاح إلى التطبيق باستخدام عنوان بريد إلكتروني وكلمة مرور تتطابق مع تلك الموجودة في متجر اللقطات لدينا. لذا ، فإن اللقطة تضمن تطابق الكائنات التي يُدخلها المستخدم في كل مرة يتم فيها تشغيل الاختبار.
في الاختبار الثاني ، نقوم باختبار وقت تسجيل خروج المستخدم. بمجرد تأكيد لقطة المخفض الخاصة بنا أن المستخدم قد قام بتسجيل الخروج ، فإنه يعود إلى الحالة الأولية للتطبيق.
بعد ذلك ، نختبر بإجراء yarn test
. في حالة اجتياز الاختبارات يجب أن نرى النتيجة التالية:
PASS src/redux/actions.test.js √ user attempts with correct password and succeeds (23ms) √ user is returned back to initial app state (19ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 2 total Time: 31s
خاتمة
باستخدام Jest ، لم يكن اختبار تطبيقات React Native أسهل من أي وقت مضى ، خاصة مع اللقطات ، التي تضمن بقاء واجهة المستخدم متسقة بغض النظر عن الأنماط العامة. أيضًا ، يسمح لنا Jest بالسخرية من بعض استدعاءات ووحدات واجهة برمجة التطبيقات في تطبيقنا. يمكننا أن نأخذ هذا إلى أبعد من ذلك عن طريق اختبار مكونات تطبيق React Native.
مزيد من الموارد
- "دليل عملي لاختبار تفاعل التطبيقات الأصلية مع الدعابة" ، ديفيد أديني ، مجلة Smashing
- وثائق الدعابة
- "الاختبار مع الدعابة" ، وثائق Expo React الأصلية
- "تعلم اختبار التفاعل الأصلي مع الدعابة" ، جايسون جاري