هرم تست فرانت را از رفتار کاربر بچین، نه از تعداد فایل تست
فرانت دیگر صفحه ایستا نیست. پرداخت، نشست، و مسیر رندر سرور این وسط است. باگ تولید گران است، ولی جمله «صد برابر گرانتر» به درد برنامهریزی اسپرینت نمیخورد. چیزی که به درد میخورد این است که بفهمی هر لایه تست کدام شکست را ارزان میگیرد و کدام را فقط گران تکرار میکند.
هرم را جدی بگیر، نه بهخاطر شکل قشنگش. پایه زیاد و سریع، وسط کمتر، نوک خیلی کم. اگر نوک هرم بزرگتر از پایه است، داری با مرورگر چیزهایی را تست میکنی که یک تابع خالص در چند میلیثانیه میگفت.
چه چیزی را واحد حساب کنی
واحد یعنی یک رفتار، بدون شبکه و بدون مرورگر. تابع تبدیل قیمت، قانون اعتبار فرم، مپر پاسخ API به مدل صفحه. خروجی برای ورودی ثابت همان است. این تستها باید آنقدر سریع باشند که ذخیره فایل خستهشان نکند.
در React، رندر کامپوننت با Testing Library هنوز میتواند واحد باشد اگر وابستگی خارجی را جعل کنی و فقط رفتار قابل دیدن را بسنجی: با این props چه متنی هست، کلیک چه صدا میزند، حالت خطا چه میگوید. پیادهسازی داخلی، نام state، یا کلاس CSS، تست شکننده میسازد. فردا ریفکتور میکنی و پنجاه تست قرمز میشود بدون باگ کاربر.
لبه را همینجا بگیر. رشته خالی، عدد منفی، تاریخ نامعتبر، خطای شبکه بهصورت مقدار برگشتی. اینها در E2E گران و کندند و اغلب فراموش میشوند.
Jest یا Vitest با Testing Library برای این لایه کافی است. ابزار را وسط پروژه عوض نکن چون یک بنچمارک تازه دیده شده. ثبات اجرا مهمتر از نو بودن رانر است.
لایه یکپارچه، جایی که واحد دروغ میگوید
یکپارچه یعنی چند قطعه واقعی کنار هم، هنوز بدون کل پروداکشن. فرم با schema اعتبار، کامپوننت با کلاینت داده جعلشده، مسیر کلاینت که کوئری را به UI وصل میکند. سؤال این است: این قطعهها با هم قرارداد را نگه میدارند؟
این لایه جای mock افراطی نیست. اگر همهچیز mock است، داری واحد گران مینویسی. شبکه را در مرز جعل کن، نه داخل هر تابع. برای Next.js، منطق Server Action را میتوانی با ورودی ساختگی و وابستگی تزریقشده تست کنی، بدون بالا آوردن مرورگر.
چیزهایی که اینجا خوب مینشینند: وضعیت بارگذاری و خطا، غیرفعال بودن دکمه تا اعتبار، همگام ماندن stepper فرم، و اینکه کش کلاینت بعد از mutation باطل میشود. اگر اینها را به E2E بسپاری، هر شکست یعنی چند دقیقه لاگ مرورگر برای یک شرط جاافتاده.
E2E را برای چیزی بگذار که واحد نمیبیند
انتهای هرم مسیر کاربر است. ورود، کوکی، ریدایرکت middleware، رندر واقعی مسیر، و اینکه باندل پروداکشن همان صفحه را نشان میدهد. این را با Testing Library نمیبینی، چون اصلاً سرور و مرورگر در کار نیست.
کم بنویس. مسیرهایی که پول یا اعتماد را جابهجا میکنند: ثبتنام، ورود، پرداخت یا معادلش، و یک مسیر محتوای عمومی که ناوبریاش شکسته باشد دردسر پشتیبانی میسازد. بقیه را پایین هرم نگه دار.
روی App Router، E2E باید نزدیک پروداکشن باشد. `next build` و `next start` رفتار Server Component و middleware را شبیهتر از dev نشان میدهد. dev برای حلقه محلی خوب است؛ برای اطمینان انتشار کافی نیست.
E2E شکننده میشود اگر به متن تصادفی، انیمیشن، یا ترتیب شبکه واقعی بچسبد. داده را seed کن، منتظر وضعیت قابل مشاهده بمان نه `sleep` ثابت، و انتخابگر را به نقش و نام قابل دسترس تکیه بده.
جزئیات انتخاب Playwright یا Cypress را جای دیگری باز کردهام. اینجا فقط مرز مسئولیت مهم است. هیچکدام جایگزین واحد نیستند.
چه چیزی را عمداً تست نکن
پیادهسازی کتابخانه UI. استایل پیکسلی جز در چند نقطه حیاتی برند. تابعهایی که فقط فیلد را پاس میدهند. درصد coverage بهعنوان هدف. پوشش بالا با assert ضعیف آرامش دروغین است.
همچنین کل ماتریس مرورگر را روی هر واحد نکش. یک مرورگر در CI برای E2E مسیر بحرانی، و WebKit فقط اگر کاربر Safari برایت واقعی است. هزینه را به جایی بده که شکست دیده شده، نه به جایی که اسلاید معماری خالی بوده.
ترتیب عملی برای یک کدبیس موجود
اگر تقریباً تست نداری، از مسیر پول شروع نکن با پنجاه E2E. اول منطق خالصی که باگ تولید داده را واحد کن. بعد یک جریان فرم یا mutation را یکپارچه. آخر دو E2E دود: بالا آمدن صفحه عمومی، و یک مسیر احراز.
هر تست قرمز باید یک جمله فارسی داشته باشد: کاربر چه چیزی را از دست میدهد. اگر جمله را نمیتوانی بنویسی، تست یا اضافی است یا اسم بد دارد.
نگهداری را در تعریف انجامشده بگذار. تستی که کسی آپدیت نمیکند چون «فلکی است» را یا درست کن یا حذف کن. مجموعه قرمز دائمی بدتر از مجموعه کوچک سبز است، چون سیگنال را میکشد.
ناپایداری را باگ تست بدان، نه ویژگی E2E
تستی که گاهی قرمز است سیگنال را خراب میکند. تیم عادت میکند دکمه rerun را بزند. از آنجا به بعد قرمز واقعی هم تا فردا میماند.
علتهای تکراری را اسم ببر. انتظار ثابت بهجای شرط قابل مشاهده. داده مشترک بین تستهای موازی. وابستگی به ساعت یا منطقه زمانی. انیمیشن که کلیک را میدزدد. شبکه بیرونی که در CI کند است. هر کدام را یا از تست حذف کن یا ثابت کن. retry در CI برای E2E میتواند نویز محیطی را بگیرد، به شرطی که سقف داشته باشد و اثر جانبی تست دوباره اجرا نشود. retry روی تست واحد یعنی تست غلط است، نه بدشانسی.
بودجه زمان را هم بنویس. اگر واحدها باید زیر چند ده ثانیه تمام شوند و E2E مسیر بحرانی حق دارد چند دقیقه بگیرد، این عدد را در پایپلاین نشان بده. وقتی مجموعه آرامآرام به بیست دقیقه رسید، کسی عمداً کندش نکرده؛ هر تست «فقط یک مورد دیگر» اضافه شده. همانجا هرس کن، نه شش ماه بعد در یک پروژه جدا.
برای فرانت، دسترسیپذیری و تست رفتار هممسیرند. اگر دکمه نام قابل دسترس ندارد، هم کاربر صفحهخوان گیر میکند هم تست مجبور میشود به کلاس CSS بچسبد. نقش و نام را درست کن تا هر دو مشکل سبکتر شود.
داده تست را نزدیک سناریو بساز. fixture غولپیکر مشترک که همه به آن وابستهاند، با اولین تغییر محصول میشکند. سازنده کوچک با پیشفرض معقول، و فقط همان فیلدی که سناریو لازم دارد را عوض کن.
پرسشهای کوتاه
چند درصد پوشش لازم است؟ عدد جادویی ندارم. مسیرهای پرریسک باید رفتارشان قفل باشد. بقیه را با مرور و نوع تغییر بسنج.
همهچیز را با E2E بپوشانم راحتتر نیست؟ هفته اول بله. ماه سوم زمان CI و تستهای ناپایدار کار را متوقف میکنند.
کامپوننت سرور را چطور واحد تست کنم؟ رفتار دامنه را بیرون از JSX خالص کن و همان را واحد بگیر. رندر مسیر را به E2E سبک بسپار.