xUnit چارچوبی برای نوشتن و اجرای تستهای خودکار در داتنت است. هدف آن سادهکردن نوشتن تستهای قابل اعتماد و سریع است تا بتوانیم با اطمینان تغییر کنیم، بازطراحی انجام دهیم و از شکستن ناخواستهی کد جلوگیری کنیم. xUnit نسل تازهای از چارچوبهای تست خانوادهی xUnit بهشمار میرود که با تاکید بر سادگی، خوانایی و جداسازی مناسب تستها طراحی شده است.
معرفی کوتاه و تاریخچهی xUnit
خانوادهی xUnit ریشه در شیوههای آزموننویسی سبک دارد. xUnit برای داتنت با هدف بهبود تجربهی توسعهدهندگان نسبت به چارچوبهای قدیمیتر معرفی شد و بهمرور به گزینهی محبوب بسیاری از تیمها تبدیل گردید؛ بهویژه برای پروژههای .NET 6/7/8 و ASP.NET Core.
چرا xUnit؟ مزایا و فلسفهی طراحی
سادگی و سرعت در چرخهی توسعه
xUnit تلاش میکند اصطلاحات را ساده نگه دارد: تستها بهصورت متدهای عادی نوشته میشوند، مقداردهی اولیهی سبک با سازندهی کلاس انجام میشود و آزادسازی منابع در Dispose
یا Fixtureها مدیریت میگردد. نتیجه، اجرای سریع و هزینهی نگهداری پایینتر است.
مقایسهی کوتاه با MSTest و NUnit
هر سه چارچوب توانمندند، اما xUnit بهخاطر تزریق وابستگی سادهتر در سازندهی کلاس تست، جداسازی بهتر وضعیت بین تستها و دادهمحور بودن طبیعی (نظریهها) نزد بسیاری محبوب است. اگرچه تیمهایی که سالها با MSTest/NUnit کار کردهاند نیز میتوانند با حداقل اصطکاک مهاجرت کنند.
شروع سریع با xUnit
ایجاد پروژهی تست با خط فرمان
برای شروع در یک راهحل داتنت، میتوانید یک پروژهی تست بسازید:
dotnet new xunit -n MyProject.Tests
cd MyProject.Tests
dotnet test
این دستورات یک پروژهی تست میسازند و تستهای نمونه را اجرا میکنند.
ساختار پوشهها و نامگذاری تستها
پوشهی MyProject.Tests
را مطابق لایهها/ویژگیها تقسیم کنید (مثلاً Services
, Controllers
).
الگوی نامگذاری پیشنهادی کلاس تست: ClassName_Should_Behavior
.
الگوی نامگذاری متد تست: MethodName_Scenario_ExpectedResult
.
مفاهیم پایه در xUnit
تفاوت [Fact]
و [Theory]
[Fact]
: برای سناریوهایی که دادهی ورودی خاص ندارند یا فقط یک حالت را میسنجند.
[Theory]
: برای تستهای دادهمحور؛ میتوانید با ورودیهای مختلف همان منطق را بیازمایید.
تأمین دادهی تست با InlineData
، MemberData
، ClassData
public class CalculatorTests
{
[Theory]
[InlineData(2, 2, 4)]
[InlineData(5, 3, 8)]
public void Add_Should_Return_Sum(int a, int b, int expected)
{
var calc = new Calculator();
var result = calc.Add(a, b);
Assert.Equal(expected, result);
}
}
برای دادههای پیچیدهتر از MemberData
/ClassData
استفاده کنید تا خوانایی حفظ شود.
آشنایی با Assert
ها (مقایسه، استثناء، مجموعهها)
برابری: Assert.Equal
, Assert.NotEqual
مقداردهی: Assert.True
, Assert.False
, Assert.Null
استثناء: Assert.Throws<TException>
مجموعهها: Assert.Contains
, Assert.All
, Assert.Collection
سازماندهی و مدیریت وضعیت (Fixture)
الگوی IClassFixture<T>
وقتی چند تست به یک منبع مشترک نیاز دارند (مثلاً یک سرویس با پیکربندی زمانبر)، آن را در یک کلاس Fixture راهاندازی کنید و با IClassFixture<T>
به تستها تزریق نمایید. این کار زمان اجرای تستها را کاهش و تکرار کد را حذف میکند.
الگوی ICollectionFixture<T>
و چرخهی عمر
برای اشتراک منبع در چند کلاس تست مرتبط از Collection Fixture استفاده کنید. این کار به جداسازی خوب، کنترل چرخهی عمر، و جلوگیری از تداخل در موازیسازی کمک میکند.
اجرای تستها و پیکربندی
اجرای dotnet test
، فیلترگذاری، برچسبگذاری با Trait
میتوانید تستها را با فیلتر اجرا کنید:
dotnet test --filter "FullyQualifiedName~CalculatorTests"
یا با برچسبگذاری ([Trait("Category","Fast")]
) گروهبندی و فیلتر نمایید.
لاگ و خروجی با ITestOutputHelper
برای ثبت خروجی در طول اجرای تست از این واسط استفاده کنید تا عیبیابی سناریوهای پیچیده سادهتر شود.
موازیسازی و کنترل برخوردها
xUnit بهصورت پیشفرض میتواند تستها را موازی اجرا کند. اگر منبع مشترکی دارید که Thread-Safe نیست، موازیسازی را در سطح کالکشن یا پروژه غیرفعال کنید یا از Fixtureهای مناسب بهره بگیرید.
تست یکپارچهسازی در دنیای واقعی
تست سرویسهای وب با WebApplicationFactory
برای پروژههای ASP.NET Core، از کارخانهی برنامهی وب جهت بوتکردن میزبان تستی استفاده کنید و سپس با یک HttpClient
درونفرآیندی درخواستها را ارسال نمایید.
پایگاهدادهی سبک (درونحافظه/SQLite) و جداسازی دادهها
برای جلوگیری از آلودگی دادهها از پایگاهدادهی سبک و ایجاد/حذف اسکیما در هر سناریو استفاده کنید. این کار تستها را قابل تکرار و سریع نگه میدارد.
شبیهسازی وابستگیها (Mock) و تست ایزوله
برای ایزولهکردن واحد تحت تست، وابستگیها را شبیهسازی کنید تا تنها منطق همان واحد سنجیده شود. این رویکرد باعث میشود تستها پایدار، سریع و قابل اعتماد باشند.
پوشش کد (Code Coverage) با ابزارهای متداول
پوشش کد نشان میدهد چه بخشهایی از برنامه زیر بار تست قرار گرفتهاند. میتوانید با ابزارهای پوشش در کنار dotnet test
گزارشهای متنی/HTML تولید کنید و روند افزایش پوشش را در CI دنبال نمایید.
ادغام در CI/CD (نمونهی سادهی گردشکار)
— اجرای خودکار تستها در هر Pull Request —
چکاوِت کد
نصب SDK
بازیابی وابستگیها
بیلد
اجرای تستها
انتشار گزارشها
شکست تستها باید ادغام را متوقف کند تا کیفیت حفظ شود.
بهترین شیوهها و الگوهای نامگذاری
اصل AAA (Arrange, Act, Assert)
کد تست را به سه بخش واضح تقسیم کنید: چیدن دادهها و وابستگیها، اجرای عمل، و راستیآزمایی خروجی. این چینش خوانایی و نگهداری را بهبود میدهد.
اصل FIRST برای تستهای خوب
سریع، مستقل، قابل تکرار، خودتوصیف، و بهموقع (درستهنگام). هرگاه تستی یکی از این ویژگیها را از دست بدهد، دیر یا زود دردسرساز میشود.
خطاهای رایج و راهحلها
بهاشتراکگذاری وضعیت بین تستها: از Fixture درست استفاده کنید یا موازیسازی را مدیریت کنید.
تستهای لرزان (Flaky): وابستگی به زمان/شبکه را حذف یا شبیهسازی کنید.
نامگذاری مبهم: اسم تست باید انتظار شما را بهروشنی بیان کند.
نکات پیشرفته
مرتبسازی و اولویت اجرای تستها
در موارد خاص میتوان ترتیب اجرای تستها را با مکانیزمهای سفارشی مشخص کرد؛ با این حال توصیه میشود تستها مستقل باشند تا به ترتیب وابسته نمانند.
نظریههای دادهمحور پیچیده
برای ورودیهای بزرگ و پیچیده از MemberData
/ClassData
استفاده کنید تا هم خوانایی بالا بماند و هم موارد آزمون گسترده پوشش داده شود.
مهاجرت از NUnit/MSTest به xUnit
گامهای پیشنهادی:
برداشت موجودی تستها
نگاشت ویژگیهای معادل ([Test]
→ [Fact]
و …)
جایگزینی Assertها
انتقال Setup/TearDown به سازنده/Dispose یا Fixture
اجرای تدریجی و موازی چارچوبها تا تکمیل مهاجرت
جمعبندی
xUnit راهی ساده و مؤثر برای تضمین کیفیت در پروژههای داتنت است. با تکیه بر مفهوم تستهای مستقل، دادهمحور و سریع، میتوانید با اطمینان بازطراحی کنید و از نفوذ باگها جلوگیری نمایید. استفاده از Fixtureها، موازیسازی کنترلشده، پوشش کد، و ادغام در CI/CD، تصویری کامل از سلامت کد به شما میدهد. اگر تازه میخواهید چارچوب تست اصلی تیمتان را انتخاب کنید، xUnit ترکیبی خوشتعادل از سادگی، قدرت و جامعهی کاربری بالغ را ارائه میکند.
پرسشهای متداول (FAQ)
۱) آیا xUnit برای پروژههای کوچک هم مناسب است؟
بله؛ راهاندازی سریع و سادگی آن برای پروژههای کوچک ایدهآل است و با رشد پروژه هم کم نمیآورد.
۲) فرق [Fact]
و [Theory]
در عمل چیست؟Fact
یک سناریوی ثابت را میسنجد؛ Theory
همان سناریو را با ورودیهای مختلف امتحان میکند تا پوشش بیشتری بدهد.
۳) آیا میتوان تستهای یکپارچهسازی را هم با xUnit نوشت؟
بله؛ با راهاندازی میزبان تستی وب و پایگاهدادهی سبک، سناریوهای سرتاسری را نیز میتوان سنجید.
۴) چه زمانی موازیسازی را غیرفعال کنیم؟
وقتی منبع مشترک Thread-Safe نیست یا ترتیب اجرای تستها اهمیت دارد؛ در غیر این صورت موازیسازی زمان اجرا را کاهش میدهد.
۵) بهترین الگوی نامگذاری تست چیست؟
الگوهای توصیفی مانند Method_Scenario_ExpectedResult
خوانایی و هدف تست را واضح میکند (مثلاً: Add_NegativeNumbers_ReturnsCorrectSum
).
برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربریتان شوید