021-88881776

آموزش تست و اشکال‌زدایی در .NET

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

در این مقاله، ابتدا با مفاهیم پایه تست و اشکال‌زدایی در .NET آشنا می‌شویم. سپس به سراغ نوشتن تست‌های واحد با xUnit می‌رویم، ابزارهای اشکال‌زدایی در Visual Studio را بررسی می‌کنیم و در نهایت تست خودکار و پیوسته را توضیح می‌دهیم. هدف این است که با خواندن این مقاله، بتوانید برنامه‌های خود را با اطمینان بیشتری توسعه دهید.

تست و اشکال‌زدایی در .NET

تست و اشکال‌زدایی در .NET دو بخش حیاتی در فرآیند توسعه نرم‌افزار هستند که کیفیت و پایداری برنامه‌های شما را تضمین می‌کنند. تست به شما کمک می‌کند مطمئن شوید که کدتان همان‌طور که انتظار دارید عمل می‌کند، در حالی که اشکال‌زدایی فرآیندی است برای ردیابی و رفع خطاهایی که ممکن است در طول توسعه یا اجرا رخ دهند. این دو مهارت نه‌تنها به شما کمک می‌کنند تا برنامه‌های بهتری بسازید، بلکه باعث می‌شوند در زمان و انرژی صرفه‌جویی کنید و از سردرگمی‌های احتمالی در پروژه‌های بزرگ جلوگیری کنید.

تست و اشکال‌زدایی چیست؟

برای درک بهتر، بیایید این دو مفهوم را جداگانه توضیح دهیم:

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

چرا تست و اشکال‌زدایی در .NET مهم است؟

فرض کنید یک برنامه ساده برای مدیریت لیست خرید نوشته‌اید. اگر تست نکنید، ممکن است وقتی کاربر یک آیتم را حذف می‌کند، به‌جای حذف، دو برابر شود! یا اگر اشکال‌زدایی بلد نباشید، ساعت‌ها دنبال این بگردید که چرا این اتفاق افتاده است. در اینجا چند دلیل کلیدی برای اهمیت تست و اشکال‌زدایی در .NET آورده شده است:

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

انواع تست در .NET

برای اینکه تست و اشکال‌زدایی در .NET را به‌خوبی انجام دهید، باید با انواع تست آشنا شوید. هر نوع تست هدف خاص خودش را دارد:

تست واحد (Unit Testing):

چیست؟ تست یک بخش کوچک و مستقل از کد، مثل یک متد یا تابع.
مثال: فرض کنید یک متد دارید که دو عدد را جمع می‌کند. تست واحد بررسی می‌کند که آیا 2 + 3 واقعاً 5 می‌شود یا نه.
چرا مهم است؟ اگر هر قطعه کوچک درست کار کند، احتمال خرابی کل سیستم کمتر می‌شود.

تست یکپارچگی (Integration Testing):

چیست؟ تست همکاری بین چند بخش از برنامه.
مثال: اگر متد جمع شما قرار است نتیجه را در دیتابیس ذخیره کند، تست یکپارچگی بررسی می‌کند که آیا این ارتباط درست برقرار می‌شود یا نه.
چرا مهم است؟ گاهی بخش‌ها به‌تنهایی درست کار می‌کنند، اما با هم مشکل دارند.

تست سیستم (System Testing):

چیست؟ تست کل برنامه به‌صورت یکپارچه، مثل وقتی که همه قطعات یک پازل کنار هم قرار می‌گیرند.
مثال: آیا برنامه مدیریت لیست خرید شما از ابتدا تا انتها (اضافه کردن، ویرایش، حذف) به‌درستی کار می‌کند؟
چرا مهم است؟ این تست نشان می‌دهد که محصول نهایی آماده استفاده است یا نه.

تست پذیرش (Acceptance Testing):

چیست؟ بررسی اینکه آیا برنامه نیازهای کاربر یا مشتری را برآورده می‌کند یا نه.
مثال: آیا مشتری از سرعت و رابط کاربری برنامه راضی است؟

مراحل اشکال‌زدایی در عمل

اشکال‌زدایی مثل حل یک معما است. بیایید مراحل آن را با جزئیات بیشتری بررسی کنیم:

شناسایی مشکل:

اولین قدم این است که بفهمید چه چیزی درست نیست. مثلاً آیا برنامه کرش می‌کند؟ آیا خروجی اشتباه است؟ یا اصلاً چیزی که باید اتفاق بیفتد، نمی‌افتد؟
مثال: فرض کنید در برنامه ماشین‌حساب، وقتی 5 ÷ 0 را وارد می‌کنید، برنامه متوقف می‌شود.

پیدا کردن محل خطا:

حالا باید بفهمید خطا کجای کد است. آیا در منطق برنامه است؟ در ورودی‌ها؟ یا شاید در یک حلقه یا شرط؟
مثال: با بررسی کد، می‌بینید که متد تقسیم هیچ کنترلی برای صفر بودن مخرج ندارد.

