راهنمای قطعی تست واحد در برنامه های React با Jest و React-Testing

معرفی

به عنوان یک توسعه دهنده، یکی از چیزهایی که در بالای لیست شما قرار دارد باید ارسال کد بدون اشکال باشد. هیچ چیز بدتر از این نیست که پنجشنبه شب متوجه شوید که تغییراتی که در روز دوشنبه ایجاد کردید، برنامه زنده را شکسته است. تنها راه برای اطمینان از اینکه برنامه شما مطابق با نیازهای سیستم و کاربر کار می کند این است امتحانش کن!

تست جزء حیاتی هر چرخه عمر توسعه نرم افزار است و تضمین می کند که یک نرم افزار به درستی و طبق برنامه عمل می کند. توسعه وب، توسعه اپلیکیشن موبایل، و به طور قابل توجهی در زمینه ما، برنامه های React همگی از اصول یکسانی پیروی می کنند.

اجزای React را می توان به چند روش مختلف آزمایش کرد که به طور کلی به دو گروه تقسیم می شوند:

  • ارائه درختان جزء در یک محیط آزمایشی ساده و اظهار نظر در مورد عملکرد آنها
  • محل دویدن و پیاده روی "آزمون های انتها به انتها"، که شامل آزمایش کل یک برنامه در یک محیط مرورگر واقعی است

در حالی که آزمایش برنامه‌های React ممکن است به روش‌های مختلفی انجام شود، در این راهنما، یک برنامه React ایجاد می‌کنیم و راهنمای کاملی درباره نحوه انجام تست‌های واحد روی برنامه React با استفاده از آن را پوشش می‌دهیم. جادوگری و کتابخانه آزمایش واکنش به طوری که می توانید مهارت های تست خود را تقویت کنید و یاد بگیرید که چگونه یک برنامه رام React ایجاد کنید.

توجه داشته باشید: با استفاده از این می‌توانید به مخزن این راهنما دسترسی داشته باشید و با همه چیزهایی که در آن است بازی کنید لینک در GitHub.

تست چیست؟

اول از همه، اجازه دهید همه چیز را در یک چشم انداز قرار دهیم. تست یک اصطلاح بسیار گسترده است و می تواند به تست دستی، تست واحد، تست رگرسیون، تست یکپارچه سازی، تست بار و غیره اشاره داشته باشد.

در زمینه تست واحد که امروز روی آن تمرکز خواهیم کرد - ما آن را آزمایش می کنیم عملکرد واحدهای متمایز، معمولاً در سطح روش. این می تواند مقادیر عددی خروجی ها، طول مقادیر خروجی، شکل آنها، نحوه واکنش روش به ورودی نامعتبر و غیره را آزمایش کند.

از آنجایی که اکثر روش‌های خوب نرم‌افزاری از روش‌ها/عملکردهای کوتاه و عملی حمایت می‌کنند که با هدف مشخصی مستقل هستند، بسیاری از روش‌ها را فراخوانی می‌کنند. روشهای دیگر. به طور معمول، شما باید هم روش‌های داخلی و هم روش‌های خارجی را آزمایش کنید تا مطمئن شوید که تغییراتی که در حین اصلاح، رفع اشکال یا بهبود یک ویژگی ایجاد می‌کنید، هیچ عملکرد دیگری را مختل نمی‌کند.

In توسعه آزمون محور (TDD)، توصیه می شود قبل از نوشتن منطق یک روش، یک تست و مقدار مورد انتظار بنویسید. طبیعتاً در ابتدا شکست خواهد خورد. پس از آن، شما فقط آن را به کار می‌گیرید، و وقتی تست را پشت سر گذاشت، شروع به بازسازی آن می‌کنید تا کوتاه‌تر، تمیزتر، سریع‌تر و غیره شود. تا زمانی که خروجی ثابت بماند، می‌دانید که چیزی را خراب نکرده‌اید. در حین بازسازی!

نوشتن تست های واحد خود شما را در ذهنیت یک نفر قرار می دهد با استفاده از روش های شما، به جای کسی نوشته این روش‌ها، که اغلب به نگاهی تازه به یک ویژگی کمک می‌کنند، بررسی‌های اضافی و اعتبارسنجی و شکار باگ‌ها را شامل می‌شوند. گاهی اوقات، منجر به تغییرات طراحی برای ایجاد کد می شود قابل آزمایش تر، مانند عملکرد جداسازی برای فعال کردن آزمایش عددی برای هر جزء جداگانه.

هنگامی که یک خط مبنا ایجاد شد، و کد شما تست ها را پشت سر گذاشت، می توانید تغییراتی ایجاد کنید و تأیید کنید که واحدها (معمولاً روش ها) به صورت جداگانه کار می کنند. آزمایش به ویژه زمانی مفید است که به‌روزرسانی‌هایی در پایگاه کد وجود داشته باشد.

