ما يعجبني في النظام البيئي React هو أن IDEA وراء العديد من القرارات. يكتب العديد من المؤلفين مقالات متنوعة لدعم الترتيب الحالي ويشرحون لماذا كل شيء على ما يرام ، لذلك يفهم الجميع أن الحزب على المسار الصحيح.
بعد بعض الوقت ، يتغير IDEA قليلاً ، ويبدأ كل شيء من البداية.
بداية هذه القصة هي فصل المكونات إلى حاويات وغير حاويات (تسمى شعبياً Dumb Components ، آسف على لغتي الفرنسية).

المشكلة
المشكلة بسيطة جدا - وحدة الاختبارات. في الآونة الأخيرة ، كان هناك بعض التحرك نحو اختبارات التكامل - حسناً ، أنت تعرف "اختبارات الكتابة. ليست كثيرة. معظمها تكامل." . هذه ليست فكرة سيئة ، وإذا كان الوقت قصيرًا (والاختبارات ليست ضرورية بشكل خاص) - هذا ما يتعين عليك القيام به. فقط دعنا نسميها اختبارات الدخان - للتحقق من أن لا شيء يبدو أنه ينفجر.
إذا كان هناك الكثير من الوقت ، وكانت هناك حاجة للاختبارات ، فمن الأفضل عدم السير بهذه الطريقة ، لأن كتابة اختبارات التكامل الجيدة طويلة جدًا. فقط لأنها ستنمو وتنمو ، ومن أجل اختبار الزر الثالث على اليمين ، ستحتاج أولاً إلى النقر فوق 3 أزرار في القائمة ، ولا تنسَ تسجيل الدخول. بشكل عام - إليك انفجار اندماجي على طبق فضي.
الحل هنا واحد وبسيط (بحكم التعريف) - اختبارات وحدة. القدرة على بدء الاختبارات مع بعض الحالة الجاهزة لبعض جزء من التطبيق. بتعبير أدق ، لتقليل (تضييق) منطقة الاختبار من التطبيق أو الكتلة الكبيرة إلى شيء صغير - وحدة ، أيا كانت. ليس من الضروري استخدام الإنزيم - يمكنك إجراء اختبارات المتصفح ، إذا سألت الروح. الشيء الأكثر أهمية هنا هو أن تكون قادرًا على اختبار شيء بمعزل . ودون الكثير من المتاعب.
العزلة هي واحدة من النقاط الرئيسية في اختبار الوحدة ، وهذا هو السبب في أن اختبارات الوحدة لا تحبها. أنهم لا يحبون ذلك لأسباب مختلفة:
- على سبيل المثال ، تمزيق "الوحدة" الخاصة بك عن التطبيق ، ولا تعمل في تكوينها حتى عندما تكون الاختبارات الخاصة بها باللون الأخضر.
- أو على سبيل المثال لأن العزلة هي حصان كروي في فراغ لم يره أحد. كيفية تحقيق ذلك ، وكيفية قياسه؟
شخصيا ، لا أرى أي مشاكل هنا. في الفقرة الأولى ، بالطبع ، يمكنك التوصية باختبارات التكامل ، لقد تم اختراعها لذلك - للتحقق من كيفية تجميع المكونات التي تم اختبارها مسبقًا بشكل صحيح. تثق في حزم npm التي تختبر ، بالطبع ، نفسها فقط ، وليس نفسها كجزء من طلبك. كيف "المكونات" الخاصة بك تختلف عن "ليس لديك" الحزم؟
مع الفقرة الثانية ، كل شيء أكثر تعقيدًا بقليل. وستتناول هذه المقالة بالضبط هذه النقطة (وكل شيء قبل ذلك كان - مقدمة) - حول كيفية جعل وحدة " وحدة " قابلة للاختبار .
فرق تسد
إن فكرة فصل مكونات React إلى "Container" و "Presentation" ليست جديدة ، موصوفة جيدًا ، وقد نجحت بالفعل في أن تصبح قديمة بعض الشيء. إذا أخذنا أساسًا (ما يفعله 99٪ من المطورين) في مقال بقلم دان أبراموف ، فعندئذٍ مكون العرض التقديمي:
- نشعر بالقلق مع كيف تبدو الأمور
- قد تحتوي على كل من مكونات العرض التقديمي ومكونات الحاوية
**
الداخل ، وعادة ما تحتوي على بعض علامات DOM وأنماط خاصة بها) - فتحات الدعم (غالبًا ما تسمح بالاحتواء عبر this.props.children)
- التطبيق مستقل (ليس له أي تبعيات على بقية التطبيق ، مثل إجراءات Flux أو المتاجر)
- لا تعتمد على البيانات (لا تحدد كيفية تحميل البيانات أو تحورها)
- تعتمد الواجهة على الدعائم (تلقي البيانات وعمليات الاسترجاعات حصريًا عبر الدعائم)
- غالبًا عديمي الجنسية (نادرًا ما يكون لديهم حالة خاصة بهم (عندما يفعلون ، تكون حالة واجهة المستخدم بدلاً من البيانات))
- غالبًا SFC (تتم كتابته كمكونات وظيفية ما لم تكن بحاجة إلى حالة أو خطافات دورة حياة أو تحسينات في الأداء)
حسنا ، الحاويات هي كل المنطق ، كل الوصول إلى البيانات ، والتطبيق كله من حيث المبدأ.
في عالم مثالي ، الحاويات هي الجذع ، ومكونات العرض هي الأوراق.
هناك نقطتان أساسيتان في تعريف Dan: "التطبيق المستقل" ، وهو عبارة عن تعريف أكاديمي تقريبًا لـ "unit" ، و * "قد يحتوي على مكونات العرض والحاويات الأخرى **
" * ، حيث تكون هذه النجوم مثيرة للاهتمام بشكل خاص.
(ترجمة مجانية) ** في الإصدارات الأولى من مقالي ، قلت (دان) إنه يجب أن تحتوي المكونات التقديمية على مكونات عرضية أخرى فقط. لا أعتقد ذلك بعد الآن. نوع المكون هو التفاصيل وقد يتغير بمرور الوقت. بشكل عام ، لا تشاركه وسوف يكون كل شيء على ما يرام.
لنتذكر ما يحدث بعد هذا:
- في القصص القصيرة ، يسقط كل شيء ، لأن هناك نوعًا من الحاوية ، في الزر الثالث على اليسار ، يزحف إلى الجانب الذي لا يوجد به شيء. تحية خاصة إلى graphql ، رد فعل الموجه وغيرها من رد الفعل int.
- يتم فقد القدرة على استخدام التحميل في الاختبارات ، لأنه يؤدي إلى نقل كل شيء من A إلى Z ، ومرة أخرى ، في مكان ما في أعماق شجرة التقديم ، يقوم شخص ما بعمل ما ، وتنخفض الاختبارات.
- تُفقد القدرة على التحكم في حالة التطبيق ، نظرًا لأنه (بالمعنى المجازي) تُفقد القدرة على تحديد المُحلِّلات / المُحلِّلات (خاصة مع الوكيل) ، ويجب أن تكون البوابة بأكملها رطبة. وهذا رائع للاختبارات وحدة.
إذا كنت تعتقد أن المشكلات بعيدة المنال قليلاً ، فحاول العمل في فريق عندما تكون هذه الحاويات ، التي سيتم استخدامها في الحاويات غير الخاصة بك ، وتغييرها في الأقسام الأخرى ، ونتيجة لذلك ، أنت وأنت تنظر إلى الاختبارات ولا يمكنك فهم سبب كل شيء بالأمس نجحت ، والآن مرة أخرى.
نتيجةً لذلك ، يجب عليك استخدام الضحلة ، والتي تقضي حسب التصميم على جميع الآثار الجانبية الضارة (وغير المتوقعة). هنا مثال بسيط من مقال "لماذا أنا دائما استخدام الضحلة"
تخيل أن "تلميح الأدوات" يعرض "؟" ، عند النقر فوقه ، سيتم عرض النوع نفسه.
import Tooltip from 'react-cool-tooltip'; const MyComponent = () => { <Tooltip> hint: {veryImportantTextYouHaveToTest} </Tooltip> }
كيفية اختباره؟ جبل + انقر + تحقق ما هو مرئي. هذا اختبار تكامل ، وليس وحدة ، والسؤال هو كيفية النقر فوق مكون "أجنبي" لك. لا توجد مشكلة مع الضحلة ، حيث لا توجد أدمغة و "العنصر الغريب" نفسه. ولكن هناك أدمغة هنا ، لأن Tooltip عبارة عن حاوية ، في حين أن MyComponent عبارة عن عرض تقديمي عمليًا .
jest.mock('react-cool-tooltip', {default: ({children}) => childlren});
ولكن إذا كان رد فعلك رائعًا ، فلن تكون هناك مشكلات في الاختبار. لقد أصبح "المكون" بشكل حاد أغبى بكثير ، أقصر بكثير ، وأكثر محدودية .
المكون النهائي
- مكون ذو حجم معروف ، والذي قد يتضمن مكونات أخرى معروفة سابقًا أو لا تحتوي على الإطلاق.
- لا يحتوي على حاويات أخرى ، لأنها تحتوي على حالة غير متحكم فيها وحجم "الزيادة" ، أي جعل المكون الحالي لانهائي .
- وإلا فإنه مكون عرض تقديمي منتظم. في الواقع ، بالضبط كما هو موضح في الإصدار الأول من مقالة دان.
المكون الأخير هو مجرد معدات مأخوذة من آلية كبيرة.
السؤال كله هو كيفية إخراجها.
الحل 1 - DI
بلدي المفضل هو حقن التبعية. دان يحبه أيضًا . بشكل عام ، هذا ليس DI ، ولكن "فتحات". باختصار - لا حاجة لاستخدام الحاويات داخل العرض التقديمي - يجب أن يتم حقنها هناك. وفي الاختبارات سيكون من الممكن حقن شيء آخر.
هذا هو الحال بالضبط عندما "الحاويات هي الجذع ، ومكونات العرض التقديمي يترك"
الحل 2 - الحدود
يمكن أن يكون DI في كثير من الأحيان بارد. ربما الآن٪ username٪ يفكر في كيفية تطبيقه على قاعدة الشفرة الحالية ، والحل لا يتم اختراعه ...
في مثل هذه الحالات ، سوف توفر لك الحدود .
const Boundary = ({children}) => ( process.env.NODE_ENV === 'test' ? null : children
هنا ، بدلاً من "فتحات" ، تتحول كل "نقاط الانتقال" ببساطة إلى حدود ، والتي ستجعل أي شيء أثناء الاختبارات. يكفي بشكل تعريفي ، وبالضبط ما تحتاج إلى "إخراج العتاد".
الحل 3 - المستوى
يمكن أن تكون الحدود صعبة بعض الشيء ، وقد يكون من الأسهل جعلها أكثر ذكاءً عن طريق إضافة القليل من المعرفة حول Layer.
const checkTier = tier => tier === currentTier; const withTier = tier => WrapperComponent => (props) => ( (process.env.NODE_ENV !== 'test' || checkTier(tier)) && <WrapperComponent{...props} /> ); const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> ); const ASideContainer = withTier('UI')(...) const Page = withTier('Page')(...) const PageChromeContainer = withTier('UI')(PageChrome);
تحت اسم الطبقة / الطبقة ، يمكن أن يكون هناك أشياء مختلفة - ميزة ، بطة ، وحدة ، أو تلك الطبقة / الطبقة فقط. النقطة ليست مهمة ، والشيء الرئيسي هو أنه يمكنك سحب الترس ، ربما لا الترس ، لكن الرقم النهائي ، رسم خط ما بين ما تحتاج إليه وما لا تحتاج إليه (بالنسبة للاختبارات المختلفة ، فهذه حدود مختلفة).
ولا شيء يمنع وضع هذه الحدود بطريقة مختلفة بطريقة ما.
الحل 4 - مخاوف منفصلة
إذا كان الحل (بحكم التعريف) يكمن في الفصل بين الكيانات - ماذا سيحدث إذا أخذناها وفصلنا عنها؟
عادة ما تسمى "الحاويات" ، التي لا نحبها كثيرًا ، بالحاويات . وإذا لم يكن الأمر كذلك - فلا شيء يمنع الآن البدء في تسمية المكونات بطريقة أو بأخرى بطريقة أكثر سخونة. أو لديهم نمط معين في اسمه - الاتصال (WrappedComonent) ، أو GraphQL / Query.
ماذا لو كان صحيحا في وقت التشغيل رسم خط بين الكيانات على أساس اسم؟
const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> );
بالإضافة إلى سطر واحد في الاختبارات ، وسيؤدي رد الفعل إلى إزالة كل الحاويات التي قد تتداخل مع الاختبارات.
من حيث المبدأ ، يمكن استخدام هذا النهج لاختبار الحاويات نفسها - تحتاج فقط إلى إزالة كل شيء باستثناء الحاوية الأولى.
import {createElement, remock} from 'react-remock';
مرة أخرى - بضع خطوط والعتاد إزالتها.
المجموع
خلال العام الماضي ، أصبح اختبار مكونات React أكثر تعقيدًا ، خاصة بالنسبة للتركيب - تحتاج إلى الكتابة فوق جميع موفري الخدمات العشرة ، والسياقات ، ويصعب اختبار المكون الصحيح بالطريقة الصحيحة - وهناك الكثير من الحبال التي لا يمكن سحبها.
شخص يبصق ويذهب إلى العالم الضحل. ولوح شخص بأيديهم في اختبارات الوحدة ونقل كل شيء إلى Cypress (المشي مثل المشي!).
شخص آخر يثقب إصبعك في رد الفعل ، يقول إن هذه تأثيرات جبرية ويمكنك أن تفعل ما تريد. جميع الأمثلة المذكورة أعلاه هي أساسا استخدام هذه الآثار الجبرية والسخرية. بالنسبة لي و DI هذه هي moki.
ملاحظة: تمت كتابة هذا المنشور كرد على تعليقات في React / RFC حول حقيقة أن فريق React كسر كل شيء ، وجميع البوليمرات هناك أيضًا
PPS: هذا المنشور هو في الواقع ترجمة مجانية لآخر
PPPS: بشكل عام ، من أجل العزلة الحقيقية ، انظر إلى rewiremock