رفع مشکل و تست دوباره:

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

public int Divide(int a, int b)
{
    if (b == 0) throw new ArgumentException("نمی‌توان بر صفر تقسیم کرد!");
    return a / b;
}

سپس تست می‌کنید که آیا این تغییر کار می‌کند یا نه.

مثال عملی از تست و اشکال‌زدایی

بیایید یک سناریوی واقعی‌تر را بررسی کنیم. فرض کنید یک متد برای محاسبه تخفیف در یک فروشگاه آنلاین دارید:

public class DiscountCalculator
{
    public decimal CalculateDiscount(decimal price, int discountPercentage)
    {
        return price - (price * discountPercentage / 100);
    }
}

تست: می‌خواهید مطمئن شوید که برای price = 1000 و discountPercentage = 20، نتیجه 800 می‌شود.
مشکل احتمالی: اگر کاربر discountPercentage = 150 وارد کند، نتیجه منفی می‌شود (-500) که منطقی نیست!
اشکال‌زدایی: با بررسی کد، می‌فهمید که باید یک شرط برای محدود کردن درصد تخفیف اضافه کنید:

public decimal CalculateDiscount(decimal price, int discountPercentage)
{
    if (discountPercentage < 0 || discountPercentage > 100)
        throw new ArgumentException("درصد تخفیف باید بین 0 و 100 باشد!");
    return price - (price * discountPercentage / 100);
}

ابزارهای مورد استفاده در .NET

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

فریم‌ورک‌های تست: مثل xUnit، NUnit یا MSTest برای نوشتن تست‌های واحد.
ابزارهای اشکال‌زدایی: Visual Studio با قابلیت‌هایی مثل Breakpoint، Watch و Diagnostic Tools.
اتوماسیون: ابزارهایی مثل Azure DevOps برای تست خودکار و پیوسته.

نکات کاربردی برای مبتدیان

با کدهای کوچک شروع کنید: تست و اشکال‌زدایی یک متد ساده مثل جمع دو عدد را امتحان کنید.
خطاها را یادداشت کنید: هر بار که باگی پیدا می‌کنید، بنویسید چه بود و چطور حلش کردید. این به شما کمک می‌کند الگوها را بشناسید.
از ابزارها نترسید: Visual Studio ممکن است در ابتدا پیچیده به نظر برسد، اما با کمی تمرین، بهترین دوست شما خواهد شد.
سؤال بپرسید: اگر چیزی گیج‌کننده بود، از مستندات یا انجمن‌های برنامه‌نویسی مثل Stack Overflow کمک بگیرید.

یک سناریوی پیشرفته‌تر

فرض کنید در یک پروژه واقعی، کدی دارید که اطلاعات کاربر را از دیتابیس می‌خواند:

public string GetUserName(int userId)
{
    var user = _database.Users.Find(userId);
    return user.Name;
}

مشکل: اگر userId وجود نداشته باشد، user null می‌شود و خطای “Null Reference” می‌گیرید.
تست: یک تست واحد می‌نویسید که بررسی کند آیا متد در این حالت درست عمل می‌کند یا نه.
اشکال‌زدایی: با ابزارهای Visual Studio، مقدار user را موقع اجرا بررسی می‌کنید و شرطی اضافه می‌کنید:

public string GetUserName(int userId)
{
    var user = _database.Users.Find(userId);
    if (user == null) return "کاربر یافت نشد";
    return user.Name;
}

با این توضیحات گسترده، حالا درک کاملی از تست و اشکال‌زدایی در .NET دارید. این پایه محکمی است که در بخش‌های بعدی (مثل xUnit و Visual Studio) روی آن کار خواهیم کرد تا مهارت‌هایتان را به سطح بالاتری برسانیم.

نوشتن تست‌های واحد با xUnit

تست واحد یکی از مهم‌ترین و رایج‌ترین روش‌ها در تست و اشکال‌زدایی در .NET است که به شما کمک می‌کند تا مطمئن شوید هر بخش کوچک از کدتان به‌درستی کار می‌کند. xUnit یک فریم‌ورک قدرتمند و محبوب برای نوشتن تست‌های واحد در .NET است که به‌دلیل سادگی و انعطاف‌پذیری‌اش بین توسعه‌دهندگان جایگاه ویژه‌ای دارد. در این بخش، شما را از صفر تا صد با نوشتن تست‌های واحد با xUnit آشنا می‌کنیم؛ از نصب اولیه تا تکنیک‌های پیشرفته.

تست واحد چیست و چرا مهم است؟

تست واحد یعنی بررسی یک بخش کوچک و مستقل از کد (مثل یک متد یا تابع) به‌صورت جداگانه. هدف این است که مطمئن شوید این بخش، بدون وابستگی به بقیه برنامه، کارش را درست انجام می‌دهد. در تست و اشکال‌زدایی در .NET، تست واحد مثل چک کردن تک‌تک قطعات یک ماشین قبل از سوار کردن آن‌هاست. اگر هر قطعه درست کار کند، احتمال خرابی کل سیستم کمتر می‌شود.

مزایا:

پیدا کردن خطاها در مراحل اولیه.
اطمینان از عملکرد صحیح کد قبل از ادغام با بخش‌های دیگر.
ساده‌تر کردن فرآیند نگهداری و تغییر کد در آینده.

نصب xUnit

برای شروع کار با xUnit در پروژه .NET، باید آن را به پروژه خود اضافه کنید. این کار در Visual Studio خیلی ساده است:

باز کردن NuGet Package Manager:

در Solution Explorer، روی پروژه خود کلیک راست کنید.
گزینه “Manage NuGet Packages” را انتخاب کنید.

نصب پکیج‌های لازم:

در قسمت جستجو، عبارت xunit را تایپ کنید و آخرین نسخه را نصب کنید. این پکیج اصلی xUnit است.
سپس xunit.runner.visualstudio را هم نصب کنید. این پکیج به شما اجازه می‌دهد تست‌ها را مستقیماً در Visual Studio اجرا کنید.

بررسی نصب:

بعد از نصب، فایل csproj پروژه‌تان را باز کنید. باید خطوطی مثل این را ببینید:

<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.7.0" />

حالا همه‌چیز برای نوشتن اولین تست واحد آماده است!

نوشتن اولین تست

بیایید با یک مثال ساده شروع کنیم. فرض کنید یک کلاس Calculator دارید که دو عدد را جمع می‌کند:

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

حالا می‌خواهیم یک تست واحد بنویسیم تا مطمئن شویم این متد درست کار می‌کند. کد تست به این شکل خواهد بود:

using Xunit;

public class CalculatorTests
{
    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange (آماده‌سازی)
        var calculator = new Calculator();
        int a = 3;
        int b = 5;

        // Act (اجرا)
        int result = calculator.Add(a, b);

        // Assert (تأیید)
        Assert.Equal(8, result);
    }
}

توضیح بخش‌های کد
using Xunit;: این خط فریم‌ورک xUnit را به کد اضافه می‌کند تا بتوانید از قابلیت‌هایش استفاده کنید.
[Fact]: این ویژگی (Attribute) به xUnit می‌گوید که این متد یک تست واحد است. هر متد با [Fact] یک تست جداگانه محسوب می‌شود.

سه بخش اصلی تست (AAA):

Arrange (آماده‌سازی): اینجا شرایط اولیه را آماده می‌کنید؛ مثلاً یک نمونه از کلاس Calculator می‌سازید و ورودی‌ها را مشخص می‌کنید.
Act (اجرا): متد موردنظر را اجرا می‌کنید و نتیجه را در یک متغیر ذخیره می‌کنید.
Assert (تأیید): بررسی می‌کنید که آیا نتیجه همان چیزی است که انتظار داشتید یا نه. Assert.Equal می‌گوید که مقدار result باید برابر 8 باشد.

اجرای تست

برای اجرا کردن تست در Visual Studio:

به منوی “Test” بروید.
گزینه “Run All Tests” را انتخاب کنید.
در پنجره “Test Explorer”، نتیجه را می‌بینید. اگر تست موفق باشد، یک علامت سبز کنار نام تست ظاهر می‌شود. اگر شکست بخورد، علامت قرمز می‌بینید با توضیحی درباره اینکه چه چیزی اشتباه بوده است.

وقتی تست شکست می‌خورد

فرض کنید متد Add به‌اشتباه تفریق انجام دهد:

public int Add(int a, int b)
{
    return a - b; // خطا!
}

تست اجرا می‌شود و شکست می‌خورد چون 3 – 5 برابر -2 است، نه 8. اینجا xUnit به شما می‌گوید که نتیجه واقعی (-2) با نتیجه مورد انتظار (8) مطابقت ندارد. اینجاست که تست و اشکال‌زدایی در .NET با هم همکاری می‌کنند؛ تست خطا را پیدا کرد و حالا می‌توانید با اشکال‌زدایی آن را رفع کنید.

نکات پیشرفته با [Theory] و [InlineData]

وقتی کمی با تست‌نویسی راحت شدید، می‌توانید از قابلیت‌های پیشرفته‌تر xUnit استفاده کنید. مثلاً اگر بخواهید متد Add را با چند مجموعه داده مختلف تست کنید:

[Theory]
[InlineData(2, 3, 5)]
[InlineData(-1, 1, 0)]
[InlineData(10, -5, 5)]
public void Add_TwoNumbers_ReturnsSum(int a, int b, int expected)
{
    // Arrange
    var calculator = new Calculator();
    // Act
    int result = calculator.Add(a, b);
    // Assert
    Assert.Equal(expected, result);
}

[Theory]: به xUnit می‌گوید که این تست با چند مجموعه داده اجرا شود.
[InlineData]: هر خط یک مجموعه ورودی و خروجی مورد انتظار را مشخص می‌کند. اینجا تست برای سه حالت مختلف اجرا می‌شود.
این روش باعث می‌شود کدتان را در برابر سناریوهای مختلف آزمایش کنید و اطمینان بیشتری از عملکرد آن داشته باشید، که بخشی کلیدی از تست و اشکال‌زدایی در .NET است.

مثال عملی‌تر: تست یک متد پیچیده‌تر

فرض کنید متدی دارید که تخفیف را محاسبه می‌کند، اما باید درصد تخفیف را هم بررسی کند:

public class DiscountCalculator
{
    public decimal CalculateDiscount(decimal price, int discountPercentage)
    {
        if (discountPercentage < 0 || discountPercentage > 100)
            throw new ArgumentException("درصد تخفیف باید بین 0 و 100 باشد!");
        return price - (price * discountPercentage / 100);
    }
}

تست این متد می‌تواند شامل موارد زیر باشد:

public class DiscountCalculatorTests
{
    [Fact]
    public void CalculateDiscount_ValidInput_ReturnsCorrectDiscount()
    {
        var calculator = new DiscountCalculator();
        decimal result = calculator.CalculateDiscount(1000m, 20);
        Assert.Equal(800m, result);
    }

    [Fact]
    public void CalculateDiscount_NegativePercentage_ThrowsException()
    {
        var calculator = new DiscountCalculator();
        var exception = Assert.Throws<ArgumentException>(() => calculator.CalculateDiscount(1000m, -10));
        Assert.Equal("درصد تخفیف باید بین 0 و 100 باشد!", exception.Message);
    }

    [Fact]
    public void CalculateDiscount_Over100Percentage_ThrowsException()
    {
        var calculator = new DiscountCalculator();
        var exception = Assert.Throws<ArgumentException>(() => calculator.CalculateDiscount(1000m, 150));
        Assert.Equal("درصد تخفیف باید بین 0 و 100 باشد!", exception.Message);
    }
}

تست موفقیت: بررسی می‌کند که آیا تخفیف درست محاسبه می‌شود.
تست خطا: بررسی می‌کند که آیا متد در شرایط غیرمجاز (مثل درصد منفی یا بیش از 100) خطای مناسب می‌دهد.

نکات کاربردی برای نوشتن تست با xUnit

نام‌گذاری واضح: نام تست‌ها را طوری انتخاب کنید که مشخص باشد چه چیزی را تست می‌کنند (مثل CalculateDiscount_ValidInput_ReturnsCorrectDiscount).
تست‌های مستقل: هر تست باید بدون وابستگی به تست‌های دیگر اجرا شود.
پوشش کامل: سعی کنید همه سناریوها (ورودی‌های معتبر، نامعتبر، مرزی) را تست کنید.
سرعت: تست‌های واحد باید سریع باشند تا بتوانید آن‌ها را بارها اجرا کنید.

ادغام با اشکال‌زدایی

وقتی تست‌ها شکست می‌خورند، می‌توانید از ابزارهای اشکال‌زدایی Visual Studio (که در بخش بعدی توضیح داده می‌شود) استفاده کنید تا دلیل شکست را پیدا کنید. این ترکیب، قدرت واقعی تست و اشکال‌زدایی در .NET را نشان می‌دهد.

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

استفاده از ابزارهای اشکال‌زدایی در Visual Studio

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

اشکال‌زدایی چیست و چرا به آن نیاز داریم؟

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

شروع اشکال‌زدایی

برای شروع اشکال‌زدایی در Visual Studio، باید برنامه را در حالت خاصی اجرا کنید و نقاط کلیدی را مشخص کنید. بیایید قدم‌به‌قدم پیش برویم:

Breakpoint (نقطه توقف) بگذارید:

Breakpoint مثل یک علامت “ایست” در کد است که به Visual Studio می‌گوید: “اینجا صبر کن تا من بررسی کنم.”
در ویرایشگر کد، کنار شماره خطی که می‌خواهید بررسی کنید (مثلاً خطی که فکر می‌کنید مشکل دارد)، کلیک چپ کنید. یک دایره قرمز ظاهر می‌شود.
مثلاً اگر متدی دارید که نتیجه عجیبی می‌دهد، Breakpoint را روی آن خط بگذارید.

اجرای برنامه در حالت Debug:

به‌جای اجرای معمولی برنامه (با F5 در حالت Release)، باید آن را در حالت Debug اجرا کنید.
کلید F5 را بزنید یا از منوی “Debug” گزینه “Start Debugging” را انتخاب کنید.
برنامه اجرا می‌شود و وقتی به Breakpoint برسد، متوقف می‌شود تا شما بتوانید داخلش را ببینید.

ابزارهای اصلی اشکال‌زدایی

Visual Studio ابزارهای ساده اما قدرتمندی دارد که مثل یک جعبه‌ابزار برای تست و اشکال‌زدایی در .NET عمل می‌کنند:

Step Into (F11):