رویکردهای آزمایش

آزمایش را می توان به دو روش مختلف انجام داد: دستی و بطور خودکار. با تعامل مستقیم با یک برنامه، آزمایش دستی عملکرد صحیح آن را تأیید می کند. تست خودکار تمرین نوشتن برنامه ها برای انجام بررسی ها برای شما است.

تست دستی

اکثر توسعه دهندگان به صورت دستی کد خود را بررسی می کنند، زیرا این سریع ترین، طبیعی ترین و ساده ترین راه برای آزمایش سریع یک عملکرد است.

آزمایش دستی مرحله منطقی بعدی است که پس از نوشتن عملکرد انجام می شود، درست مانند چشیدن یک غذا پس از چاشنی کردن آن (اضافه کردن یک ویژگی) تا بررسی شود که آیا آن طور که در نظر گرفته شده کار می کند یا خیر.

فرض کنید که به عنوان یک توسعه دهنده شاغل، در حال ساخت یک فرم ثبت نام هستید. شما به سادگی ویرایشگر متن خود را نمی بندید و به رئیس خود اطلاع نمی دهید که فرم پس از کدنویسی کامل شده است. مرورگر را باز می‌کنید، مراحل فرم ثبت‌نام را طی می‌کنید و مطمئن می‌شوید که همه چیز طبق برنامه پیش می‌رود. به عبارت دیگر، شما به صورت دستی کد را تست خواهید کرد.

تست دستی برای پروژه‌های کوچک ایده‌آل است و اگر یک برنامه فهرست کار دارید که می‌توانید هر دو دقیقه آن را به صورت دستی بررسی کنید، نیازی به تست‌های خودکار ندارید. با این حال، بسته به تست دستی با رشد برنامه شما دشوار می شود - ممکن است از دست دادن تمرکز و فراموش کردن بررسی چیزی بسیار آسان باشد. با یک لیست رو به رشد از اجزای متقابل، تست دستی حتی سخت تر می شود، به خصوص اگر چیزی را آزمایش کرده باشید، و به یک مورد جدید پیشرفت کرده باشید و آخرین ویژگی را شکسته باشید، بنابراین برای مدتی آن را دوباره آزمایش نمی کنید و نمی دانید اکنون خراب است.

به زبان ساده، آزمایش دستی برای شروع کار مناسبی است – اما به خوبی مقیاس بندی نمی شود و کیفیت کد را برای پروژه های بزرگتر تضمین نمی کند. خبر خوب این است که رایانه ها در کارهایی مانند این عالی هستند، ما باید از تست های خودکار تشکر کنیم!

تست خودکار

در تست خودکار، کد اضافی را برای آزمایش کد برنامه خود می نویسید. بعد از اینکه کد تست را نوشتید، می توانید برنامه خود را هر چند بار که می خواهید با حداقل تلاش آزمایش کنید.

تکنیک های متعددی برای نوشتن تست های خودکار وجود دارد:

  • نوشتن برنامه هایی برای خودکارسازی مرورگر،
  • فراخوانی توابع به طور مستقیم از کد منبع شما،
  • مقایسه اسکرین شات های برنامه رندر شده شما…

هر تکنیک مجموعه ای از مزایای خاص خود را دارد، اما همه آنها یک ویژگی مشترک دارند: آنها در زمان شما صرفه جویی می کنند و کیفیت کد بالاتر را نسبت به آزمایش دستی تضمین می کنند!

تست های خودکار برای اطمینان از اینکه برنامه شما طبق برنامه ریزی انجام می شود عالی هستند. آنها همچنین مرور تغییرات کد را در یک برنامه آسان تر می کنند.

انواع تست

تا کنون، ما به تست ها در سطح بالایی نگاه کرده ایم. زمان آن فرا رسیده است که در مورد انواع مختلف آزمون هایی که می توان نوشتند بحث کرد.

