← Назад к вопросам

Что такое MOQ?

1.2 Junior🔥 131 комментариев
#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Moq (Moq Framework)?

Moq (произносится "Mock-you" или "M-O-Q") — это популярная библиотека для .NET, предназначенная для создания мок-объектов (mock objects) в процессе модульного тестирования. Она относится к категории библиотек для изоляции зависимостей и позволяет тестировать код в полной изоляции от реальных внешних сервисов, баз данных, файловых систем или сложных компонентов.

Основная цель и философия Moq

Главная задача Moq — упростить написание юнит-тестов через создание "подставных" объектов, которые имитируют поведение реальных зависимостей вашего кода. Это следует принципу Dependency Injection (DI) и тестирования в изоляции.

Представьте, вы тестируете класс OrderService, который зависит от IEmailService. Вместо использования реальной службы отправки писем (что замедлит тесты и может вызвать побочные эффекты), вы создаете мок для IEmailService, настраиваете его ожидаемое поведение и проверяете, как OrderService с ним взаимодействует.

Ключевые возможности и синтаксис Moq

1. Создание мок-объекта

Базовый синтаксис использует обобщенные типы (Generic).

// Создание мока для интерфейса
var mockService = new Mock<IEmailService>();

// Создание мока для класса (требует виртуальные методы или open/override)
var mockLogger = new Mock<Logger>();

2. Настройка возвращаемых значений (Setup)

Вы можете определить, что должен возвращать метод мока при определенных аргументах.

mockService.Setup(x => x.SendEmail("test@mail.com", It.IsAny<string>()))
           .Returns(true); // Всегда возвращает true

// Использование It.Is для гибких условий
mockService.Setup(x => x.SendEmail(It.Is<string>(s => s.Contains("@")), "Тема"))
           .Returns(true);

3. Проверка вызовов (Verify)

Moq позволяет убедиться, что метод был вызван ожидаемое количество раз с нужными параметрами — это behavior verification.

// Проверяем, что метод Send был вызван ровно 1 раз
mockService.Verify(x => x.SendEmail("client@mail.com", "Заказ создан"), Times.Once);

// Проверяем, что метод не вызывался никогда
mockService.Verify(x => x.SendEmail(It.IsAny<string>(), "Отмена"), Times.Never);

4. Имитация исключений (Throws)

Вы можете сконфигурировать мок так, чтобы он выбрасывал исключения для тестирования обработки ошибок.

mockService.Setup(x => x.SendEmail("invalid", It.IsAny<string>()))
           .Throws(new SmtpException("Ошибка сервера"));

5. Свойства (Properties) и последовательности (Sequence)

Moq умеет мокать свойства с помощью SetupGet, SetupSet, а также настраивать последовательность различных возвращаемых значений.

// Настройка свойства
mockService.SetupGet(x => x.IsEnabled).Returns(true);

// Последовательность возвращаемых значений
mockService.SetupSequence(x => x.GetStatus())
           .Returns("Pending")
           .Returns("Processed")
           .Returns("Completed");

6. Использование Callback

Позволяет выполнить дополнительный код при вызове метода мока, полезно для захвата переданных аргументов.

string capturedEmail = null;
mockService.Setup(x => x.SendEmail(It.IsAny<string>(), It.IsAny<string>()))
           .Callback<string, string>((email, body) => capturedEmail = email)
           .Returns(true);

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

  • Простой и лаконичный синтаксис: Fluent-интерфейс делает код тестов читаемым.
  • Строгая типизация: Все настройки проверяются на этапе компиляции.
  • Гибкость: Поддержка сложных сценариев через It.Is, It.IsAny, последовательности и коллбеки.
  • Интеграция с популярными фреймворками: Отлично работает с xUnit, NUnit, MSTest.
  • Активное сообщество: Широкая распространенность, много примеров и обсуждений.

Практический пример

Допустим, у нас есть интерфейс репозитория и сервис, который мы хотим протестировать.

public interface IUserRepository
{
    User GetUserById(int id);
    void SaveUser(User user);
}

public class UserService
{
    private readonly IUserRepository _repository;
    public UserService(IUserRepository repository) => _repository = repository;

    public string ActivateUser(int userId)
    {
        var user = _repository.GetUserById(userId);
        if (user == null) return "User not found";

        user.IsActive = true;
        _repository.SaveUser(user);
        return "User activated";
    }
}

Тест с использованием Moq:

[Test]
public void ActivateUser_ValidId_ActivatesAndSavesUser()
{
    // Arrange
    var mockRepo = new Mock<IUserRepository>();
    var testUser = new User { Id = 1, IsActive = false };

    // Настраиваем мок: при вызове GetUserById с id=1 вернуть testUser
    mockRepo.Setup(r => r.GetUserById(1)).Returns(testUser);

    var service = new UserService(mockRepo.Object);

    // Act
    var result = service.ActivateUser(1);

    // Assert
    Assert.AreEqual("User activated", result);
    Assert.IsTrue(testUser.IsActive); // Состояние объекта изменилось

    // Проверяем, что SaveUser был вызван ровно 1 раз с нашим user
    mockRepo.Verify(r => r.SaveUser(testUser), Times.Once);
}

Альтернативы и заключение

Помимо Moq, в экосистеме .NET существуют и другие библиотеки для мокинга: NSubstitute (более лаконичный синтаксис), FakeItEasy (другая философия API), Rhino Mocks (более старая). Однако Moq долгое время остается де-факто стандартом благодаря балансу между мощностью и простотой.

Использование Moq кардинально повышает эффективность написания надежных, быстрых и детерминированных модульных тестов, позволяя сфокусироваться на тестировании логики конкретного класса, не беспокоясь о работе его зависимостей. Это ключевой инструмент в арсенале современного C#-разработчика, следующих практикам Test-Driven Development (TDD) и Clean Architecture.

Что такое MOQ? | PrepBro