بهترین الگوهای آزمایش واحد در NET Core. و NET Standard. قسمت اول

نوشتن تست های واحد مزایای بی شماری دارد. آنها به مستندسازی کمک کرده و طراحی خوب را تسهیل می بخشند اما تست های واحدی که شکننده بوده و از خوانایی کمی برخوردار هستند می توانند به پایه کد شما آسیب برسانند.
در این مقاله بهترین روشهای طراحی واحد آزمون برای پروژه های NET Core. و NET. شرح داده شده است.
شما در اینجا بهترین روش ها را برای نوشتن آزمون های واحد خواهید آموخت تا تست های خود را مقاوم و قابل درک نگه دارید.
چرا آزمون واحد!؟
تست های عملکردی هزینه بر هستند و یک یا چند شخص باید رفتارهای مورد نظر نرم افزار را تست کرده و از صحت انجام انها مطمئن شود. این مراحل ممکن است همیشه برای تست کننده شناخته شده نباشد، به این معنی که آنها برای انجام آزمون مجبورند با کسی که در این مورد دانش بیشتری دارد تماس بگیرند. آزمایش خود می تواند برای تغییرات بی اهمیت ثانیه ها یا برای تغییرات بزرگتر چند دقیقه طول بکشد. همچنین ، این فرایند باید برای هر تغییری که در سیستم ایجاد می کنید تکرار شود.
از طرف دیگر ، آزمون واحد یک میلی ثانیه طول می کشد و می توان آن را با فشار دادن یک دکمه اجرا کرد و لزوماً نیازی به دانش کلی سیستم نمی باشد. قبولی یا عدم موفقیت در آزمون ، به اجرا کننده آزمون بستگی داشته و به نیروی انسانی بستگی ندارد.
محافظت در برابر رگرسیون
نقص رگرسیون نقصی است که با ایجاد تغییر در برنامه ایجاد می شود. معمولا آزمایش کنندگان علاوه بر اینکه متدهای جدید خود را آزمایش می کنند ، متدهای قبلی را نیز آزمایش می کنند تا مطمئن شوند که همچنان مطابق انتظار عمل می کنند.
با آزمایش واحد ، می توانید کل مجموعه آزمایشات خود را پس از هر ساخت یا حتی پس از تغییر یک خط کد ، دوباره انجام دهید.
مستندات قابل اجرا
ممکن است همیشه مشخص نباشد که یک متد خاص چه کاری انجام می دهد یا چگونه با توجه به ورودی خاصی رفتار می کند. ممکن است از خودتان بپرسید: اگر من یک رشته خالی را به عنوان ورودی آن ارسال کنم ، این متد چگونه رفتار می کند؟ یا در صورتی که ورودی آن به صورت NULL باشد چه اتفاقی می افتد؟
وقتی مجموعه آزمایشی واحدی با صورت کامل دارید ، هر آزمون باید بتواند خروجی مورد انتظار برای ورودی مشخص را به وضوح توضیح دهد. علاوه بر این ، باید بتواند عملکرد واقعی متد را مشخص کند.
کدهای با اتصال سست
نوشتن آزمون واحد برای کدهای با اتصال قوی کار دشواریست .
باید دقت داشته باشید که بدون نوشتن آزمون واحد اتصال قوی کد آشکار نمی شود ، در واقع نوشتن آزمون واحد به صورت طبیعی کمک می کند که اتصال قوی کد از بین برود چون آزمایش درغیراین صورت دشوار می شود.
ویژگی های یک تست واحد خوب
تست های خوب پنج قانون را دنبال می کنند که عبارت F.I.R.S.T را تشکیل داده اند
Fast
داشتن پروژه ای که هزاران آزمایش واحد دارند غیر معمول نیست. به همین دلیل تست های واحد باید در زمان بسیار کمی (در حد میلی ثانیه) اجرا شوند.
Isolated
تست های واحد مستقل هستند ، می توانند به صورت جداگانه اجرا شوند و به عوامل بیرونی مانند سیستم فایل یا پایگاه داده وابستگی ندارند.
Repeatable
اجرای یک تست واحد باید با نتایج آن مطابقت داشته باشد ، یعنی اگر در این بین چیزی را تغییر ندهید ، همیشه همان نتیجه را برمی گرداند.
Self-Checking
آزمایش واحد باید بتواند بدون هیچ گونه تعامل انسانی ، قبولی یا شکست آزمایش را به طور خودکار تشخیص دهد.
Timely
نوشتن یک آزمون واحد نباید در مقایسه با کدی که در حال آزمایش است ، به طور نامتناسب طولانی مدت باشد. اگر متوجه شدید که کد تست در مقایسه با کد زمان زیادی را صرف می کند ، طرحی را آزمایش کنید که تست پذیرتر باشد
درصد پوشش برنامه توسط آزمونهای واحد
بالا بودن درصد پوشش برنامه توسط آزمونهای واحد با بالا بودن کیفیت برنامه مرتبط است اما این اندازه گیری به خودی خود نمی تواند کیفیت کد را تعیین کند
و اگر به آن بیش از حد بلند پروازانه نگاه کنیم در پروسه تولید محصول تاثیر منفی خواهد گذاشت
برای روشن شدن موضوع به این مثال توجه کنید، تصور کنید که هدف شما این است که آزمون های واحد، %95 از کد را پوشش دهند، اما آزمون های واحد %90 از کد را پوشش داده اند و چون شما به پوشش %95 از کد توسط آزمون واحد به عنوان یک هدف نگاه میکنید مجبور هستید که %5 دیگر را نیز به هر نحوی پوشش دهید که می تواند اقدامی عظیم باشد و سرعت تولید محصول را کاهش دهد.
درصد بالای پوشش کد توسط آزمون واحد نشانگر موفقیت و به معنی کیفیت بالای کد نیست بلکه فقط مقدار کدی که توسط آزمایشات واحد پوشش داده می شود را نشان می دهد.
مفهوم Mocks و Stubs و Fakes
اصطلاح Mock اغلب هنگام صحبت در مورد آزمون واحد به کار می رود.
رایج ترین انواع Mock در نوشتن آزمون های واحد عبارتند از :
- Fakes: یک Fake شیای است که یک مکانیزم داخلی دارد که نتایج قابل پیشبینی برمیگرداند، اما منطق کاری واقعی را پیادهسازی نکرده است.
- Stubs: یک Stub شیای است که یک نتیجه مشخص را بر اساس یک سری ورودی مشخص برمیگرداند. اگر من به stub بگویم که هر وقت شخصی با شناسه 42 را خواستم عبارت John Doe را برگردان، stub همین کار را خواهد کرد. با این حال اگر من از stub بخواهم که شخصی با شناسه 41 را برگرداند، نمیداند چه کار باید بکند. بر حسب اینکه از کدام mocking framework استفاده کنم، stub یا exception ایجاد میکند یا یک شی null برمیگرداند. stub میتواند بعضی اطلاعات مربوط به نحوه فراخوانی مثل تعداد فراخوانی یا اینکه با چه دادههایی فراخوانی شده است را به یاد داشته باشد.
- Mocks: یک Mock یک نسخه پیچیدهتر از stub است. همچنان مانند stub مقادیر را برمیگرداند، اما همچنین میتواند طوری برنامهریزی شود که باید چند بار به چه ترتیب یا به چه دادههایی فراخوانی شود.
Best Practise
نام گذاری آزمون های واحد
نام آزمون شما باید شامل سه قسمت باشد:
-
- نام متدی که تست می شود
- سناریویی که تحت آن تست انجام می شود
- رفتار مورد انتظار هنگام فراخوانی سناریو
یک سناریوی تست(Test Scenario) به عنوان هر Functionality که میتواند مورد تست قرار گیرد تعریف شده است. به عنوان یک تستر، ممکن است شما خود را جای یک کاربر نهایی قرار دهید و سناریوهایی از دنیای واقعی و نیز Use Caseهایی از اپلیکیشن تحت تست را بیابد.
چرا؟
استانداردهای نامگذاری از این جهت مهم هستند که صریحاً هدف آزمون را بیان می کنند.
آزمایشات علاوه بر اطمینان از عملکرد کد شما ، همچنین مستنداتی را نیز ارائه می دهند. به طوریکه فقط با مشاهده مجموعه آزمون های واحد ، می توانید رفتار کدهای خود را بدون حتی مشاهده خود کد ، استنباط کنید. علاوه بر این ، هنگامی که آزمایشات ناموفق است ، می توانید دقیقاً ببینید که کدام سناریوها انتظارات شما را برآورده نمی کنند.
نمونه نام گذاری بد
[Fact]
public void Test_Single()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Assert.Equal(0, actual);
}
نام گذاری بهتر
[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Assert.Equal(0, actual);
}
تنظیم آزمایشات
Arrange, Act, Assert(AAA) یک الگوی معمول در آزمون واحد است.
Object : Arrange های خود را مرتب کنید ، آنها را در صورت لزوم ایجاد و تنظیم کنید.
Act : بر روی یک شی عمل کنید. مثلا تابع Save از Object ایجاد شده را فراخوانی کنید.
Assert : صحت عملکرد تست را بررسی کنید .برای مثال مقدار برگشتی کدی که در قسمت Act نوشته شده با نتیجه درست را بررسی کنید.
چرا؟
با این روش قسمت های مختلف تست به سادگی از یکدیگر جدا می شوند.
خوانایی یکی از مهمترین موارد هنگام نوشتن یک آزمون است. جدا کردن هر یک از این اقدامات در آزمون ، وابستگی های لازم برای فراخوانی کد ، نحوه فراخوانی کد و آنچه را که می خواهید صحت سنجی کنید را به وضوح برجسته می کند. هرچند ممکن است ترکیب برخی از مراحل موجب کاهش اندازه آزمون شما شود ، اما هدف اصلی این است که آزمون هر چه بیشتر خوانا باشد.
نمونه بد
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Assert
Assert.Equal(0, stringCalculator.Add(""));
}
نمونه بهتر
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add("");
// Assert
Assert.Equal(0, actual);
}
دیدگاهتان را بنویسید