سه نوع تست برنامه کاربردی front-end وجود دارد:

  • تست های واحد: در تست های واحد، تک تک واحدها یا اجزای نرم افزار تست می شوند. یک واحد منفرد یک تابع، روش، رویه، ماژول، جزء یا شیء واحد است. تست واحد، بخشی از کد را جداسازی و تأیید می‌کند تا تأیید کند که هر واحد از کد نرم‌افزار مطابق انتظار عمل می‌کند.
    ماژول ها یا توابع مجزا در تست واحد آزمایش می شوند تا اطمینان حاصل شود که همانطور که باید به درستی کار می کنند و همه اجزاء نیز به صورت جداگانه آزمایش می شوند. برای مثال، تست واحد شامل تعیین اینکه آیا یک تابع، یک دستور یا یک حلقه در یک برنامه به درستی کار می کند یا خیر.

  • تست های عکس فوری: این نوع آزمایش تضمین می کند که رابط کاربری (UI) یک برنامه وب به طور غیر منتظره تغییر نمی کند. کد یک کامپوننت را در یک نقطه زمانی خاص می گیرد و به ما امکان می دهد کامپوننت را در یک حالت با هر حالت ممکن دیگری که می تواند داشته باشد مقایسه کنیم.
    یک سناریوی تست اسنپ شات معمولی شامل رندر کردن یک مؤلفه رابط کاربری، گرفتن یک عکس فوری و مقایسه عکس فوری با یک فایل عکس فوری مرجع است که همراه با آزمون نگهداری می شود. اگر دو عکس فوری متفاوت باشند، آزمایش ناموفق خواهد بود زیرا تغییر یا غیرمنتظره بوده است یا باید عکس فوری مرجع به‌روزرسانی شود تا مؤلفه رابط کاربری جدید را منعکس کند.

  • تست های پایان به انتها: آزمون های پایان به پایان ساده ترین نوع آزمون برای درک هستند. آزمایش‌های سرتاسری در برنامه‌های فرانت‌اند، مرورگر را خودکار می‌کند تا اطمینان حاصل شود که یک برنامه از دیدگاه کاربر به درستی کار می‌کند.
    تست های انتها به انتها در زمان بسیار صرفه جویی می کنند. بعد از نوشتن، می‌توانید هر چند بار که بخواهید یک تست سرتاسری را اجرا کنید. در نظر بگیرید که مجموعه‌ای از صدها تست می‌تواند در مقایسه با نوشتن تست‌های هر واحد مجزا چقدر زمان صرفه‌جویی کند.
    با تمام مزایایی که به ارمغان می آورد، تست های انتها به انتها چند مشکل دارند. برای شروع، تست های انتها به انتها زمان بر هستند. یکی دیگر از مسائل مربوط به تست های انتها به انتها این است که اشکال زدایی آنها ممکن است دشوار باشد.

توجه داشته باشید: برای جلوگیری از مشکلات تکرارپذیری، آزمایش‌های سرتاسری را می‌توان در یک محیط تکرارپذیر اجرا کرد، مانند کانتینر داکر. کانتینرهای داکر و آزمایش‌های سرتاسری خارج از محدوده این راهنما هستند، اما اگر می‌خواهید آزمایش‌های سرتاسری را اجرا کنید تا از مشکل خرابی در ماشین‌های مختلف جلوگیری کنید، باید آنها را بررسی کنید.

اگر می‌خواهید اصول اولیه آزمایش انتها به انتها با Cypress را درک کنید - ما را بخوانید "آزمایش انتها به انتها در جاوا اسکریپت با Cypress"!

مزایا و معایب تست

در حالی که آزمایش مهم است و باید انجام شود، طبق معمول، هم مزایا و هم معایبی دارد.

مزایای

  • از پسرفت غیرمنتظره محافظت می کند
  • تست صحیح به طور قابل توجهی کیفیت کد را افزایش می دهد
  • این به توسعه‌دهنده اجازه می‌دهد تا به جای گذشته، روی کار فعلی تمرکز کند
  • ساخت ماژولار برنامه‌هایی را که ساختن آنها سخت است را امکان‌پذیر می‌سازد
  • نیاز به تأیید دستی را از بین می برد

معایب

  • شما باید علاوه بر اشکال زدایی و نگهداری کد بیشتری بنویسید، و بسیاری احساس می کنند که بدون در نظر گرفتن مزایا، در پروژه های کوچکتر سربار غیرضروری است.
  • خرابی‌های تست غیر بحرانی/خوش خیم ممکن است منجر به رد شدن برنامه در طول یکپارچه‌سازی مداوم شود

بررسی اجمالی تست واحد

تا اینجا به طور کلی نگاهی به تست انداختیم. اکنون زمان آن است که به تمام موارد مربوط به تست واحد و نحوه نوشتن تست های واحد در برنامه های React بپردازیم!

قبل از تعریف تست واحد، لازم است بدانیم که یک روش تست خوب هدف آن سرعت بخشیدن به زمان توسعه، کاهش باگ ها در یک برنامه و بهبود کیفیت کد است، در حالی که یک رویکرد تست ضعیف یک برنامه را فلج می کند. در نتیجه، به عنوان توسعه دهندگان نرم افزار، ما باید رویکردهای موثر تست واحد را یاد بگیریم و یکی از آنها تست واحد است.

یک تعریف ساده از تست این است که فرآیند بررسی درستی عملکرد یک برنامه است. تست واحد فرآیند اجرای آزمایش‌ها بر روی اجزا یا عملکردهای یک برنامه کاربردی است. تست‌های واحد توابعی هستند که نسخه‌های مجزای توابع موجود در کد منبع شما را فراخوانی می‌کنند تا بررسی کنند که آن‌طور که باید و به‌طور قطعی رفتار می‌کنند.

نکات مثبت تست های واحد

تست‌های واحد سریع هستند و می‌توانند در چند ثانیه اجرا شوند (چه به صورت جداگانه برای یک ویژگی جدید و چه به صورت سراسری همه آزمایش‌ها را اجرا می‌کنند)، که به توسعه‌دهندگان بازخورد فوری درباره خرابی یا عدم خرابی یک ویژگی می‌دهد. آنها همچنین به ارائه مستندات کمک می کنند، زیرا اگر یک توسعه دهنده جدید به یک پروژه ملحق شود، باید بدانند واحدهای مختلف پایگاه کد چگونه رفتار می کنند. این را می توان با مشاهده نتایج آزمون های واحد فهمید.

معایب آزمون های واحد

در حالی که تست های واحد جنبه های خوبی دارند، اما مشکلات خاص خود را نیز دارند. یک مشکل این است که وقتی صحبت از آن به میان می آید، کد refactoring است تغییرات طراحی، زیرا اینها با آزمون های واحد دشوارتر هستند. مثلاً، یک تابع پیچیده با تست های واحد آن دارید و می خواهید آن تابع را به چندین تابع مدولار تقسیم کنید. یک تست واحد احتمالاً برای آن تابع ناموفق خواهد بود، و باید آن را منسوخ کنید و دو تست واحد برای توابع تقسیم بنویسید. به همین دلیل است که تست واحد به طور ضمنی تقسیم آنها را از قبل و آزمایش جداگانه آنها را تشویق می کند، که منجر به مؤلفه های کد ماژولار و قابل آزمایش تر می شود. با این وجود، در برخی موارد، نمی‌توانید تغییرات احتمالی را پیش‌بینی کنید، و مدت زمانی که برای به‌روزرسانی آزمایش‌های واحد طول می‌کشد، فرآیندهای بازسازی جدی را کمتر جذاب می‌کند.

مشکل دیگر تست واحد این است که فقط بخش‌های جداگانه یک برنامه را بررسی می‌کند، حتی اگر آن بخش ترکیبی منطقی از چندین بخش کوچک‌تر باشد - هیچ واحد آزمایش برای کل برنامه بخش‌های جداگانه یک برنامه ممکن است به درستی کار کنند، اما اگر نحوه رفتار آنها هنگام ترکیب آزمایش نشود، آزمایش‌ها ممکن است بی‌فایده شوند. به همین دلیل است که تست‌های واحد باید با تست‌های سرتاسر یا تست‌های ادغام یا در حالت ایده‌آل – هر دو تکمیل شوند.

واحد تست یک React Application – Demo Project

بیایید نگاهی به یک مثال واقعی از واحد آزمایش یک برنامه React بیندازیم!

در این نسخه ی نمایشی، ما یک برنامه Counter را با تعداد زیادی بخش مختلف آزمایش خواهیم کرد. علیرغم اینکه شبیه یک برنامه بسیار ساده به نظر می رسد، به عنوان مثال خوبی برای یادگیری نحوه عملکرد تست واحد عمل می کند. ماهیت آزمایش این برنامه این است که جنبه های مختلفی از مؤلفه وجود دارد که به نحوه تعامل کاربر با آن بستگی دارد.

راه اندازی پروژه

La create-react-app دستور ساخته شده توسط تیم React، بهترین راه برای شروع ایجاد یک برنامه React در دنیای واقعی و در مقیاس بزرگ است زیرا آماده استفاده است و به راحتی با کتابخانه تست شوخی. اگر در را باز کنید package.json فایل، خواهید دید که ما پشتیبانی پیش فرض برای آن داریم جادوگری و کتابخانه تست واکنش در setupTests.js فایل. در صورت نیاز، نیازی به نصب دستی Jest در پروژه خود نداریم!

اگر قبلاً از آن استفاده نکرده اید - آن را با آن اجرا کنید npx، که آن را برای استفاده بعدی نصب می کند:

$ npx create-react-app react-unit-tests

اگر قبلاً این ابزار را نصب کرده اید، یک برنامه React ایجاد کنید و نام آن را بگذارید react-unit-tests:

$ create-react-app react-unit-tests

توجه داشته باشید: npx از آخرین نسخه استفاده می کند create-react-app، در حالی که ممکن است نسخه جهانی نصب شده نباشد. به طور کلی توصیه می شود که ابزار را از طریق آن اجرا کنید npx برای اطمینان از آخرین نسخه ها، مگر اینکه به طور هدفمند بخواهید از نسخه دیگری استفاده کنید.

سپس وارد دایرکتوری پروژه می شویم و سرور توسعه را راه اندازی می کنیم:

$ cd react-unit-tests && npm start
// OR
$ cd react-unit-tests && yarn start

این برنامه جدید ایجاد شده ما را در مرورگر خروجی می دهد localhost:3000.

توجه داشته باشید: یک ویژگی مفید در اینجا این است که بارگذاری مجدد داغ به طور پیش فرض پشتیبانی می شود، بنابراین نیازی به بارگیری مجدد مرورگر برای مشاهده تغییرات جدید یا نصب دستی نیست nodemon یا کتابخانه های مشابه.

ساخت مولفه شمارنده

در src دایرکتوری پروژه ما، یک فایل جدید به نام ایجاد کنید Counter.js. به Counter.js، تمام قسمت های کامپوننت را تعریف می کنیم. این شامل توابع مختلف شمارنده، از جمله increment(), decrement(), restart()و switchSign()، که با کلیک کردن، مقدار شمارش را از منفی به مثبت معکوس می کند. این توابع برای دستکاری مقدار شمارش اولیه (که به عنوان یک پایه ارسال می شود) ایجاد می شوند:


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount((prev) => prev + 1);
  };

  const decrement = () => {
    setCount((prev) => prev - 1);
  };

  const restart = () => {
    setCount(0);
  };

  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
        Count: <h3>{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

سپس، به روز رسانی کنید App.js:


import "./App.css";
import Counter from "./Counter";

function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}

export default App;

اکنون می‌توانیم برنامه شمارنده را در مرورگر مشاهده کنیم:

Definitive Guide to Unit Testing in React Applications with Jest and React-Testing PlatoBlockchain Data Intelligence. Vertical Search. Ai.

ایجاد تست برای کامپوننت ها

بیایید یک فایل آزمایشی به نام ایجاد کنیم Counter.test.js برای نشان دادن آزمون مولفه Counter. حتما حذف کنید App.test.js به طوری که در حین اجرای آزمایش نتایج ناخواسته ایجاد نکند.

توجه داشته باشید: یک روش معمول این است که فایل های آزمایشی خود را با پسوند نامگذاری کنید .test.js، بازتاب نام فایل/جزئی که در حال آزمایش هستید. این امر تداوم بین فایل‌های آزمایشی را تضمین می‌کند، تغییرات فقط در فایل‌های مربوط به کدی که به‌روزرسانی می‌کنید هنگام فشار دادن تغییرات (تعداد کمتر تداخل ادغام) ایجاد می‌شود و قابل خواندن است.

علاوه بر این، فایل های آزمایشی معمولاً در a قرار دارند /test فهرست راهنما موازی به دایرکتوری اصلی کد منبع شما، هر چند، این نیز وابسته به تیم است.

In Counter.test.js، ابتدا وارد می کنیم Counter جزء، سپس تست را با describe() تابع برای توصیف تمام عملکردهای مختلف که ممکن است در کامپوننت اتفاق بیفتد.

La describe() تابع برای گروه بندی مجموعه های خاصی از تست ها که می توانند روی یک جزء با استفاده از موارد مختلف انجام شوند، استفاده می شود it() و test() مواد و روش ها. این نوعی بسته بندی منطقی است، که در آن شما، خوب، توصیف می کنید که یک سری از آزمایش ها با هر کدام چه می کنند. it() یک تست عملکردی برای یک واحد است.

آزمایش مؤلفه‌های React شما می‌تواند به گونه‌ای انجام شود که ما ممکن است از یک رندر آزمایشی برای ایجاد سریع یک مقدار قابل سریال‌سازی برای درخت React شما به جای ایجاد رابط کاربری گرافیکی استفاده کنیم، که شامل ایجاد برنامه کامل می‌شود.

آزمایش مقدار شمارنده اولیه

هنگام آزمایش، به ایجاد فهرستی منظم از ویژگی‌ها و جنبه‌های یک ویژگی خاص کمک می‌کند – حالت‌هایی که اجزا می‌توانند در آن قرار گیرند، چه چیزی می‌تواند بر آنها تأثیر بگذارد و غیره.

اولین چیزی که ما می خواهیم تست کنیم این است مقدار شمارش اولیه و چگونه کامپوننت با پایه ای که آن را تنظیم می کند کنترل می کند. با it() روش، بررسی می کنیم که آیا برنامه شمارنده واقعاً مقدار دقیق شمارش اولیه را که به عنوان یک پایه ارسال شده است را نمایش می دهد یا خیر، که 0 در این مورد، و یک تابع فراخوانی را ارسال کنید که تمام اقداماتی را که در داخل آزمایش رخ می دهد، توصیف می کند:


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(screen.getByTestId("count").textContent).toEqual(0);
  });
});

در اینجا، ما از screen نمونه ای از کتابخانه React Testing برای ارائه کامپوننت برای اهداف آزمایشی. ارائه یک نسخه ساختگی از یک مؤلفه برای آزمایش مفید است. و از آنجایی که

عنصری که count مقدار موظف به تغییر پویا است، ما از آن استفاده می کنیم screen.getByTestId() تابع برای گوش دادن به آن و واکشی ارزش آن با textContent ویژگی.

