رفتن به محتوای اصلی
← همه‌ی تصمیم‌ها

گیت بیلد به‌جای تست واحد

accepted

زمینه

این ریپو تست واحد ندارد. چهار اسکریپت دارد که بیلد را می‌شکنند.

این موضعی است که باید بتوانم از آن دفاع کنم، چون «ما تست نمی‌نویسیم» معمولاً یک اعتراف است. استدلال من باریک‌تر از چیزی است که به نظر می‌رسد: برای باگ‌هایی که واقعاً در کار فرانت‌اند منتشر می‌شوند، مجموعه‌ی تست اغلب دارد لایه‌ی اشتباه را تست می‌کند.

باگ‌هایی که در کار UI من به production می‌رسند به‌ندرت «تابعی که عدد اشتباه برمی‌گرداند»‌اند. یک ویژگی فیزیکی CSS‌اند که در جهت خواندنِ دیگر برعکس می‌شود، کلید ترجمه‌ای که در یک زبان هست و در آن یکی نیست، نسبت کنتراستی که وقتی کسی توکنی را دستکاری کرد زیر ۴٫۵:۱ سُر خورده، یا باندلی که بی‌سروصدا از بودجه‌اش زده بیرون. هیچ‌کدام به شکل طبیعی قابل تست واحد نیستند و همه‌شان به‌سادگی قابل اسکریپت نوشتن‌اند.

گزینه‌های بررسی‌شده

Vitest به‌همراه Testing Library. گزینه‌ی پیش‌فرض. ریاضیات رنگ را خوب پوشش می‌داد و باگ‌های جهت را اصلاً نه.

Playwright و تست end-to-end. رگرسیون‌های رندر را می‌گرفت، اما کند، و با هزینه‌ی نگهداری‌ای که برای سایتی با چهار روت نمی‌خواهم.

اسکریپت‌های assertion که به یک فرمان verify وصل شده‌اند. هرکدام قاعده‌ای را کد می‌کنند که در غیر این صورت باید توی ریویو و از روی حافظه اعمالش کنم.

تصمیم

یک فرمان زنجیره‌ای:

verify = build && lint && typecheck
       && check:style && check:direction && check:contrast && check:adr
  • check-direction.mjs روی هر یوتیلیتی فیزیکی Tailwind که معادل لاجیکال دارد بیلد را می‌شکند، مگر آنکه صریحاً با rtl:/ltr: نوشته شده باشد.
  • check-contrast.mjs موتور رنگ واقعی را اجرا می‌کند — همان تایپ‌اسکریپتی که مرورگر اجرا می‌کند، از طریق node --experimental-strip-types، نه یک نسخه‌ی کامپایل‌شده که می‌تواند فاصله بگیرد — روی همه‌ی seedهای پریست‌ها در هر دو حالت، و بررسی می‌کند که هر جفت نقش به هدف WCAG خودش رسیده باشد. ۱۴۴ جفت.
  • check-style.mjs همان دو قاعده‌ی فرمت‌بندی‌ای را اعمال می‌کند که واقعاً برایم مهم‌اند — بدون سمی‌کالن، و مشخص‌کننده‌های ماژول با کوتیشن تکی.
  • check-adr.mjs برابری زبانی همه‌ی رکوردهای تصمیم، ترتیب ثابت بخش‌ها، و این را اعمال می‌کند که هیچ متن ناتمامی منتشر نشود: یک TODO در رکورد accepted خطاست، در حالی که رکورد draft اجازه دارد داشته باشد و کلاً از ایندکس و از تولید استاتیک بیرون می‌ماند.

گیت رنگ فقط آستانه چک نمی‌کند. تبدیل OKLCH را پیش از آنکه چیزی رویش ساخته شود با مقادیر مرجع منتشرشده‌ی CSS Color 4 اعتبارسنجی می‌کند، ده رنگ را بدون اتلاف رفت‌وبرگشت می‌دهد، و هر توکن خروجی را دوباره اندازه می‌گیرد و با نسبتی که خودش گزارش کرده مقایسه می‌کند — پس موتور نمی‌تواند عددی را گزارش کند که واقعاً منتشرش نکرده.

هزینه‌هایی که پذیرفتم

  • این گیت‌ها باگ منطقی نمی‌گیرند. اگر جمع سبد خرید غلط باشد هیچ‌کدام متوجه نمی‌شوند. این حفره‌ی واقعی است و دقیقاً می‌دانم کجاست.
  • check-direction متن سورس را grep می‌کند. کلاسی که زمان اجرا ساخته شود برایش نامرئی است. ترجیح می‌دهم رشته‌های کلاس را ثابت نگه دارم تا اینکه بررسی را ضعیف کنم.
  • برای ریفکتور هیچ تور ایمنی ندارم. مجموعه‌ی تست به تو اجازه می‌دهد یک جزء داخلی را بیرون بکشی و مطمئن باشی رفتار حفظ شده. من آن را ندارم و روی کدبیس بزرگ‌تر می‌خواستمش.
  • هر گیت را خودم نوشته‌ام، پس هر گیت می‌تواند غلط باشد. یکی‌شان بود: ادعا کرده بودم سفید روی سفید قابل اصلاح نیست. هست — متن آزاد است تا سیاه تیره شود و به ۲۱:۱ برسد. موتور درست می‌گفت و تست من غلط بود.

نتیجه

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

چه چیزی را عوض می‌کردم

هر گیت را قبل از کدی می‌نوشتم که قرار است بر آن حکم کند. اول کامپوننت‌ها را نوشتم و گیت جهت را بعد، و بعد برگشتم و چیزهایی را که پیدا کرد درست کردم. نوشتنش در ابتدا طرز تایپ‌کردن را عوض می‌کند — وقتی می‌دانی این بررسی وجود دارد، دستت سراغ ms- می‌رود، نه اینکه ml- بزنی و بعد تمیزکاری کنی.

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

راه‌حل این نیست که یکی را انتخاب کنی. حالا Prettier با یک .prettierrc هماهنگ با سبک پروژه فرمت می‌کند و گیت هم سر جایش در CI است — چون فرمتری که کسی یادش برود اجرا کند تضمین نیست، و تضمینی که چیزی را فرمت نمی‌کند فرمتر نیست. این دو کار متفاوتی می‌کنند. من رقیب هم می‌دیدمشان چون می‌خواستم روایت مرتب‌تر از واقعیت باشد.

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