تست نباید خسته کننده باشد. با این بهترین روشها و نکات تست خودکار، برنامه نویس ها و توسعه دهندگان میتوانند از تستهای خودکار برای افزایش بهرهوری و لذتبخشتر کردن کارشان استفاده کنند.
جای تعجب نیست که بسیاری از توسعه دهندگان تست را به عنوان یک چیز الکی و ضروری می بینند که زمان و انرژی را کاهش می دهد: تست می تواند خسته کننده، غیرمولد و کاملاً پیچیده باشد.
اولین تجربه من با تست افتضاح بود. در یکی از پروژه های تیمی هنگام تغییر یکی از کد ها و آپدیت آن ها 10 تا تست دیگه به مشکل بر می خوردند و تجربه ناخوش آیندی بود.
من اوایل از کار با تست ها بدم می آمد زیرا آنها به عنوان یک بار وقت گیر تلقی می شدند. با این حال، من تسلیم نشدم. تست اطمینان فراهم میکند و خودکارسازی چکها پس از هر تغییر کوچک، توجه من را برانگیخت. شروع به خواندن و تمرین کردم و یاد گرفتم که تست ها، زمانی که به درستی انجام شوند، می توانند مفید و لذت بخش باشند.
در این مقاله، هشت روش تست خودکار را به اشتراک میگذارم که کاش از ابتدا میدانستم.
چرا به یک استراتژی تست خودکار نیاز دارید؟
تست خودکار اغلب بر روی آینده متمرکز است، اما زمانی که آن را به درستی اجرا کنید، فوراً سود خواهید برد. استفاده از ابزارهایی که به شما کمک میکنند کارتان را بهتر انجام دهید، میتواند باعث صرفهجویی در زمان و لذتتر شدن کارتان شود.
تصور کنید در حال توسعه سیستمی هستید که سفارشات خرید را از ERP شرکت بازیابی میکند و آن سفارشها را به یک فروشنده ارسال میکند. شما قیمت اقلامی را که قبلاً سفارش داده اید در ERP دارید، اما قیمت های فعلی ممکن است متفاوت باشد. شما می خواهید کنترل کنید که آیا با قیمت پایین تر یا بالاتر سفارش دهید. شما تنظیمات برگزیده کاربر را ذخیره کرده اید و در حال نوشتن کد برای کنترل نوسانات قیمت هستید.
چگونه بررسی می کنید که کد مطابق انتظار کار می کند؟ شما احتمالا:
یک سفارش ساختگی در نمونه ERP توسعه دهنده ایجاد کنید (با فرض اینکه آن را از قبل تنظیم کرده باشید).
برنامه خود را اجرا کنید
آن سفارش را انتخاب کنید و فرآیند ثبت سفارش را شروع کنید.
داده ها را از پایگاه داده ERP جمع آوری کنید.
قیمت های فعلی را از API فروشنده درخواست کنید.
برای ایجاد شرایط خاص، قیمتها را در کد لغو کنید.
شما در نقطه شکست توقف کردید و می توانید قدم به قدم پیش بروید تا ببینید برای یک سناریو چه اتفاقی می افتد، اما سناریوهای ممکن زیادی وجود دارد:
در صورت بروز اشکال، شرکت ممکن است پول خود را از دست بدهد، به شهرت آن آسیب برساند یا هر دو. شما باید چندین سناریو را بررسی کنید و حلقه چک را چندین بار تکرار کنید. انجام این کار به صورت دستی خسته کننده خواهد بود. اما تستها به شما کمک میکنند!
تستها به شما امکان میدهند هر زمینهای را بدون تماس با APIهای ناپایدار ایجاد کنید. آنها نیاز به کلیک های مکرر را از طریق رابط های قدیمی و کندی که در سیستم های ERP قدیمی بسیار رایج هستند، از بین می برند. تنها کاری که باید انجام دهید این است که زمینه را برای واحد یا زیرسیستم تعریف کنید و سپس هرگونه اشکال زدایی، عیب یابی یا کاوش سناریو فوراً اتفاق می افتد، تست را اجرا می کنید و به کد خود باز می گردید. ترجیح من این است که یک keybinding در IDE خود راه اندازی کنم که اجرای تستی قبلی من را تکرار کند و همزمان با ایجاد تغییرات، بازخورد خودکار و فوری ارائه دهد.
1. نگرش درست را حفظ کنید
در مقایسه با اشکالزدایی دستی و خودآزمایی، تستهای خودکار از همان ابتدا، حتی قبل از اجرای هر کد تستی، کارآمدتر هستند. بعد از اینکه بررسی کردید کد شما مطابق انتظار عمل می کند، با تست دستی یا شاید برای یک ماژول پیچیده تر، با گام برداشتن در آن با یک دیباگر در طول تست، می توانید از اظهارات برای تعریف آنچه برای هر ترکیبی از پارامترهای ورودی انتظار دارید استفاده کنید.
با گذراندن تستها، تقریباً آماده کامیت هستید، اما نه کاملاً. برای اصلاح کد خود آماده شوید زیرا اولین نسخه کار معمولاً زیبا نیست. آیا آن refactoring را بدون تست انجام می دهید؟ این مشکوک است زیرا شما باید تمام مراحل دستی را دوباره انجام دهید، که می تواند اشتیاق شما را کاهش دهد.
در مورد آینده چطور؟ در حین انجام هر گونه بازسازی، بهینهسازی یا افزودن ویژگی، تستها به اطمینان حاصل میکنند که یک ماژول پس از تغییر همچنان همانطور که انتظار میرود رفتار میکند، در نتیجه اعتماد پایدار را القا میکند و به توسعهدهندگان این امکان را میدهد که برای مقابله با کارهای آینده احساس آمادگی بهتری داشته باشند.
فکر کردن به تستها بهعنوان یک بار یا چیزی که فقط بازبینکنندگان کد یا سرنخها را خوشحال میکند، معکوس است. تست ها ابزاری هستند که ما به عنوان توسعه دهندگان از آن سود می بریم. زمانی را دوست داریم که کدمان کار کند و دوست نداریم برای رفع اشکالات وقت خود را صرف کارهای تکراری یا رفع کد کنیم.
البته باید نحوه استفاده از ابزار را بلد باشید. ممکن است به نظر برسد که تعریف context خستهکننده است و میتواند سختتر از اجرای برنامه باشد، زیرا تستها به نگهداری بیش از حد نیاز دارند تا سودمند و بیفایده نشوند. اینها نکات معتبری هستند و به آنها خواهیم پرداخت.
2. نوع مناسب تست را انتخاب کنید
توسعهدهندگان معمولاً از تستهای خودکار خوششان نمیآید، زیرا سعی میکنند دهها وابستگی را فقط برای بررسی اینکه آیا با کد فراخوانی شدهاند یا نه، مسخره کنند. از طرف دیگر، توسعهدهندگان با یک تست سطح بالا مواجه میشوند و سعی میکنند هر حالت برنامه را بازتولید کنند تا همه تغییرات را در یک ماژول کوچک بررسی کنند. این الگوها بیمولد و خستهکننده هستند، اما میتوانیم با استفاده از انواع تستهای مختلف همانطور که در نظر گرفته شدهاند، از آنها اجتناب کنیم. (تست ها باید عملی و لذت بخش باشند!)
خوانندگان باید بدانند تستهای واحد چیست و چگونه آنها را بنویسند، و با تستهای یکپارچهسازی آشنا باشند، در غیر این صورت، ارزش آن را دارد که در اینجا مکث کنید تا سرعت خود را افزایش دهید.
دهها نوع تست وجود دارد، اما این پنج نوع رایج ترکیبی بسیار مؤثر را ایجاد میکنند:
تست های واحد (Unit tests) برای تست یک ماژول ایزوله با فراخوانی مستقیم متد های آن استفاده می شود. وابستگی ها مورد تست قرار نمی گیرند، بنابراین، آنها را mock می کنند.
تست های یکپارچه سازی (Integration tests) برای تست زیرسیستم ها استفاده می شود. شما همچنان از تماسهای مستقیم به متد های خود ماژول استفاده میکنید، اما در اینجا ما به وابستگیها اهمیت میدهیم، بنابراین از وابستگیهای mock شده استفاده نکنید، فقط ماژولهای وابسته واقعی (تولید). شما همچنان می توانید از پایگاه داده درون حافظه یا وب سرور mock شده استفاده کنید زیرا اینها زیرساخت های ساختگی هستند.
تست های عملکردی (Functional tests) تست هایی برای کل برنامه هستند. شما از تماس مستقیم استفاده نمی کنید. در عوض، تمام تعاملات از طریق API یا رابط کاربری انجام می شود، اینها از دیدگاه کاربر نهایی تست هستند. با این حال، زیرساخت ها هنوز مورد mock قرار می گیرند.
تست Canary tests شبیه تست های Functional هستند اما با زیرساخت های تولید و مجموعه ای کوچکتر از اقدامات. از آنها برای اطمینان از کارکرد برنامه های تازه مستقر شده استفاده می شود.
تستهای بارگذاری (Load tests) مشابه تستهای Canary هستند، اما با زیرساختهای مرحلهبندی واقعی و مجموعهای از اقدامات حتی کوچکتر، که بارها تکرار میشوند.
همیشه لازم نیست از ابتدا با هر پنج نوع تست کار کنید. در بیشتر موارد، می توانید با سه تست اول راه زیادی را طی کنید.
ما به طور خلاصه موارد استفاده از هر نوع را بررسی می کنیم تا به شما در انتخاب موارد مناسب برای نیازهایتان کمک کنیم.
تست های واحد
مثال را با قیمت های مختلف و ترجیحات مدیریتی به یاد بیاورید. این کاندیدای خوبی برای تست واحد است زیرا ما فقط به آنچه در داخل ماژول اتفاق میافتد اهمیت میدهیم و نتایج پیامدهای تجاری مهمی دارند.
ماژول دارای تعداد زیادی ترکیب مختلف از پارامترهای ورودی است و ما می خواهیم برای هر ترکیبی از آرگومان های معتبر یک مقدار بازگشتی معتبر دریافت کنیم. تستهای واحد در تضمین اعتبار خوب هستند، زیرا دسترسی مستقیم به پارامترهای ورودی تابع یا روش را فراهم میکنند و مجبور نیستید دهها روش تست را برای پوشش هر ترکیب بنویسید. در بسیاری از زبانها، میتوانید با تعریف روشی که آرگومانهای مورد نیاز برای کد و نتایج مورد انتظار شما را میپذیرد، از تکرار روشهای تست اجتناب کنید. سپس، میتوانید از ابزار تست خود برای ارائه مجموعههای متفاوتی از مقادیر و انتظارات برای آن روش پارامتری استفاده کنید.
جهت آشنایی با 5 اصل تست واحد می توانید این مقاله را مطالعه کنید.
تست های یکپارچه سازی
تستهای یکپارچهسازی برای مواردی مناسب هستند که به نحوه تعامل یک ماژول با وابستگیها، سایر ماژولها یا زیرساخت علاقهمند هستید. شما همچنان از فراخوانیهای روش مستقیم استفاده میکنید، اما دسترسی به زیر ماژولها وجود ندارد، بنابراین تلاش برای تست همه سناریوها برای همه روشهای ورودی همه زیرماژولها غیرعملی است.
به طور معمول، من ترجیح می دهم در هر ماژول یک سناریوی موفقیت و یک سناریوی شکست داشته باشم.
من دوست دارم از تستهای یکپارچهسازی استفاده کنم تا بررسی کنم آیا ظرف تزریق وابستگی با موفقیت ساخته شده است، آیا Pipeline پردازش یا محاسبه نتیجه مورد انتظار را برمیگرداند یا اینکه دادههای پیچیده به درستی از پایگاه داده یا API شخص ثالث خوانده و تبدیل شدهاند.
تست های Functional
این تستها بیشترین اطمینان را به شما میدهند که برنامه شما کار میکند، زیرا تأیید میکنند که برنامه شما حداقل میتواند بدون خطای زمان اجرا شروع شود. شروع تست کد خود بدون دسترسی مستقیم به کلاسهای آن کمی کار بیشتری دارد، اما وقتی چند تست اول را فهمیدید و بنویسید، متوجه میشوید که خیلی سخت نیست.
در صورت نیاز، برنامه را با شروع فرآیندی با آرگومان های خط فرمان اجرا کنید و سپس از برنامه همانطور که مشتری بالقوه خود استفاده می کند استفاده کنید: با فراخوانی نقاط پایانی API یا فشار دادن دکمه ها. این کار حتی در مورد تست UI دشوار نیست: هر پلتفرم اصلی ابزاری برای یافتن یک عنصر بصری در یک رابط کاربری دارد.
تست های Canary
تستهای عملکردی به شما اطلاع میدهند که آیا برنامه شما در یک محیط تستی کار میکند، اما در مورد محیط تولید چطور؟ فرض کنید با چندین API شخص ثالث کار میکنید و میخواهید داشبوردی از وضعیتهای آنها داشته باشید یا میخواهید ببینید برنامه شما چگونه درخواستهای دریافتی را مدیریت میکند. این موارد رایج برای تست Canary هستند.
آنها با عمل کوتاه بر روی سیستم کار بدون ایجاد عوارض جانبی برای سیستم های شخص ثالث عمل می کنند. به عنوان مثال، می توانید یک کاربر جدید ثبت کنید یا بدون سفارش، در دسترس بودن محصول را بررسی کنید.
هدف از تستهای Canary این است که مطمئن شویم همه اجزای اصلی در یک محیط تولید با هم کار میکنند و مثلاً به دلیل مشکلات اعتباری شکست نمیخورند.
تست های بارگذاری
تستهای بارگذاری نشان میدهد که آیا برنامه شما زمانی که تعداد زیادی از افراد شروع به استفاده از آن میکنند به کار خود ادامه میدهد یا خیر. آنها شبیه تست های Canary و Functional هستند اما در محیط های محلی یا تولیدی انجام نمی شوند. معمولاً از محیط صحنه سازی خاصی استفاده می شود که مشابه محیط تولید است.
توجه به این نکته مهم است که این تستها از خدمات شخص ثالث واقعی استفاده نمیکنند، که ممکن است از تست بار خارجی خدمات تولیدی خود ناراضی باشند و در نتیجه ممکن است هزینه اضافی دریافت کنند.
جهت آشنایی با نحوه تهیه نقشه تست می توانید این مقاله را بررسی کنید.
3. انواع تست را جدا نگه دارید
هنگام طراحی طرح تست خودکار خود، هر نوع تست باید جدا شود تا بتواند به طور مستقل اجرا شود. در حالی که این امر مستلزم سازماندهی اضافی است، ارزش آن را دارد زیرا اختلاط تست ها می تواند مشکلاتی ایجاد کند.
این تست ها متفاوت هستند:
مقاصد و مفاهیم اساسی (بنابراین جداسازی آنها سابقه خوبی برای فرد بعدی ایجاد می کند که به کد نگاه می کند، از جمله "شما آینده").
زمانهای اجرا (بنابراین اجرای تستهای واحد ابتدا امکان چرخه تست سریعتر را در صورت شکست یک تست فراهم میکند).
وابستگی ها (بنابراین بارگیری فقط موارد مورد نیاز در یک نوع تست کارآمدتر است).
زیرساخت های مورد نیاز
زبان های برنامه نویسی (در موارد خاص).
موقعیت ها در Pipeline ادغام پیوسته (CI) یا خارج از آن.
توجه به این نکته مهم است که در اکثر زبانها و استک های فناوری، میتوانید، به عنوان مثال، تمام تستهای واحد را با زیرپوشههایی که به نام ماژولهای functional نامگذاری شدهاند، گروهبندی کنید. این راحت است، اصطکاک را هنگام ایجاد ماژولهای functional جدید کاهش میدهد، برای ساختهای خودکار آسانتر است، به هم ریختگی کمتری منجر میشود و راه دیگری برای سادهسازی تست است.
4. تست های خود را به صورت خودکار اجرا کنید
موقعیتی را تصور کنید که در آن چند تست نوشته اید، اما پس از چند هفته بعد از کشیدن مخزن، متوجه می شوید که آن تست ها دیگر موفق نمی شوند.
این یک یادآوری ناخوشایند است که تست ها کد هستند و مانند هر قطعه کد دیگری، باید حفظ شوند. بهترین زمان برای این کار درست قبل از لحظهای است که فکر میکنید کارتان را تمام کردهاید و میخواهید ببینید که آیا همه چیز همچنان همانطور که میخواهید عمل میکند یا خیر. شما تمام زمینه های مورد نیاز را دارید و می توانید کد را اصلاح کنید یا تست های شکست خورده را راحت تر از همکارتان که روی زیرسیستم دیگری کار می کند تغییر دهید. اما این لحظه فقط در ذهن شما وجود دارد، بنابراین رایج ترین راه برای اجرای تست ها به صورت خودکار پس از push کردن به شاخه dev یا پس از ایجاد یک درخواست PR است.
به این ترتیب، شاخه اصلی شما همیشه در وضعیت معتبر خواهد بود، یا حداقل، نشانه روشنی از وضعیت آن خواهید داشت. یک Pipeline تست و ساختمان خودکار، یا یک Pipeline CI، کمک می کند:
اطمینان حاصل کنید که کد قابل ساخت است.
مشکلات احتمالی "این روی دستگاه من کار می کند" را حذف کنید.
دستورالعمل های قابل اجرا را در مورد نحوه آماده سازی یک محیط توسعه ارائه دهید.
پیکربندی این Pipeline زمان میبرد، اما Pipeline میتواند طیف وسیعی از مسائل را قبل از رسیدن به کاربران یا مشتریان آشکار کند، حتی زمانی که شما تنها توسعهدهنده هستید.
پس از اجرا، CI همچنین مسائل جدید را قبل از اینکه فرصتی برای افزایش دامنه داشته باشند، آشکار می کند. به این ترتیب، ترجیح میدهم آن را درست بعد از نوشتن اولین تست تنظیم کنم. می توانید کد خود را در یک مخزن خصوصی در GitHub میزبانی کنید و GitHub Actions را راه اندازی کنید. اگر مخزن شما عمومی است، گزینه های بیشتری نسبت به GitHub Actions دارید. به عنوان مثال، طرح تست خودکار من روی AppVeyor اجرا می شود، برای پروژه ای با پایگاه داده و سه نوع تست.
من ترجیح می دهم Pipeline خود را برای پروژه های تولیدی به صورت زیر ساختار دهم:
Compilation یا transpilation
تست های واحد: آنها سریع هستند و نیازی به وابستگی ندارند
راه اندازی و مقداردهی اولیه پایگاه داده یا سایر خدمات
تست های یکپارچه سازی: آنها وابستگی هایی خارج از کد شما دارند، اما سریعتر از تست های عملکردی هستند
تست های عملکردی: وقتی سایر مراحل با موفقیت به پایان رسید، کل برنامه را اجرا کنید
هیچ تست Canary یا تست بار وجود ندارد. به دلیل ویژگی ها و الزامات آنها، آنها باید به صورت دستی شروع شوند.
5. فقط تست های ضروری را بنویسید
نوشتن تست های واحد برای همه کدها یک استراتژی رایج است، اما گاهی اوقات این باعث اتلاف وقت و انرژی می شود و به شما اطمینان نمی دهد. اگر با مفهوم "هرم تست" آشنا هستید، ممکن است فکر کنید که تمام کد شما باید با تست های واحد پوشش داده شود، و تنها یک زیر مجموعه توسط تست های سطح بالاتر دیگر پوشش داده شود.
من نیازی به نوشتن تست واحدی نمی بینم که اطمینان حاصل کند که چندین وابستگی mock شده به ترتیب دلخواه فراخوانی می شوند. انجام این کار مستلزم راهاندازی چندین ماک و تأیید همه تماسها است، اما همچنان به من اطمینان نمیدهد که ماژول کار میکند. معمولا، من فقط یک تست یکپارچه سازی می نویسم که از وابستگی های واقعی استفاده می کند و فقط نتیجه را بررسی می کند. این به من اطمینان می دهد که Pipeline در ماژول تست شده به درستی کار می کند.
به طور کلی، من تست هایی می نویسم که زندگی من را آسان تر می کند و در عین حال functionality را اجرا می کنم و بعداً از آن پشتیبانی می کنم.
برای اکثر برنامه ها، هدف گذاری برای پوشش کد 100 درصدی، کارهای خسته کننده زیادی را اضافه می کند و لذت کار با تست ها و به طور کلی برنامه نویسی را از بین می برد.
همانطور که پوشش تست مارتین فاولر بیان می کند:
پوشش تست ابزار مفیدی برای یافتن بخشهای تست نشده یک پایگاه کد است. پوشش تست به عنوان یک بیانیه عددی در مورد اینکه تست های شما چقدر خوب هستند، کاربرد چندانی ندارد.
بنابراین توصیه میکنم پس از نوشتن چند تست، آنالایزر پوشش را نصب و اجرا کنید. گزارش با خطوط کد برجسته به شما کمک می کند تا مسیرهای اجرای آن را بهتر بشناسید و مکان های کشف نشده ای را که باید پوشش داده شوند پیدا کنید. همچنین، با نگاهی به گیرندهها، ستترها و View های خود، متوجه خواهید شد که چرا پوشش 100% جالب نیست.
6. بازی لگو
هر از گاهی سوالاتی از این قبیل می بینم: «چگونه می توانم متد های خصوصی را تست کنم؟» شما نمی کنید. اگر این سوال را پرسیده اید، قبلاً مشکلی پیش آمده است. معمولاً به این معنی است که شما اصل تک مسئولیتی را نقض کرده اید و ماژول شما کاری را به درستی انجام نمی دهد.
این ماژول را اصلاح کنید و منطقی که فکر می کنید مهم است را در یک ماژول جداگانه بکشید. هیچ مشکلی با افزایش تعداد فایلها وجود ندارد، که منجر به ساختار کد لگو میشود: بسیار خوانا، قابل نگهداری، قابل تعویض و تستپذیر.
گفتن ساختار صحیح کد آسان تر از انجام آن است. در اینجا دو پیشنهاد وجود دارد:
برنامه نویسی Functional
ارزش یادگیری در مورد اصول و ایده های برنامه نویسی Functional را دارد. اکثر زبان های رایج مانند C، C++، C#، جاوا، اسمبلی، جاوا اسکریپت و پایتون شما را مجبور به نوشتن برنامه برای ماشین ها می کنند. برنامه نویسی Functional برای مغز انسان مناسب تر است.
این ممکن است در ابتدا غیر منطقی به نظر برسد، اما این را در نظر بگیرید: اگر همه کدهای خود را در یک متد قرار دهید، از یک تکه حافظه مشترک برای ذخیره مقادیر موقت و استفاده از مقدار مناسبی از دستورالعمل های پرش استفاده کنید، یک کامپیوتر خوب خواهد بود. علاوه بر این، کامپایلرها در مرحله بهینه سازی گاهی اوقات این کار را انجام می دهند. با این حال، مغز انسان به راحتی از عهده این رویکرد بر نمی آید.
برنامه نویسی Functional شما را مجبور می کند تا توابع خالص را بدون ساید افکت، با تایپ قوی، به شیوه ای رسا بنویسید. به این ترتیب استدلال در مورد یک تابع بسیار آسان تر است زیرا تنها چیزی که تولید می کند مقدار بازگشتی آن است.
توسعه تست محور
من تسلط بر TDD را توصیه می کنم. بهترین راه برای یادگیری تمرین است. String Calculator Kata یک راه عالی برای تمرین با کاتای کد است. تسلط بر کاتا زمان می برد، اما در نهایت به شما این امکان را می دهد که ایده TDD را به طور کامل جذب کنید، که به شما کمک می کند کدی با ساختار خوب ایجاد کنید که کار با آن لذت بخش است و همچنین قابل تست است.
یک نکته احتیاطی: گاهی اوقات می بینید که متخصصان TDD ادعا می کنند که TDD تنها راه درست برای برنامه ریزی است. به نظر من، این یک ابزار مفید دیگر در جعبه ابزار شماست، نه بیشتر.
گاهی اوقات، شما باید ببینید که چگونه ماژول ها و فرآیندها را در ارتباط با یکدیگر تنظیم کنید و نمی دانید از چه داده ها و امضاهایی استفاده کنید. در چنین مواردی، کد بنویسید تا زمانی که کامپایل شود، و سپس تست هایی برای عیب یابی و اشکال زدایی Functional بنویسید.
در موارد دیگر، شما ورودی و خروجی مورد نظر خود را می دانید، اما به دلیل منطق پیچیده، نمی دانید چگونه پیاده سازی را به درستی بنویسید. برای این موارد، شروع به دنبال کردن رویه TDD و ساختن کد خود به صورت گام به گام به جای صرف زمان برای فکر کردن در مورد اجرای کامل، آسان تر است.
7. تست ها را ساده و متمرکز نگه دارید
کار کردن در یک محیط کد منظم و منظم بدون حواسپرتی غیر ضروری لذت بخش است. به همین دلیل مهم است که اصول SOLID، KISS، و DRY را در تستها به کار ببریم، در صورت نیاز از refactoring استفاده کنیم.
گاهی اوقات نظراتی مانند این را می شنوم: "من از کار در یک پایگاه کد به شدت تست شده متنفرم، زیرا هر تغییری مستلزم انجام ده ها تست از من است." این یک مشکل تعمیر و نگهداری بالا است که توسط تستهایی که متمرکز نیستند و سعی میکنند بیش از حد تست کنند، ایجاد میشود. اصل "یک کار را به خوبی انجام دهید" در مورد تست ها نیز صدق می کند: "یک چیز را خوب تست کنید". هر تست باید نسبتا کوتاه باشد و فقط یک مفهوم را تست کند. «یک چیز را خوب تست کنید» به این معنی نیست که باید در هر تست به یک ادعا محدود شوید: اگر در حال تست نقشهبرداری دادههای غیر پیش پا افتاده و مهم هستید، میتوانید از دهها مورد استفاده کنید.
این تمرکز به یک تست یا نوع تست خاص محدود نمی شود. تصور کنید که با منطق پیچیدهای که با استفاده از تستهای واحد تست کردهاید، مانند نقشهبرداری دادهها از سیستم ERP به ساختار خود، سر و کار دارید و یک تست یکپارچهسازی دارید که به APIهای ERP ساختگی دسترسی پیدا میکند و نتیجه را برمیگرداند. در این صورت، مهم است که به یاد داشته باشید که تست واحد شما قبلاً چه چیزی را پوشش میدهد تا دیگر نقشهبرداری را در تستهای ادغام تست نکنید. معمولاً کافی است مطمئن شوید که نتیجه دارای فیلد شناسایی صحیح است.
با ساختار کد مانند لگو و تست های متمرکز، تغییرات در منطق تجاری نباید اذیت کننده باشد. اگر تغییرات اساسی هستند، به سادگی فایل و تست های مربوط به آن را رها کرده و با تست های جدید پیاده سازی جدیدی انجام می دهید. در صورت تغییرات جزئی، معمولاً یک تا سه تست را تغییر می دهید تا نیازهای جدید را برآورده کنید و در منطق تغییرات ایجاد کنید. تغییر تست ها خوب است.
راه های دیگر برای دستیابی به سادگی عبارتند از:
ایجاد قراردادهایی برای ساختار فایل تستی، ساختاربندی محتوای تستی (معمولاً ساختار Arrange-Act-Assert) و نامگذاری تست. سپس، مهمتر از همه، پیروی مداوم از این قوانین.
استخراج بلوکهای کد بزرگ به روشهایی مانند «آماده کردن درخواست» و ساخت توابع کمکی برای اقدامات مکرر.
استفاده از الگوی سازنده برای پیکربندی داده های تستی.
با استفاده از (در تستهای ادغام) از همان محفظه DI که در برنامه اصلی استفاده میکنید، بنابراین هر نمونهسازی به اندازه ()TestServices.Get بیاهمیت خواهد بود بدون اینکه به صورت دستی وابستگی ایجاد کنید. به این ترتیب خواندن، نگهداری و نوشتن تستهای جدید آسان خواهد بود، زیرا در حال حاضر کمککنندگان مفیدی دارید.
اگر احساس میکنید یک تست خیلی پیچیده میشود، کافی است توقف کنید و فکر کنید. یا ماژول یا تست شما نیاز به بازسازی دارد.
8. از ابزارهایی برای آسان کردن زندگی خود استفاده کنید
در حین تست با کارهای خسته کننده زیادی روبرو خواهید شد. به عنوان مثال، راه اندازی محیط های تستی یا اشیاء داده، پیکربندی خرد و ماک برای وابستگی ها و غیره. خوشبختانه، هر استک فناوری بالغ حاوی چندین ابزار است که این کارها را بسیار خسته کننده تر می کند.
پیشنهاد میکنم صد تست اول خود را بنویسید، اگر قبلاً این کار را نکردهاید، سپس برای شناسایی کارهای تکراری وقت بگذارید و در مورد ابزارهای مرتبط با تست برای استک فناوری خود بیاموزید.
برای الهام گرفتن، در اینجا چند ابزار وجود دارد که می توانید از آنها استفاده کنید:
Runner های تست به دنبال سینتکس مختصر و سهولت استفاده باشید. برای جاوا اسکریپت یا تایپ اسکریپت، با Jest پیش می روم. سعی کنید بهترین تطابق را برای وظایف و طرز فکر خود پیدا کنید زیرا ابزارها و چالش ها تکامل می یابند.
کتابخانه های ماک آمیز ممکن است مدلهای سطح پایین برای وابستگیهای کد مانند رابطها وجود داشته باشد، اما ماکتهای سطح بالاتری نیز برای APIهای وب یا پایگاههای داده وجود دارد. برای جاوا اسکریپت و تایپ اسکریپت، ماکهای سطح پایین موجود در Jest مشکلی ندارند.
کتابخانه های تولید داده این ابزارهای کمکی اشیاء داده شما را با داده های تصادفی پر می کنند. آنها زمانی مفید هستند که، برای مثال، شما فقط به چند فیلد از یک شی انتقال داده بزرگ اهمیت می دهید (اگر چنین باشد، شاید فقط می خواهید صحت نقشه برداری را تست کنید). میتوانید از آنها برای تستها و همچنین بهعنوان دادههای تصادفی برای نمایش در فرم یا پر کردن پایگاه داده خود استفاده کنید.
کتابخانه های اتوماسیون رابط کاربری اینها کاربران خودکاری برای تست های خودکار هستند: آنها می توانند برنامه شما را اجرا کنند، فرم ها را پر کنند، روی دکمه ها کلیک کنند، برچسب ها را بخوانند و غیره. برای پیمایش در تمام عناصر برنامه خود، نیازی به کلیک کردن با مختصات یا تشخیص تصویر ندارید. پلتفرمهای اصلی ابزاری برای یافتن عناصر مورد نیاز بر اساس نوع، شناسه یا دادهها دارند، بنابراین نیازی به تغییر تستهای خود با هر طراحی مجدد ندارید. آنها قوی هستند، بنابراین هنگامی که آنها را مجبور کردید برای شما و CI کار کنند (گاهی اوقات متوجه می شوید که چیزها فقط روی دستگاه شما کار می کنند)، آنها به کار خود ادامه می دهند. من از Cypress برای جاوا اسکریپت و تایپ اسکریپت استفاده میکنم و لذت می برم.
کتابخانه های Assertion اکثر تستکنندهها شامل ابزارهای Assertion هستند، اما مواردی وجود دارد که در آن یک ابزار مستقل میتواند به شما کمک کند تا با استفاده از سینتکس پاکتر و خواناتر، Assertion پیچیدهتری بنویسید.
نتیجه
تستها، بهویژه زمانی که با TDD مورد بررسی قرار میگیرند، میتوانند به راهنمایی شما و القای اعتماد به نفس کمک کنند. آنها به شما کمک می کنند تا اهداف خاصی را تعیین کنید و هر تستی که گذرانده اید نشانگر پیشرفت شما است.
رویکرد صحیح برای تست زدن می تواند شما را شادتر و بهره وری تر کند. نکته کلیدی این است که تست را به عنوان یک ابزار (یا مجموعه ابزار) ببینید که می تواند به شما در روال توسعه روزانه کمک کند، نه به عنوان یک گام سنگین برای تصحیح کد شما در آینده.
تست بخشی ضروری از برنامه نویسی است که به مهندسان نرم افزار اجازه می دهد تا نحوه کار خود را بهبود بخشند، بهترین نتایج را ارائه دهند و از زمان خود به طور بهینه استفاده کنند. شاید مهمتر از آن این باشد که تست ها می توانند به توسعه دهندگان کمک کنند از کار خود بیشتر لذت ببرند و در نتیجه روحیه و انگیزه آنها را تقویت کنند.