الگوهای تست برای کامپوننت های ری اکت – راهنمای کاربردی
هر کدی نیاز به تست دارد و کامپوننتهای React نیز از این قاعده مستثنا نیستند. در این مقاله با روش طراحی کامپوننتهای React آشنا میشویم که قابلیت تستپذیری آنها را ارتقا میدهد. با ما تا انتهای این راهنما همراه باشید تا الگوهای تست برای کامپوننت های ری اکت را بشناسید.
کامپوننت زیر به نام HelloName وقتی مقدار name در props برابر با James باشد، عبارت Hello James را رندر میکند. از آنجا که این مقدار در مالکیت HelloName نیست، با استفاده از یک دکوراتور «کامپوننت مرتبه بالا» (High Order Component) به نام WithName به آن ارسال میشود. این رویه در مواردی که کتابخانهها از API مربوط به context ریاکت استفاده نمیکند معمولاً رایج است.
دکوراتور name نیز به صورت زیر است:
رابطه فوق بین HelloName و WithName بسیار رایج است، زیرا از آن برای دستیابی به چیزهای زیادی در React بهره میگیریم. اگر از «ریداکس» (Redux) استفاده کرده باشید، این رویه برای شما کاملاً آشنا است، زیرا در آنجا از این نوع رابطه برای اتصال کامپوننتها به حالت سراسری استفاده میشود.
برای تست HelloName به روش فوق، تست باید چیزی مانند زیر باشد تا صرفاً آن را رندر کند:
اینک مشکل این است که هدف از تست این است که کامپوننت را در معرض حالتها و props ممکن تصادفی قرار دهد و کارکرد آن را بررسی کند. فرض کنید مالک کامپوننت WithName نیستید که پدیده رایجی است. این بدان معنی است که دسترسی آسانی به متغیر name برای دستکاری آن و تست سناریوهای مختلف ندارید. این وضعیت در مواردی که مقدار مورد نظر در HelloName برای انجام کاری استفاده شود که کامپوننت به جای رندر شدن صرف، نتیجهای نیز تولید کند، باز هم پیچیدهتر میشود. برای نمونه باید تست کنیم که آیا Hello James و در مورد دیگر Hello Mike رندر میشود یا نه.
راه حل این مشکل کاملاً ساده است. نخستین کاری که باید انجام دهیم این است که کامپوننت HelloName را قبل از اعمال دکوراتور اکسپورت کنیم و نسخه با اعمال دکوراتور را به صورت پیشفرض درآوریم.
در این تست نسخه با اعمال دکوراتور تست میشود و سپس نسخه بدون دکوراتور را میتوان به هر تعداد نیاز تست کرد.
این الگو در مواردی که قرار باشد یک اپلیکیشن را تست کنیم و در آن اپلیکیشن استفاده گستردهای از Redux و Redux Form شده باشد، سهولت زیادی ایجاد میکند.
تست کردن اکشنهای کامپوننت
الگوی دیگری که به منظور سهولت تست میتوان استفاده کرد، در زمان تست کردن اکشنهایی است که درون یک کامپوننت رخ میدهند.
کامپوننت Form را در نظر بگیرید که یک فرم HTML را با یک ورودی برای مقدار name رندر میکند. این متغیر name به متغیر حالت name اتصال یافته است و این فرم یک دکمه دارد که رویداد onClick آن به یک تابع اتصال یافته و آن را اجرا میکند.
اکنون باید این کد را در رندر تست کنیم. زمانی که متن در ورودی تایپ میشود و سپس روی دکمه کلیک میکنیم، تابع handleSubmit یک بار فراخوانی میشود.
مشکل این جا است که handleSubmit درون کامپوننت فرم تعریف شده است و از این رو دسترسی به آن ممکن نیست. برای این که آن را تستپذیر بکنیم، باید تابع handleSubmit را از کامپوننت به props انتقال بدهیم.
اینک برای تست کامپوننت باید handleSubmit را که اینک در props است با یک مشابه ساختگی جایگزین کنیم:
این روش برای انتقال اکشنهای بیرونی از کامپوننت به props موجب میشود که بتوانیم کامپوننت را برای تست بهتر اکشنهایی که کاربر در زمان تعامل با کامپوننت ایجاد میکند دستکاری کنیم.
کامپوننتهای دارای نسخههای پیچیده از حالت
فرض کنید کامپوننت زیر را دارید. کامپوننت Profile به متغیر user_id در localStorage وابسته است و از این رو حالتهای زیر را میگیرد:
- زمانی که هیچ user_id در localStorage نباشد، صفحه لاگین را نمایش میدهد.
- تنها زمانی نمایش مییابد که کاربر لاگین کرده باشید.
- در صورتی که user_id در localStorage برابر با user_id در Profile نباشد، \دکمه follow را نشان میدهد.
- اگر user_id در localStorage برابر با user_id در Profile باشد، دکمه edit را نمایش میدهد.
توجه کنید که این صرفاً یک مثال است. شما نباید از localStorage برای نمایش لاگین کردن یا نکردن کاربر استفاده کنید. این بهترین روش برای انجام این کار نیست، گرچه کار میکند و کاملاً ساده است.
اکنون برای تست کامپوننت فوق، باید مطمئن شویم که دادههای مورد استفاده برای رندر کامپوننت کاملاً از آن مجزا هستند به طوری که دستکاری کامپوننت آسان میشود.
نخستین کاری که باید بکنیم این است که کامپوننت مالک دادهها باشد، یعنی کامپوننت دارای کمترین عوارض جانبی باشد و تنها دادهها را عرضه کند. در صورت امکان باید به صورت کامل از رندر نیز اجتناب کند. در ریداکس این وضعیت از طریق یک Reducer و یک Provider با نوعی HOC یا هر نامی که MobX یا Flux به آن میدهند ممکن است.
در این راه حل نمونه از React Context برای ارائه دادهها به کامپوننت استفاده کردهایم. به این ترتیب یک ProfileDataContext/Provider داریم:
اینک کامپوننت ما به صورت زیر در آمده است:
اکنون در تستها میتوانیم از کامپوننت خام استفاده کنیم و Provider خود را با دادههایمان در اختیار آن قرار دهیم. سپس میتوانیم هر تعداد سناریو را که میخواهیم تست کنیم و مهم نیست که دادههای پروفایل چه قدر پیچیده هستند و همچنین پیچیدگی رندر و حتی فرزندان در کامپوننت Profile اهمیتی ندارد. در ادامه چهار حالت مختلف را تست کردهایم که نماینده نسخههای مختلفی از کامپوننتها هستند:
سخن پایانی
در این مقاله با روشهای طراحی الگوهای تستپذیر برای کامپوننتها آشنا شدیم. این روشها موجب میشوند که تست کردن کامپوننتها بسیار آسان شود. نکات کلیدی به شرح زیر هستند:
- تا حدی که میتوانید کامپوننتهای خالص (Pure) طراحی کنید.
- هر چه کامپوننتهای عوارض جانبی کمتری داشته باشند، آنها را آسانتر میتوان شبیهسازی کرد.
- اکشنهای کامپوننتها را به صورت والدین خالص جدا کنید تا بتوانید به سهولت آنها را شبیهسازی کنید.
- در حد امکان دادهها را جداسازی کنید.
امیدواریم این راهنما برای شما مفید بوده باشد. کد کامل موارد مطرح شده در این راهنما را در این ریپوی گیتهاب (+) ببینید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش JavaScript ES6 (جاوا اسکریپت)
- هشت ترفند مفید برای توسعه اپلیکیشن های React — راهنمای کاربردی
- ارسال پارامترهای چندگانه مسیر در React — به زبان ساده
==