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

این بدان معنی است که به جای توصیف آن شیء، مشغول ایجاد پیاده‌سازی‌هایی از یک اینترفیس هستید و کامپایلر Go آن‌ها را محاسبه می‌کند. اگر شیء شما شامل همه متدهای تعریف‌شده از سوی اینترفیس باشد، Go درک می‌کند که شیء مورد نظر آن اینترفیس را پیاده‌سازی کرده و می‌تواند هر جا که اینترفیس استفاده شده به جای آن مورد استفاده قرار گیرد.

اینک سؤال این است که این وضعیت چرا مفید است و چگونه می‌توان از اینترفیس‌ها برای نوشتن کد قابل تست استفاده کرد.

مثال: استفاده از کتابخانه‌های اکسترنال

یک سناریوی رایج برای نشان دادن کاربرد اینترفیس‌ها، تست کردن کدی است که از کتابخانه کلاینت اکسترنال برای انجام کاری استفاده می‌کند. در این مثال که ما بررسی می‌کنیم، داده‌ها از یک API خیالی وب دریافت می‌شوند.

فرض کنید یک پکیج به نام external داریم. این پکیج کتابخانه‌ای است که برای تعامل با وب‌سرویس اکسترنال نوشته شده است. این پکیج می‌تواند یک شیء Client را با متدهایی برای تعامل با API اکسپورت کند. تصور کنید یک متد GetData در این مثال وجود دارد که تنها می‌تواند داده‌ها را بازگشت دهد، اما می‌تواند یک درخواست وب نیز ایجاد کند و از نظر تئوریک در صورت وجود یک پیاده‌سازی واقعی خطایی بازگشت دهد.

در کد فوق پکیج foo را داریم. این پکیج کدی است که می‌نویسیم و امیدواریم از Client برای دریافت داده‌ها از API اکسترنال استفاده کنیم و سپس کارهایی روی آن انجام دهیم. ما در این پیاده‌سازی دو حالت ممکن برای خطا داریم، یکی در زمانی که است GetData خطایی بازگشت دهد که دلیل احتمالی آن شکست خوردن درخواست وب است و دیگری این است که داده‌های بازگشت یافته آن چیزی نباشند که ما انتظار داریم و از این رو نتوانیم آن‌ها را پردازش کنیم. هر دو مسیر منجر به این می‌شود که تابع Controller خطایی بازگشت دهد:

اکنون به بررسی چگونگی تست تابع Controller می‌پردازیم. دو نوع تست مقدماتی می‌توانیم داشته باشیم که یکی موفقیت تابع را بررسی می‌کند و دیگری دو حالت شکست را مورد توجه قرار می‌دهد. مشکل اینجا است که نمی‌توانیم رفتار API اکسترنال را تحت تأثیر قرار دهیم و از این رو نمی‌توانیم GetData را ملزم به موفقیت یا شکست بکنیم.

تست فوق TestController_Success موفق می‌شود، اما TestController_Failure شکست می‌خورد، زیرا ما برای تست حالت‌های شکست ناموفق بوده‌ایم. این حالت تأیید می‌شود و در گزارش coverage به تصویر کشیده است.

نه تنها حالت‌های شکست ما پوشش نیافته‌اند بلکه تست‌های Unit نیز هم اینک غیرقطعی هستند، چون شکست یا موفقیت API اکسترنال چنین حالتی دارد. ما به روشی نیاز داریم که رفتار GetData را در کد خود تثبیت کنیم و بدین ترتیب بتوانیم خروجی آن را در طی تست‌های unit دستکاری کنیم و این همان کاری است که اینترفیس‌ها به خوبی انجام می‌دهند.

استفاده از اینترفیس‌ها برای فعال‌سازی Stubbing

اگر بتوانیم اینترفیسی تعریف کنیم که شیء Client کتابخانه اکسترنال را تأمین کند، می‌توانیم از اینترفیس در کد خود به جای آن استفاده کنیم. بدین ترتیب می‌توانیم یک شیء کلاینت را در زمان تست کردن جعل کنیم و در زمان عملیات واقعی از نسخه اصلی استفاده کنیم.

در زبان‌های دیگر این کار نیازمند آن است که کد کتابخانه اکسترنال را دستکاری کنیم تا صراحتاً بیان کنیم که Client اینترفیس جدید ما را پیاده‌سازی می‌کند، اما از آنجا که اینترفیس‌های Go صراحتاً تأمین می‌شوند، کامپایلر این نکته را خواهد دانست.

بنابراین اینترفیس IExternalClient را تعریف می‌کنیم که متدهایی که در Controller خود مورد استفاده قرار می‌دهیم را تعیین کرده است. تابع کنترلر را طوری تغییر می‌دهیم که یک نوع اینترفیس IExternalClient به عنوان پارامتر بگیرد. بقیه تابع کنترلر همانند قبل عمل می‌کند یعنی متد GetData را از اینترفیس به جای پیاده‌سازی Client اکسترنال خاص فراخوانی می‌کند.

اکنون نگاهی به میزان سادگی تست کردن متد Controller می‌پردازیم. ما می‌توانیم اینترفیس IExternalClient را صرفاً با پیاده‌سازی متد GetData روی شیء MockClient پیاده‌سازی کنیم، اما در مورد پیاده‌سازی MockClient باید آن چه را می‌خواهیم هم بازگشت دهیم. سپس پیاده‌سازی خود از IExternalClient را به Controller در تست‌های خود می‌دهیم. می‌توان از MockClient برای بازگشت مقادیر مختلف برای نتیجه GetData استفاده کنیم و از FailingClient برای واداشتن به بازگشت خطا بهره بگیریم.

اینک به سادگی می‌توانیم همه شاخه‌های تابع Controller را مدیریت کنیم و به‌روزرسانی گزارش پوشش نیز این نکته را نشان می‌دهد:

چنان که می‌بینید، نوشتن و استفاده از اینترفیس‌ها می‌تواند انعطاف‌پذیری و تست‌پذیری کد را افزایش دهد. از این رو می‌تواند یک راه‌حل آسان برای تثبیت وابستگی‌های اکسترنال باشد.

کتابخانه‌های استاندارد Go استفاده گسترده‌ای از اینترفیس‌ها دارند. مثال‌های مناسبی را می‌توانید در پکیج‌هایی مانند io (+) و net/http (+) ببینید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

میثم لطفی (+)

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

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *