Тестирование в C# (.NET): фреймворки и инструменты для разработчика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Тестирование в C# (.NET): обзор фреймворков и инструментов

Качественное тестирование — основа надёжного ПО на платформе .NET. В экосистеме C# существует несколько зрелых фреймворков для модульного (unit), интеграционного и приёмочного тестирования. В этой статье мы разберём основные инструменты, их особенности и приведём практические примеры кода.

Основные фреймворки для unit-тестирования

В .NET Core и .NET 5+ встроенная поддержка тестирования реализована через три основных фреймворка:

  • MSTest — классический фреймворк от Microsoft, простой для начинающих.
  • NUnit — популярный open-source фреймворк с богатым набором атрибутов.
  • xUnit.net — современный, лёгкий и гибкий инструмент, созданный автором NUnit.

Все три поддерживаются в Visual Studio, Rider и через dotnet CLI. Выбор зависит от предпочтений команды, но xUnit и NUnit считаются более гибкими для сложных сценариев.

Пример теста на xUnit

Рассмотрим тестирование простого калькулятора с использованием xUnit:

using Xunit;

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public int Divide(int a, int b) => b == 0 ? throw new DivideByZeroException() : a / b;
}

public class CalculatorTests
{
    [Fact]
    public void Add_ShouldReturnSum_WhenTwoNumbers()
    {
        // Arrange
        var calc = new Calculator();

        // Act
        var result = calc.Add(3, 5);

        // Assert
        Assert.Equal(8, result);
    }

    [Theory]
    [InlineData(10, 2, 5)]
    [InlineData(9, 3, 3)]
    [InlineData(7, 1, 7)]
    public void Divide_ShouldReturnQuotient(int a, int b, int expected)
    {
        var calc = new Calculator();
        var result = calc.Divide(a, b);
        Assert.Equal(expected, result);
    }

    [Fact]
    public void Divide_ShouldThrow_WhenDivideByZero()
    {
        var calc = new Calculator();
        Assert.Throws<DivideByZeroException>(() => calc.Divide(5, 0));
    }
}

Атрибут [Fact] обозначает обычный тест, а [Theory] с [InlineData] позволяет запускать один тест с разными входными данными.

Инструменты для mock-объектов

При unit-тестировании часто нужно изолировать тестируемый класс от внешних зависимостей (базы данных, API, файловая система). Для этого применяются mock-фреймворки:

  • Moq — самый популярный, простой синтаксис, хорошая документация.
  • NSubstitute — более лаконичный, с fluent-интерфейсом.
  • FakeItEasy — дружелюбный API для быстрого создания заглушек.

Пример использования Moq

Допустим, у нас есть сервис, который отправляет email. Мы хотим протестировать логику без реальной отправки:

using Moq;
using Xunit;

public interface IEmailSender
{
    void Send(string to, string subject, string body);
}

public class NotificationService
{
    private readonly IEmailSender _sender;
    public NotificationService(IEmailSender sender) => _sender = sender;

    public void NotifyUser(string email, string message)
    {
        if (string.IsNullOrWhiteSpace(email))
            throw new ArgumentException("Email is required");
        _sender.Send(email, "Notification", message);
    }
}

public class NotificationServiceTests
{
    [Fact]
    public void NotifyUser_ShouldCallSend_WhenValidEmail()
    {
        // Arrange
        var mockSender = new Mock<IEmailSender>();
        var service = new NotificationService(mockSender.Object);

        // Act
        service.NotifyUser("test@example.com", "Hello!");

        // Assert
        mockSender.Verify(s => s.Send("test@example.com", "Notification", "Hello!"), Times.Once);
    }

    [Fact]
    public void NotifyUser_ShouldThrow_WhenEmailEmpty()
    {
        var mockSender = new Mock<IEmailSender>();
        var service = new NotificationService(mockSender.Object);

        Assert.Throws<ArgumentException>(() => service.NotifyUser("", "Message"));
    }
}

Moq автоматически создаёт заглушку интерфейса, а метод Verify проверяет, что вызов произошёл с нужными параметрами.

Fluent Assertions — читаемые проверки

Библиотека FluentAssertions делает код проверок более естественным и читаемым. Вместо Assert.Equal(expected, actual) вы пишете actual.Should().Be(expected):

using FluentAssertions;
using Xunit;

public class                    

Рекомендаций