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

Как используешь Moq?

2.0 Middle🔥 131 комментариев
#Тестирование

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

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

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

Использование Moq в unit-тестировании

Moq (произносится «Mock-you») — это популярная библиотека для .NET, позволяющая создавать **моки** (mock-объекты), **стабы** (stubs) и **фабрики** для изоляции зависимостей в unit-тестах. Я применяю Moq для создания тестируемого кода, который зависит от внешних сервисов, баз данных, файловых систем или других сложных компонентов.

Основные концепции и подходы

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

Моки создаются с помощью Mock<T>, где T — это интерфейс или класс (желательно интерфейс для лучшей гибкости). Пример:

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

[Test]
public void GetUser_ShouldReturnUser()
{
    // Arrange
    var mockRepo = new Mock<IUserRepository>();
    var expectedUser = new User { Id = 1, Name = "Alice" };
    
    // Настройка поведения мока
    mockRepo.Setup(repo => repo.GetUserById(1))
            .Returns(expectedUser);
    
    var userService = new UserService(mockRepo.Object);
    
    // Act
    var result = userService.GetUser(1);
    
    // Assert
    Assert.AreEqual(expectedUser, result);
}

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

Moq позволяет настраивать различные сценарии:

// Возврат конкретного значения
mock.Setup(x => x.GetCount()).Returns(42);

// Возврат последовательности значений
mock.SetupSequence(x => x.GetNext())
    .Returns(1)
    .Returns(2)
    .Throws<InvalidOperationException>();

// Асинхронные методы
mock.Setup(x => x.GetDataAsync())
    .ReturnsAsync(new Data { Value = "test" });

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

Одна из ключевых возможностей Moq — верификация, что методы были вызваны с определенными параметрами:

mockRepo.Verify(repo => repo.SaveUser(It.IsAny<User>()), Times.Once);
mockRepo.Verify(repo => repo.GetUserById(It.Is<int>(id => id > 0)), Times.AtLeast(1));

4. Использование It-класса для гибких сопоставлений

Класс It предоставляет мощные средства для настройки соответствий:

// Любое значение
mock.Setup(x => x.Process(It.IsAny<string>())).Returns(true);

// Значение по условию
mock.Setup(x => x.Process(It.Is<string>(s => s.StartsWith("A"))))
    .Returns(true);

// Диапазоны и регулярные выражения
mock.Setup(x => x.Validate(It.IsInRange(1, 100, Range.Inclusive))))
    .Returns(true);

Расширенные техники

Мокирование protected и internal членов

mock.Protected()
    .Setup<string>("GetInternalData")
    .Returns("secret");

Callbacks и отслеживание вызовов

var callCount = 0;
mock.Setup(x => x.Process(It.IsAny<string>()))
    .Callback(() => callCount++)
    .Returns(true);

Мокирование свойств (property mocking)

mock.SetupProperty(x => x.IsEnabled, true);
// Или цепочка свойств
mock.Setup(x => x.Configuration.Timeout).Returns(TimeSpan.FromSeconds(5));

SetupAllProperties для автоматической настройки

var mock = new Mock<IService>();
mock.SetupAllProperties(); // Все свойства будут иметь стандартное поведение

Best Practices и рекомендации

  1. Используйте интерфейсы — мокирование интерфейсов проще и чище, чем мокирование конкретных классов
  2. Избегайте over-mocking — не мокируйте всё подряд, иногда лучше использовать реальные легковесные объекты
  3. Проверяйте взаимодействия умеренно — чрезмерное использование Verify может сделать тесты хрупкими
  4. Создавайте фабрики моков для повторного использования сложных настроек:
public static class MockUserRepositoryFactory
{
    public static Mock<IUserRepository> CreateWithUser(int userId, string userName)
    {
        var mock = new Mock<IUserRepository>();
        mock.Setup(x => x.GetUserById(userId))
            .Returns(new User { Id = userId, Name = userName });
        return mock;
    }
}
  1. Используйте Moq в комбинации с FluentAssertions для более читаемых утверждений
  2. Очищайте состояние между тестами, особенно при использовании MockBehavior.Strict

Типичные сценарии использования

  • Тестирование бизнес-логики без доступа к базе данных
  • Имитация исключительных ситуаций (сетевые сбои, ошибки валидации)
  • Проверка правильности взаимодействия между компонентами
  • Изоляция тестов от внешних зависимостей (SMTP, API третьих сторон)
  • Тестирование сценариев времени выполнения (timeouts, повторные попытки)

Moq интегрируется с большинством фреймворков тестирования (xUnit, NUnit, MSTest) и является неотъемлемой частью современного стека тестирования C# приложений. Правильное использование моков значительно повышает надежность тестов и позволяет разрабатывать более чистую, модульную архитектуру.

Как используешь Moq? | PrepBro