راهنمای عملی و عملی ما برای یادگیری Git را با بهترین روش ها، استانداردهای پذیرفته شده در صنعت و برگه تقلب شامل بررسی کنید. دستورات Google Git را متوقف کنید و در واقع یاد گرفتن آی تی!

توجه داشته باشید: La screen تابع یک گره DOM منطبق را برای هر پرس و جو برمی گرداند یا در صورت یافتن هیچ عنصری خطا می دهد.

سپس، در Counter.js جزء، ما به گوش

عنصر هنگام آزمایش با تنظیم a data-testid به عنصر دارای مقدار نسبت دهید count:


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  const increment = () => {
    setCount((prev) => prev + 1);
  };
  const decrement = () => {
    setCount((prev) => prev - 1);
  };
  const restart = () => {
    setCount(0);
  };
  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
      	
        Count: <h3 data-testid="count">{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

برای تست اینکه آیا اولیه است count ارزش برابر است با 0، ما از expect() روشی برای توصیف آنچه از آزمونی که ما تنظیم کرده ایم انتظار می رود! در مورد ما، ما انتظار داریم که مقدار شمارش اولیه باشد 0 بنابراین ما از toEqual() روشی که برای تعیین اینکه آیا مقادیر دو شیء مطابقت دارند یا خیر استفاده می شود. به جای تعیین هویت شی، toEqual() matcher به صورت بازگشتی همه فیلدها را برای برابری بررسی می کند.

توجه داشته باشید: آزمون به طور خاص بر روی اطلاعاتی که ارائه می دهید متمرکز است. در مثال ما، یعنی Counter جزء که دریافت کرده است initialCount پشتیبانی این نشان می دهد که حتی اگر یک فایل دیگر—بگویید، اجازه دهید App.js- دارای وسایل از دست رفته در Counter کامپوننت، آزمون همچنان قبول خواهد شد زیرا صرفاً روی آن متمرکز است Counter.js و نحوه استفاده از آن را نمی داند Counter جزء. علاوه بر این، از آنجایی که تست‌ها مستقل از یکدیگر هستند، رندر کردن یک جزء با پایه‌های مختلف در تست‌های دیگر نیز تاثیری بر آن نخواهد داشت.

اکنون می توانیم تست مجموعه را اجرا کنیم:

$ yarn test

آزمون باید شکست بخورد:

FAIL  src/Counter.test.js
  Counter
    × counter displays correct initial count (75 ms)

  ● Counter › counter displays correct initial count

    expect(received).toEqual(expected) // deep equality

    Expected: 0
    Received: "0"

       5 |   it("counter displays correct initial count", () => {
       6 |     render();
    >  7 |     expect(screen.getByTestId("count").textContent).toEqual(0);
         |                                                     ^
       8 |   });
       9 | });
      10 |

      at Object. (src/Counter.test.js:7:53)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.929 s, estimated 2 s
Ran all test suites related to changed files.

این آزمایش ناموفق بود زیرا ما یک عدد را در برابر یک رشته آزمایش کرده بودیم که نتیجه آن a خطای عمیق برابری. برای رفع آن، انداختن la textContent، یعنی مقدار اولیه، در تابع callback ما به عنوان یک عدد:


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
     
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });
});

اکنون، کد ما اولین آزمایش را با موفقیت پشت سر می گذارد:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.271 s
Ran all test suites related to changed files.

این یک مثال ساده از نحوه آزمایش است در حین نوشتن منطق به شما کمک می‌کند تا قبل از انباشته شدن بیشتر بدهی‌های فناوری، از مشکلات بعدی جلوگیری کنید. آزمایش پیش از موعد نیز می‌تواند شما را قفل کند، زیرا در صورتی که مجبور به نوشتن مجدد تست‌ها باشید، بازنویسی و تغییر منطق از نظر زمانی گران‌تر است.

یافتن یک تعادل خوب می‌تواند به ارتقای کیفیت نرم‌افزار شما کمک کند و کمترین تأثیر منفی بر بهره‌وری و سرعت شما داشته باشد.

تست دکمه افزایش

برای آزمایش که increment دکمه همانطور که باید کار می کند، یعنی برای افزایش count هر بار که روی آن کلیک می شود یک مقدار، ابتدا باید به آن دسترسی داشته باشیم increment را فشار دهید، سپس یک جدید تعریف می کنیم it() روش برای همان

از آنجایی که مقدار دکمه پویا نیست، یعنی همیشه مقدار آن را خواهد داشت Increment در داخل آن، ما از getByRole() روش به جای getByTestId() برای پرس و جو از DOM.

هنگام استفاده از getByRole() روش، یک نقش یک عنصر HTML را توصیف می کند.

همچنین باید یک شی را وارد کنیم تا مشخص کنیم که کدام دکمه را می‌خواهیم آزمایش کنیم، زیرا ممکن است هنگام رندر شدن DOM دکمه‌های زیادی وجود داشته باشد. در شیء، a را تنظیم می کنیم name با مقداری که باید با متن روی دکمه افزایش یکسان باشد.

