Что такое NSubstitute?

«Что такое NSubstitute?» — вопрос из категории Тестирование, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

NSubstitute — это популярная, легковесная библиотека для .NET, предназначенная для создания тестовых заглушек (mocks, stubs, spies) с очень читаемым и лаконичным синтаксисом. Она является альтернативой Moq и часто выбирается за свой "естественный" стиль.

Основные возможности:

  • Создание заместителей (substitutes) для интерфейсов и классов (с виртуальными членами).
  • Настройка возвращаемых значений и исключений.
  • Проверка вызовов (был ли вызван, сколько раз, с какими аргументами).
  • Работа с out и ref параметрами.

Пример сравнения: тест сервиса уведомлений

public interface INotificationService
{
    void SendEmail(string to, string subject, string body);
    Task<bool> SendSmsAsync(string phoneNumber, string message);
}

public class OrderProcessor
{
    private readonly INotificationService _notifier;
    public OrderProcessor(INotificationService notifier) => _notifier = notifier;

    public void ProcessOrder(Order order)
    {
        // ... логика обработки ...
        _notifier.SendEmail(order.CustomerEmail, "Order Confirmed", $"Your order #{order.Id} is processed.");
    }
}

[Test]
public void ProcessOrder_ShouldSendConfirmationEmail()
{
    // Arrange
    var notificationSub = Substitute.For<INotificationService>();
    var processor = new OrderProcessor(notificationSub);
    var testOrder = new Order { Id = 777, CustomerEmail = "test@example.com" };

    // Act
    processor.ProcessOrder(testOrder);

    // Assert
    // Проверяем, что SendEmail был вызван 1 раз с конкретными аргументами
    notificationSub.Received(1).SendEmail(
        "test@example.com",
        "Order Confirmed",
        "Your order #777 is processed."
    );
    // Проверяем, что асинхронный метод SendSmsAsync НЕ вызывался
    notificationSub.DidNotReceive().SendSmsAsync(Arg.Any<string>(), Arg.Any<string>());
}

// Пример настройки возвращаемого значения
[Test]
public async Task CheckStatus_ShouldReturnTrue_WhenSmsSentSuccessfully()
{
    var service = Substitute.For<INotificationService>();
    // Настраиваем асинхронный метод на возврат true
    service.SendSmsAsync("+79001234567", "Hello").Returns(Task.FromResult(true));

    bool result = await service.SendSmsAsync("+79001234567", "Hello");

    Assert.That(result, Is.True);
}

Плюсы NSubstitute:

  • Читаемый синтаксис: Received(), DidNotReceive(), Returns() выглядят как часть утверждения.
  • Умные сопоставления аргументов: Arg.Any<string>(), Arg.Is<string>(s => s.Contains("test")).
  • Хорошая документация и активное сообщество.