این ابزار شما را خط‌به‌خط داخل کد می‌برد، حتی اگر وارد یک متد دیگر شوید.
کاربرد: وقتی می‌خواهید دقیقاً ببینید داخل یک متد چه اتفاقی می‌افتد.
مثال: اگر متد Add به متد دیگری زنگ بزند، با F11 می‌توانید وارد آن متد شوید.

Step Over (F10):

این ابزار خط‌به‌خط پیش می‌رود، اما وارد متدها نمی‌شود؛ فقط نتیجه را رد می‌کند.
کاربرد: وقتی نمی‌خواهید وارد جزئیات یک متد شوید و فقط نتیجه‌اش را ببینید.
مثال: اگر مطمئن هستید متد داخلی مشکلی ندارد، با F10 از رویش رد شوید.

Step Out (Shift + F11):

اگر داخل یک متد هستید و می‌خواهید سریع به متد بالادستی برگردید، از این استفاده کنید.
کاربرد: وقتی عمیق در کد هستید و می‌خواهید به سطح بالاتر برگردید.

Watch Window:

این پنجره مثل یک تابلوی زنده است که مقدار متغیرها را در لحظه نشان می‌دهد.
روش استفاده: در حالت Debug، روی یک متغیر کلیک راست کنید و “Add to Watch” را بزنید. یا نام متغیر را دستی در پنجره Watch تایپ کنید.
مثال: اگر a و b را به Watch اضافه کنید، می‌بینید که مقدارشان در هر لحظه چیست.

Locals و Autos:

Locals: تمام متغیرهای محلی در محدوده فعلی را خودکار نشان می‌دهد.
Autos: متغیرهایی که اخیراً استفاده شده‌اند را نمایش می‌دهد.
کاربرد: نیازی نیست دستی متغیرها را اضافه کنید؛ Visual Studio خودش آن‌ها را پیدا می‌کند.

مثال عملی ساده

فرض کنید متد Add شما به‌اشتباه تفریق می‌کند:

public class Calculator
{
    public int Add(int a, int b)
    {
        return a - b; // خطا!
    }
}

بیایید اشکال‌زدایی کنیم:

یک Breakpoint روی خط return a – b; بگذارید.
برنامه را با F5 در حالت Debug اجرا کنید.
وقتی به Breakpoint رسید، a و b را به Watch اضافه کنید (مثلاً a = 3 و b = 5).
با F10 یا F11 پیش بروید و ببینید که نتیجه -2 می‌شود، درحالی‌که باید 8 باشد.

مشکل را پیدا کردید: – به‌جای + استفاده شده. کد را اصلاح کنید:

return a + b;

دوباره اجرا کنید تا مطمئن شوید درست شده است.

ابزارهای پیشرفته

وقتی با ابزارهای اصلی راحت شدید، می‌توانید سراغ قابلیت‌های پیشرفته‌تر بروید که تست و اشکال‌زدایی در .NET را حرفه‌ای‌تر می‌کنند:

Conditional Breakpoint (نقطه توقف شرطی):

فقط وقتی یک شرط خاص برقرار است، برنامه متوقف می‌شود.
روش استفاده: روی Breakpoint کلیک راست کنید، “Conditions” را انتخاب کنید و شرطی مثل a < 0 بنویسید.
مثال: اگر فقط می‌خواهید وقتی a منفی است بررسی کنید، این شرط را بگذارید.
کاربرد: وقتی خطا فقط در شرایط خاصی رخ می‌دهد (مثلاً ورودی‌های خاص).

Diagnostic Tools:

این پنجره (که با Ctrl+Alt+F2 باز می‌شود) مصرف حافظه، CPU، و رویدادهای برنامه را در لحظه نشان می‌دهد.
مثال: اگر برنامه‌تان کند است، می‌توانید ببینید کدام خط بیشترین حافظه را مصرف می‌کند.
کاربرد: برای بهینه‌سازی و پیدا کردن مشکلات عملکرد.

Call Stack:

نشان می‌دهد که برنامه از کجا به اینجا رسیده است؛ مثل یک نقشه راه از متدها.
مثال: اگر داخل متد Add هستید، Call Stack نشان می‌دهد کدام متد آن را فراخوانی کرده است.
کاربرد: وقتی نمی‌دانید چرا یک متد اجرا شده یا خطا از کجا شروع شده.

Immediate Window:

با Ctrl+Alt+I باز می‌شود و به شما اجازه می‌دهد کد اجرا کنید یا مقدار متغیرها را در لحظه تغییر دهید.
مثال: تایپ کنید a = 10 و ببینید چه تأثیری روی نتیجه دارد.
کاربرد: برای تست سریع فرضیه‌ها بدون تغییر کد اصلی.

مثال عملی پیچیده‌تر

فرض کنید یک متد دارید که لیست اعداد را جمع می‌کند، اما نتیجه اشتباه است:

public int SumList(List<int> numbers)
{
    int sum = 0;
    foreach (var num in numbers)
    {
        sum -= num; // خطا! باید + باشد
    }
    return sum;
}

سناریو: ورودی [1, 2, 3] باید 6 بدهد، اما -6 می‌دهد.

اشکال‌زدایی:

Breakpoint را روی خط sum -= num; بگذارید.
برنامه را در حالت Debug با ورودی [1, 2, 3] اجرا کنید.
با F10 خط‌به‌خط بروید و sum را در Watch ببینید:
بعد از num = 1: sum = -1
بعد از num = 2: sum = -3
بعد از num = 3: sum = -6

مشکل را پیدا کردید: -= به‌جای +=. کد را اصلاح کنید:

sum += num;

دوباره اجرا کنید و تأیید کنید که نتیجه 6 است.

نکات کاربردی برای اشکال‌زدایی

Breakpoint‌ها را سازمان‌دهی کنید: اگر چند Breakpoint دارید، در پنجره “Breakpoints” (Ctrl+Alt+B) آن‌ها را نام‌گذاری کنید.
از خروجی‌ها کمک بگیرید: خطاها یا لاگ‌ها را در پنجره Output ببینید تا سرنخ پیدا کنید.
تست و اشکال‌زدایی را ترکیب کنید: وقتی تست شکست می‌خورد، مستقیماً با Debug وارد کد شوید.
صبر داشته باشید: اشکال‌زدایی گاهی زمان‌بر است؛ قدم‌به‌قدم پیش بروید.

سناریوی واقعی

فرض کنید در یک پروژه واقعی، کدی دارید که اطلاعات کاربر را از دیتابیس می‌خواند:

public string GetUserName(int userId)
{
    var user = _database.Users.Find(userId);
    return user.Name; // اگر user null باشد، خطا می‌دهد
}

مشکل: اگر userId وجود نداشته باشد، user null می‌شود و خطای “Null Reference” رخ می‌دهد.

اشکال‌زدایی:

Breakpoint روی خط return user.Name; بگذارید.
با ورودی userId = 999 (که وجود ندارد) اجرا کنید.
در Locals ببینید که user null است.
کد را اصلاح کنید:

if (user == null) return "کاربر یافت نشد";
return user.Name;

با این ابزارها و تکنیک‌ها، تست و اشکال‌زدایی در .NET در Visual Studio به یک فرآیند ساده، سریع و قدرتمند تبدیل می‌شود. حالا می‌توانید با اطمینان بیشتری مشکلات را پیدا و رفع کنید.

تست خودکار و پیوسته

در پروژه‌های بزرگ و پیچیده، تست کردن دستی هر بخش از کد نه‌تنها زمان‌بر است، بلکه احتمال خطای انسانی را هم افزایش می‌دهد. اینجا مفهوم تست خودکار و پیوسته به‌عنوان یکی از ستون‌های اصلی تست و اشکال‌زدایی در .NET در سطح حرفه‌ای وارد صحنه می‌شود. این روش به شما اجازه می‌دهد تست‌ها را به‌صورت خودکار اجرا کنید و آن‌ها را در فرآیند توسعه‌تان ادغام کنید تا همیشه از کیفیت کد مطمئن باشید. در این بخش، همه‌چیز را از مفاهیم پایه تا پیاده‌سازی عملی و مزایای آن با جزئیات کامل توضیح می‌دهیم.

تست خودکار چیست؟

تست خودکار یعنی اجرای تست‌ها بدون نیاز به دخالت مستقیم انسان. به‌جای اینکه هر بار کد را تغییر دهید و خودتان تست‌ها را دستی اجرا کنید، ابزارها و اسکریپت‌هایی این کار را برایتان انجام می‌دهند. در تست و اشکال‌زدایی در .NET، این روش مثل استخدام یک دستیار خستگی‌ناپذیر است که همیشه کدتان را بررسی می‌کند.

مثال ساده: اگر تست‌های واحد را با xUnit نوشته باشید (مثل تست متد Add که قبلاً دیدیم)، این تست‌ها می‌توانند به‌صورت خودکار هر بار که کد تغییر می‌کند، اجرا شوند.
چرا مهم است؟ در پروژه‌های کوچک، شاید بتوانید تست‌ها را دستی اجرا کنید، اما در پروژه‌های بزرگ با صدها یا هزاران خط کد، این کار عملاً غیرممکن می‌شود.

مزایای تست خودکار

سرعت: تست‌ها در چند ثانیه یا دقیقه اجرا می‌شوند، نه ساعت‌ها.
دقت: خطای انسانی حذف می‌شود.
تکرارپذیری: هر بار که نیاز دارید، می‌توانید همان تست‌ها را با همان شرایط اجرا کنید.

تست پیوسته (Continuous Testing) چیست؟