کار بعدی شبیه سازی یک رویداد کلیک با استفاده از fireEvent() روشی که باعث می‌شود رویدادهایی که اقدامات کاربر را در حین آزمایش شبیه‌سازی می‌کنند، اجرا کنند.

ابتدا یک تست می نویسیم تا ببینیم آیا مقدار شمارش از مقدار اولیه آن 1 0 افزایش می یابد یا خیر:


import { fireEvent, render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });

  it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue = Number(screen.getByTestId("count").textContent);
    expect(countValue).toEqual(1);
  });
});

این نتیجه در:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (66 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.405 s
Ran all test suites related to changed files.

سپس، ما همچنین می توانیم یک تست بنویسیم تا بررسی کنیم که آیا count مقدار 0 قبل از کلیک روی دکمه با تعریف دو بود expect() روش ها – یکی قبل از فعال شدن رویداد کلیک و دیگری بعد از فعال شدن رویداد کلیک:


it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    let countValue1 = Number(screen.getByTestId("count").textContent);
    expect(countValue1).toEqual(0);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue2 = Number(screen.getByTestId("count").textContent);
    expect(countValue2).toEqual(1);
});

تست ها همچنان گذراندند:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (82 ms)
    √ count should increment by 1 if increment button is clicked (60 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.388 s
Ran all test suites related to changed files.

تست دکمه کاهش

به همین ترتیب ما تست را برای آن نوشتیم Increment را فشار دهید، تست را برای آن تعریف می کنیم Decrement دکمه مانند این:


it("count should decrement by 1 if decrement button is clicked", () => {
  render(<Counter initialCount={0} />);
  fireEvent.click(screen.getByRole("button", { name: "Decrement" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(-1);
});

این نتیجه در:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (73 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.346 s
Ran all test suites related to changed files.

تست دکمه Restart

شبیه به Increment و Decrement دکمه ها، تست را برای آن تعریف می کنیم Restart دکمه مانند این:


it("count should reset to 0 if restart button is clicked", () => {
  render(<Counter initialCount={50} />);
  fireEvent.click(screen.getByRole("button", { name: "Restart" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(0);
});

برای آزمایش، مقدار اولیه روی 50 تنظیم شد (مقدار دلخواه) و وقتی تست اجرا شد، هر چهار تست با موفقیت انجام می شود:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)
    √ count should increment by 1 if increment button is clicked (57 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (16 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.583 s
Ran all test suites related to changed files.

تست دکمه علامت سوئیچ

ما همچنین تست معکوس کردن علامت را بر روی می نویسیم count ارزش با تنظیم مقدار count تا 50 در فایل تست. سپس به این دقت کنید که چه علامتی قبل و بعد از اجرای یک رویداد کلیک توسط دکمه اجرا می شود:


it("count invert signs if switch signs button is clicked", () => {
  render(<Counter initialCount={50} />);
  let countValue1 = Number(screen.getByTestId("count").textContent);
  expect(countValue1).toEqual(50);
  fireEvent.click(screen.getByRole("button", { name: "Switch signs" }));
  let countValue2 = Number(screen.getByTestId("count").textContent);
  expect(countValue2).toEqual(-50);
});

این نتیجه در:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (91 ms)
    √ count should increment by 1 if increment button is clicked (72 ms)
    √ count should decrement by 1 if increment button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (19 ms)
    √ count invert signs if switch signs button is clicked (14 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        3.104 s
Ran all test suites related to changed files.

ووش! همه آزمایشات برای برنامه پیشخوان ما با موفقیت سپری شده است.

نوشتن تست‌ها سخت نیست - ما به طور موثر موارد استفاده یک ویژگی را شبیه‌سازی می‌کنیم تا مطمئن شویم که در صورت استفاده به‌صورت موردنظر و ناخواسته خراب نمی‌شود. آیا کسی ارزشی خارج از محدوده ارائه کرده است؟ فرمت اشتباه؟ برنامه باید به جای شکست مشکل را حل کند.

به طور کلی، یک نقطه شروع خوب برای آزمایش این است:

  • برای رفتار مورد نظر آزمایش کنید (هر ویژگی‌هایی که دارید)
  • تست تمام جنبه های رفتار ناخواسته (ورودی های اشتباه، مانند فرمت های پشتیبانی نشده، کران ها، و غیره)
  • آزمایش عددی (اگر ویژگی شما مقادیر عددی قابل تأیید را تولید می کند، نتیجه را با دست محاسبه کنید و بررسی کنید که آیا خروجی درست را برمی گرداند)

بهترین روش ها برای تست واحد

  • تست ها باید قطعی باشند: اجرای چندین بار تست های یکسان روی یک جزء باید هر بار نتایج یکسانی را به همراه داشته باشد. باید مطمئن شوید که عکس‌های فوری تولید شده شما حاوی داده‌های خاص پلتفرم یا سایر داده‌های غیر قطعی نیست.

  • از انجام آزمایشات غیر ضروری خودداری کنید: تست های خوب با انتظارات غیر ضروری یا موارد آزمایش همراه نیستند.
    با نگاهی به تست های زیر می توانیم درک بهتری پیدا کنیم:

test('the success modal is visible', () => {});
test('the success modal has a success message', () => {});

اگر بدانیم که پیام موفقیت در مدال موفقیت قابل مشاهده است، به این معنی است که خود مدال موفقیت نیز قابل مشاهده است. بنابراین در این صورت می‌توانیم با خیال راحت اولین آزمایش را حذف کنیم و فقط آزمایش دوم را انجام دهیم یا آنها را با هم ترکیب کنیم. انجام آزمایش‌های زیاد می‌تواند احساس امنیت کاذبی را در صورت زیاد بودن آنها ایجاد کند.

  • از افشای منطق داخلی خودداری کنید: اگر تست شما عملی را انجام می دهد که کاربر شما انجام نمی دهد (مانند آزمایش یک روش داخلی که در معرض دید کاربر نیست)، به احتمال زیاد جزئیات پیاده سازی را آزمایش می کنید. شما می توانید یک تابع خصوصی را صرفاً برای آزمایش مؤلفه خود در معرض نمایش قرار دهید. این یک بوی رمز است که باید از آن اجتناب کرد. در عوض، پایگاه کد خود را بازسازی کنید تا عملکرد خصوصی بدون افشای عمومی قابل آزمایش باشد.

  • از آزمایش جزئیات پیاده سازی خودداری کنید: اگر برنامه ما افزایش یابد x و y - چه x اول افزایش می یابد یا به احتمال زیاد هیچ اهمیتی ندارد، تا زمانی که نتیجه یکسان باشد. شما باید بتوانید همیشه جزئیات پیاده سازی را تغییر دهید، تغییر دهید و در غیر این صورت به روز کنید بدون شکستن تست هادر غیر این صورت، آزمایش‌ها با افزایش هزینه بازسازی و بهینه‌سازی، انباشت بدهی فناوری را تسریع می‌کنند.

  • منطق کسب و کار را به جای اجزای UI در توابع خالص قرار دهید.

نتیجه

این راهنما در درجه اول در مورد تست واحد است. با این حال، مهم این بود که ما ابتدا همه چیزهایی را که شامل تست می‌شود، از جمله معنای آن، رویکردهای تست، انواع تست، و مزایا و معایب آن را درک کرده و قدردانی کنیم.

این مهم است که به خاطر داشته باشید که چرا در حال نوشتن تست ها هستید. به طور معمول، هدف از نوشتن تست ها صرفه جویی در زمان است. اگر پروژه ای که روی آن کار می کنید پایدار باشد و برای مدت طولانی توسعه یابد، آزمایش ها سود سهام را به همراه دارد. با این اوصاف، می توان با اطمینان گفت که آزمایش یک برنامه ممکن است ارزش آن را نداشته باشد، اگر در زمان توسعه شما صرفه جویی نشود. مهمتر از همه، تست های خوب برای حفظ و حفظ اطمینان در هنگام تغییر کد شما ساده هستند.

ما همچنین یاد گرفتیم که چگونه در حین آزمایش برنامه های React با استفاده از DOM پرس و جو کنیم getByTestId() روش. برای تعریف کانتینرها و عناصر پرس و جو با متن پویا بسیار مفید است، اما نباید درخواست پیش فرض شما باشد. به جای استفاده از getByTestId() روش فوراً، ابتدا یکی از اینها را امتحان کنید:

  • getByRole() - یک عنصر را پرس و جو می کند و در عین حال اطمینان می دهد که با نقش و متن صحیح در دسترس است
  • getByLabelText() - این یک پرس و جو عالی برای تعامل با عناصر فرم است، همچنین بررسی می کند که برچسب های ما از طریق ویژگی های for و id به درستی به ورودی های ما مرتبط هستند.
  • getByText() - هنگامی که هیچ یک از دو عبارت قبلی در دسترس نباشد، getByText() این روش در دسترسی به عناصر بر اساس متنی که برای کاربر قابل مشاهده است مفید خواهد بود
  • getByPlaceholderText(): این پرس و جو نسبت به شناسه آزمایشی بسیار ارجحیت دارد، زمانی که تنها چیزی که باید یک عنصر را پرس و جو کنید یک مکان نگهدار است.

ما امیدواریم که این راهنما برای شما مفید باشد! با استفاده از این می‌توانید به مخزن این راهنما دسترسی داشته باشید و با همه چیزهایی که در آن است بازی کنید لینک در GitHub.

تمبر زمان:

بیشتر از Stackabuse