۱۴ - ۲توضیح اجمالی از تست برای تازه‌کارها

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

عموماً با تست‌ها یه نتیجه‌ی مطلوب رو مشخص می‌کنیم، و بعد مطمئن میشیم که جوابِ عملیات ِ مورد نظرمون با انتظارمون یکسان هست یا نه. در واقع کمک می‌کنن مطمئن شیم کُدمون طبقِ انتظار عمل می‌کنه.

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

تست شناسه‌ای نسخه‌ی جدیدتری از تست واحدی به حساب میاد. این تست هم مثلِ تست واحدی، توابعِ مشخص رو مستقلاً بررسی می‌کنه، و باید جواب مورد نظر به ازای یه ورودی معین رو بهش بدیم. وقتی تست رو اجرا می‌کنیم، کامپیوتر چک می‌کنه که آیا جوابِ مورد انتظار با جوابِ واقعی برابر میشن یا نه. بعضی‌ها، تست شناسه‌ای رو به تست واحدی ترجیح میدن، دلیل‌ش هم اینه که تست شناسه‌ای معمولاً خواناییِ بیشتری برای آدمها داره. بخصوص برای کسانی که برنامه‌نویس نیستن و باید تست‌ها رو بخونن، چنین چیزی می‌تونه خیلی کارآمد باشه – جوابِ تست‌ها رو به زبان انگلیسی می‌خونن و شاید خودشون هم تست بنویسن.

هسکل، هم برای تست واحدی و هم برای تست شناسه‌ای، کتابخونه‌هایی داره. ما تو این فصل تست شناسه‌ای رو با ‏‎hspec‎‏ یاد می‌گیریم، اما ‏‎HUnit‎‏ هم برای تست واحدی وجود داره. یکی از محدودیت‌های تست‌های واحدی و شناسه‌ای اینه که واحدهای ریز ِ کُد رو مستقلاً تست می‌کنن، در نتیجه کار کردن همه‌ی اونها کنار هم رو چک نمی‌کنن.

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

این مقادیرِ تصادفی توسطِ توابعِ استانداردِ موجود در کتابخونه ِ ‏‎QuickCheck‎‏ (که برای تست مشخصه‌ای استفاده می‌کنیم) ایجاد میشن. تشخیصِ اینکه این توابع چه داده‌هایی رو ایجاد کنن، با تکیه بر تایپ سیستم انجام میشه. با تنظیماتِ پیش‌فرض ۱۰۰ ورودی ایجاد میشن و ۱۰۰ نتیجه میدن. اگه هر کدوم از اینها شکست بخورن، میدونین که برنامه‌تون مشخصه ِ تعیین شده رو نداره. البته با پاس شدن (م. شکست نخوردن) ِ این صدتا تست، باز هم تضمین نمیشه که برنامه هیچ وقت شکست نمی‌خوره، چون اون داده‌ها به صورتِ تصادفی ایجاد شدن – ممکنه یه حالت مرزی ِ عجیب غریبی باشه که باعث شکست نرم‌افزار بشه. در کل ‏‎QuickCheck‎‏ هوشمندانه نوشته شده تا خیلی دقیق عمل کنه و اکثر حالت‌های مرزی رو چک می‌کنه (برای مثال، لیست‌های خالی، و هرجا ممکن بود ‏‎maxBound‎‏ و ‏‎minBound‎‏ ِ تایپ مورد نظر). میشه تعیین کرد که تست‌های بیشتری رو اجرا کنه.

بهترین کاربردِ تست مشخصه‌ای، تضمینِ حداقل نیازهای لازم برای ارضای قوانین‌ه، مثل قوانین مونَدها یا شرکت‌پذیری. با این حال این تست برای همه‌ی برنامه‌ها مناسب نیست، مثل وقت‌هایی که هیچ مشخصه‌ای از برنامه رو نمیشه با مقادیر واقعی بیان کرد.