تست پیوسته یک قدم فراتر از تست خودکار است. در این روش، تست‌ها نه‌تنها خودکار هستند، بلکه به‌طور مداوم و یکپارچه در فرآیند توسعه اجرا می‌شوند. این یعنی هر بار که کدی را تغییر می‌دهید و به مخزن (Repository) ارسال می‌کنید، تست‌ها به‌صورت خودکار اجرا می‌شوند و نتیجه را به شما گزارش می‌دهند. تست پیوسته بخشی از رویکرد “Continuous Integration” (CI) است که در تست و اشکال‌زدایی در .NET به شما کمک می‌کند همیشه یک قدم از باگ‌ها جلوتر باشید.

مثال: فرض کنید در یک تیم کار می‌کنید و یکی از همکارانتان کدی را تغییر می‌دهد که باعث خراب شدن یک بخش دیگر می‌شود. با تست پیوسته، این خطا فوراً پیدا می‌شود، نه چند هفته بعد.

تفاوت تست خودکار و تست پیوسته

تست خودکار: تست‌ها را خودکار می‌کنید، اما خودتان تصمیم می‌گیرید کی اجرا شوند.
تست پیوسته: تست‌ها خودکارند و به‌طور مداوم در فرآیند توسعه (مثلاً بعد از هر Push) اجرا می‌شوند.

ابزارهای تست پیوسته

برای پیاده‌سازی تست پیوسته در .NET، ابزارهای مختلفی وجود دارند:

Azure DevOps: یک پلتفرم کامل برای CI/CD که تست‌ها را با pipelineهایش اجرا می‌کند.
GitHub Actions: ابزاری ساده و قدرتمند برای پروژه‌های مبتنی بر GitHub.
Jenkins: یک ابزار متن‌باز برای اتوماسیون فرآیندها.
GitLab CI: مشابه GitHub Actions، اما برای پروژه‌های GitLab.
در این مقاله، روی GitHub Actions تمرکز می‌کنیم چون ساده، رایگان، و با .NET سازگار است.

راه‌اندازی تست خودکار و پیوسته

بیایید یک مثال عملی را قدم‌به‌قدم پیاده‌سازی کنیم. فرض کنید پروژه‌ای دارید که تست‌های xUnit در آن نوشته شده است (مثل تست‌های متد Add یا CalculateDiscount که قبلاً دیدیم). حالا می‌خواهیم این تست‌ها به‌صورت خودکار و پیوسته اجرا شوند.

1. تست‌های خود را آماده کنید

ابتدا مطمئن شوید تست‌هایتان با xUnit یا هر فریم‌ورک دیگری نوشته شده‌اند و به‌درستی کار می‌کنند. مثلاً:

public class CalculatorTests
{
    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        var calculator = new Calculator();
        int result = calculator.Add(3, 5);
        Assert.Equal(8, result);
    }
}

2. اضافه کردن فایل تنظیمات برای GitHub Actions

برای تست پیوسته، باید یک فایل YAML در پروژه‌تان بسازید که به GitHub بگوید چه کار کند:

در ریشه پروژه، پوشه .github/workflows را بسازید.
داخل این پوشه، فایلی به نام ci.yml (یا هر نام دلخواه با پسوند .yml) ایجاد کنید.
کد زیر را در آن قرار دهید:

name: CI Pipeline
on: 
  push: # هر بار که کد به مخزن Push می‌شود
  pull_request: # یا یک Pull Request باز می‌شود
jobs:
  build-and-test:
    runs-on: windows-latest # سیستم‌عامل سرور
    steps:
    - uses: actions/checkout@v3 # کد را از مخزن می‌گیرد
    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.x' # نسخه .NET مورد استفاده
    - name: Restore Dependencies
      run: dotnet restore # پکیج‌ها را نصب می‌کند
    - name: Build Project
      run: dotnet build --no-restore # پروژه را می‌سازد
    - name: Run Tests
      run: dotnet test --no-build --verbosity normal # تست‌ها را اجرا می‌کند

توضیحات:
on: [push, pull_request]: تست‌ها بعد از هر Push یا Pull Request اجرا می‌شوند.
runs-on: windows-latest: تست‌ها روی آخرین نسخه ویندوز اجرا می‌شوند (چون .NET با ویندوز سازگار است).
dotnet test: دستور اصلی برای اجرای تست‌ها.

3. کد را به GitHub Push کنید

پروژه را به مخزن GitHub خود Push کنید.
به تب “Actions” در GitHub بروید و ببینید که Workflow شما اجرا شده است.
اگر تست‌ها موفق باشند، وضعیت سبز می‌شود؛ اگر نه، قرمز می‌شود با جزئیات خطا.

مثال عملی پیچیده‌تر

فرض کنید پروژه‌تان چند تست دارد و می‌خواهید گزارش تست هم تولید کنید:

name: CI Pipeline with Test Report
on: [push]
jobs:
 build-and-test:
   runs-on: windows-latest
   steps:
   - uses: actions/checkout@v3
   - name: Setup .NET
     uses: actions/setup-dotnet@v3
     with:
       dotnet-version: '8.0.x'
   - name: Install Report Generator
     run: dotnet tool install -g dotnet-reportgenerator-globaltool
   - name: Run Tests with Coverage
     run: dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage
   - name: Generate Report
     run: reportgenerator -reports:"coverage/*/coverage.cobertura.xml" -targetdir:"coveragereport" -reporttypes:Html
   - name: Upload Report
     uses: actions/upload-artifact@v3
     with:
       name: coverage-report
       path: coveragereport

 

چه اتفاقی می‌افتد؟

تست‌ها اجرا می‌شوند و میزان پوشش کد (Code Coverage) محاسبه می‌شود.
یک گزارش HTML تولید می‌شود که نشان می‌دهد کدام بخش‌های کد تست شده‌اند.
گزارش به‌عنوان Artifact در GitHub ذخیره می‌شود.

مزایای تست خودکار و پیوسته

استفاده از این روش در تست و اشکال‌زدایی در .NET فواید زیادی دارد:

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

نکات پیشرفته

اجرای موازی: اگر تست‌های زیادی دارید، می‌توانید آن‌ها را موازی اجرا کنید تا زمان کمتری بگیرد:

jobs:
  build-and-test:
    runs-on: windows-latest
    steps:
    - run: dotnet test --no-build --parallel

تست در محیط‌های مختلف: تست‌ها را روی ویندوز، لینوکس و مک اجرا کنید:

runs-on: [windows-latest, ubuntu-latest, macos-latest]

اعلان‌ها: با ابزارهایی مثل Slack یا Email، نتیجه تست‌ها را به تیم اطلاع دهید.

سناریوی واقعی

فرض کنید در یک پروژه فروش آنلاین کار می‌کنید. یک تغییر کوچک در متد محاسبه تخفیف باعث می‌شود قیمت‌ها منفی شوند:

public decimal CalculateDiscount(decimal price, int discountPercentage)
{
    return price - (price * discountPercentage); // خطا! باید / 100 شود
}

بدون تست پیوسته، این خطا ممکن است به تولید برسد.
با تست پیوسته، بعد از Push کردن کد، تست‌ها شکست می‌خورند و شما فوراً متوجه می‌شوید که باید / 100 اضافه کنید.

ادغام با اشکال‌زدایی

وقتی تست‌ها در CI شکست می‌خورند، می‌توانید کد را محلی اجرا کنید و با ابزارهای Visual Studio (مثل Breakpointها) اشکال‌زدایی کنید. این ترکیب، تست و اشکال‌زدایی در .NET را به یک فرآیند یکپارچه و قدرتمند تبدیل می‌کند.

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

نتیجه‌گیری

تست و اشکال‌زدایی در .NET مهارتی ضروری است که هر توسعه‌دهنده، از مبتدی تا حرفه‌ای، باید به آن مسلط شود تا بتواند برنامه‌هایی باکیفیت و قابل اعتماد بسازد. در این مقاله، ما از مفاهیم پایه شروع کردیم و قدم‌به‌قدم به موضوعات پیشرفته‌تر رسیدیم. ابتدا با اهمیت و انواع تست و مراحل اشکال‌زدایی آشنا شدیم، سپس یاد گرفتیم که چگونه با xUnit تست‌های واحد بنویسیم، از ابزارهای قدرتمند Visual Studio برای تست و اشکال‌زدایی در .NET استفاده کنیم، و در نهایت با تست خودکار و پیوسته، فرآیند توسعه را به سطح حرفه‌ای ارتقا دهیم.

این مسیر به شما نشان داد که تست و اشکال‌زدایی در .NET نه‌تنها به پیدا کردن و رفع خطاها کمک می‌کند، بلکه اعتمادبه‌نفس شما را در توسعه نرم‌افزار بالا می‌برد، زمان‌تان را صرفه‌جویی می‌کند، و کیفیت محصول نهایی را تضمین می‌کند. اگر تازه‌کار هستید، با تست‌های ساده و اشکال‌زدایی خط‌به‌خط شروع کنید. اگر تجربه بیشتری دارید، تست‌های پیوسته و ابزارهای پیشرفته را به کار بگیرید تا پروژه‌هایتان را بهینه‌تر کنید.

هدف نهایی این است که با تسلط بر تست و اشکال‌زدایی در .NET، بتوانید کدهایی بنویسید که نه‌تنها کار می‌کنند، بلکه در برابر تغییرات و چالش‌ها مقاوم باشند. این مقاله فقط شروعی است؛ با تمرین مداوم و استفاده از منابع پیشنهادی، می‌توانید مهارت‌هایتان را عمیق‌تر کنید و به یک توسعه‌دهنده حرفه‌ای در اکوسیستم .NET تبدیل شوید. پس دست به کار شوید، تست کنید، اشکال‌زدایی کنید، و از ساختن برنامه‌های بی‌نقص لذت ببرید!

آموزش تست و اشکال‌زدایی در .NET

دیدگاه های شما

دیدگاهتان را بنویسید

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