آموزش تست نرم افزار Test Readability

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

نوشتن تست خوانا (Readable)

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

  1. نامگذاری Unit Test ها
  2. نامگذاری متغیرها
  3. ایجاد پیام های مناسب برای Assert ها
  4. جداسازی Assert ها از فراخوانی متد

حال هر کدام را جداگانه بررسی می کنیم.

نامگذاری Unit Test ها

استانداردهای نامگذاری از اهمیت بسیار زیادی برخوردار هستند زیرا قوانین و قالب های راحتی را در اختیار ما قرار می دهند که با استفاده از آن مشخص می شود در تست چه چیزهایی توضیح داده شده است. نامگذاری تست سه قسمت دارد:

نام متد تحت تست (یعنی آن متدی که تست برای آن نوشته می شود): نوشتن نام متد تحت تست مهم است، برای این که شما به راحتی می توانید ببینید که عملکردی که تست برای آن نوشته شده است، کجاست. اگر نام متد تحت تست را به عنوان اولین قسمت از نام تست داشته باشیم، خیلی راحت می توانیم به سراغ آن متد برویم.

سناریو ای که قرار است تست شود: "وقتی متد X با مقدار Null فراخوانی می شود، سپس باید Y اتفاق بیفتد". در بخش دوم نام تست باید شرایط آن سناریو را بنویسیم (که در این سناریو، شرایط اجرا شدن، با مقدار Null است).

رفتار مورد انتظار پس از اجرای این سناریو: در این قسمت از نام تست مشخص می شود که متد باید چه کاری را انجام دهد یا چه مقداری را Return کند یا براساس شرایط سناریو چه رفتاری را از خود نشان دهد. "وقتی متد X با مقدار Null فراخوانی می شود، سپس باید Y اتفاق بیفتد". در این سناریو رفتار مورد انتظار ما از متد X اتفاق افتادن Y است. حال Y ممکن است هر چیزی باشد. حذف هریک از این سه قسمت گفته شده ممکن است خواننده تست را متعجب کند که چه اتفاقی درحال رخ دادن است و این تست چه کاری را انجام می دهد، پس او (خواننده تست) مجبور است به کد تست مراجعه کند تا تست را کامل متوجه شود. هدف اصلی ما این است که توسعه دهنده بعدی را از شَر خواندن کد تست برای فهمیدن این که تست دارد چه کار می کند، برهانیم. یکی از راه های رایج برای نوشتن این سه قسمت در کنار هم این است که با استفاده از "_" آنها را از هم جدا کنیم. مانند:

()MethodUnderTest_Scenario_Behavior

به مثال زیر توجه کنید:

آموزش تست نرم افزار

این متد درحال تست کردن متد AnalyzeFile است، که به آن یک فایل با 3 خط و یک File-Reading Provder می دهد و انتظار دارد که از این Provider برای خواندن فایل استفاده کند. اگر توسعه دهندگان این قاعده را رعایت کنند، توسعه دهندگان دیگر خیلی راحت می توانند تست ها را فقط با خواندن نام تست ها و بدون خواندن کد تست (یعنی آنچه که در متد تست نوشته می شود)، بفهمند.

نامگذاری متغیرها

نامگذاری متغیرها در تست ها همانقدر(شاید هم بیشتر) اهمیت دارد که نامگذاری متغیرها در کد برنامه مهم است. جدای از عمکرد تست، تست ها به عنوان Documentaion هم برای ما کاربرد دارند. نامگذاری خوب برای متغیر ها باعث می شود تا مطمئن شویم افرادی که تست های ما را می خوانند در سریعترین زمان ممکن مقصود ما را از تست می فهمند. تصویر زیر مثالی خوبی است از تستی که هم خیلی بد نامگذاری شده و هم بد نوشته شده است. ما این مدل تست ها را "ناخوانا یا Unreadable" می دانیم زیرا نمی توانیم متوجه شویم که این تست چه کاری انجام می دهد.

آموزش تست نرم افزار

در این مثال، Assert از عدد جادویی 100- استفاده می کند (عدد جادویی یعنی عددی که در Assert آمده و در نتیجه تست مهم است و توسعه دهنده یا خواننده تست باید بداند که چه معنی ای دارد اما در تست مشخص نشده است که این عدد چه معنی ای دارد). چون نام این تست توضیح مناسبی برای عددِ نتیجه تست ندارد، فقط می توانیم فرض کنیم که معنی عددِ نتیجه چیست و اطمینان نداریم.

آیا 100- نوعی Exception است؟ آیا یک مقدار بازگشت داده شده معتبر است؟ در این نقطه ما می توانیم انتخاب کنیم:

  • می توانیم طراحی کد برنامه را تغییر دهیم که به جای بازگشت دادن عدد 100- ، یک Exception برگرداند.
  • می توانیم عدد 100- را به یک مقدار constant تبدیل کنیم و به آن یک نام مناسب دهیم. مانند مثال زیر:
آموزش تست نرم افزار

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

ایجاد پیام های مناسب برای Assert ها

نوشتن پیام مناسب برای Assert ها مانند نوشتن یک پیام مناسب برای Exception ها است. اگر متوجه آن نشویم به راحتی به اشتباه می افتیم و برای کسانی که این پیام را می خوانند موضوع خیلی متفاوت است زیرا آن ها کاملا به اشتباه می افتند. برای نوشتن پیام مناسب برای Assert ها چند نکته را به خاطر داشته باشید:

  • آن پیامی را که فریم ورک تست برای شما روی کنسول بازمی گرداند را تکرار نکنید.
  • آنچه را که نام تست توضیح داده است را تکرار نکنید.
  • اگر پیام مناسبی به نظرتان نمی رسد، چیزی ننویسید.
  • آنچه را که باید اتفاق می افتاد یا آنچه که Fail شده است را بنویسید، و اگر امکان داشت به اینکه چه زمانی باید اتفاق می افتاد اشاره کنید.

تصویر زیر مثال بدی از از پیام Assert را نشان می دهد:

آموزش تست نرم افزار

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

Calling GetLineCount() for an non-existing file should have returned a COULD_NOT_READ_FILE

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

جداسازی Assert ها از فراخوانی متد

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

آموزش تست نرم افزار

و این تصویر حالت غلط:

آموزش تست نرم افزار

می توانید تفاوت بین این دو مثال را مشاهده کنید. مثال دوم بسیار بد است و از خوانایی پایین برخوردار است.

آموزش تست نرم افزار دوره آموزش تست نرم افزار Test Readability آموزش test driven development