Что такое Mock в контексте тестирования?

Ответ

Mock (Мок) — это объект-заглушка, который в тестах заменяет реальную зависимость. Его основная цель — изолировать тестируемый модуль, контролировать поведение зависимостей и проверять взаимодействие с ними (какие методы были вызваны, с какими аргументами и сколько раз).

Зачем это нужно?

  • Изоляция: Тестируете только логику конкретного класса, а не работу базы данных, внешнего API или файловой системы.
  • Контроль: Можете легко смоделировать любые сценарии — успешное выполнение, ошибки, долгие ответы.
  • Верификация: Убедиться, что ваш код корректно взаимодействует с другими компонентами.

Пример на C# с библиотекой Moq:

// Интерфейс зависимости (например, репозиторий)
public interface IUserRepository
{
    User GetById(int id);
    void Save(User user);
}

// Класс, который мы тестируем (сервис)
public class UserService
{
    private readonly IUserRepository _repository;
    public UserService(IUserRepository repository) => _repository = repository;

    public string ActivateUser(int userId)
    {
        var user = _repository.GetById(userId);
        user.IsActive = true;
        _repository.Save(user);
        return $"User {user.Name} activated.";
    }
}

// Сам тест с использованием Moq
[Test]
public void ActivateUser_ShouldActivateAndSaveUser()
{
    // 1. Arrange (Подготовка)
    var mockRepo = new Mock<IUserRepository>();
    var testUser = new User { Id = 1, Name = "Alice", IsActive = false };

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

    var service = new UserService(mockRepo.Object);

    // 2. Act (Действие)
    var result = service.ActivateUser(1);

    // 3. Assert (Проверка)
    Assert.That(result, Is.EqualTo("User Alice activated."));
    Assert.That(testUser.IsActive, Is.True);
    // Верификация: метод Save был вызван ровно 1 раз с конкретным объектом user
    mockRepo.Verify(repo => repo.Save(testUser), Times.Once);
}

Ключевые методы Moq:

  • Setup() — задаёт поведение мока (что возвращать при вызове).
  • Returns() — определяет возвращаемое значение.
  • Verify() — проверяет, был ли вызов метода с ожидаемыми параметрами.

Ответ 18+ 🔞

Да ты посмотри, что эти умники придумали — Mock, или по-нашему «Мок». Это ж типа такая заглушка, подстава для тестов. Суть в чём: ты берёшь свою зависимость — ну там базу данных, какой-нибудь внешний сервис, который тебе на работу мешает, — и подменяешь её этой самой заглушкой. Чтобы тестировать только свою логику, а не весь этот огород вокруг.

А нахуя это вообще надо?

  • Изоляция: Чтобы не париться, что база данных упала, API сдохло или файловая система глючит. Тестируешь только свой код, а не всю вселенную.
  • Контроль: Можешь накрутить моку любое поведение — хоть успешный ответ, хоть ошибку, хоть долгий таймаут. Всё как в жизни, только под твоим управлением.
  • Верификация: Можешь потом проверить, а вызывал ли твой код вообще этот мок, с какими аргументами и сколько раз. Чтобы не получилось, что ты думаешь, что всё работает, а на самом деле ни один метод не дернулся.

Смотри пример на C# с библиотекой Moq:

// Интерфейс зависимости (ну, типа репозиторий)
public interface IUserRepository
{
    User GetById(int id);
    void Save(User user);
}

// Класс, который мы тестируем (сервис, где вся логика)
public class UserService
{
    private readonly IUserRepository _repository;
    public UserService(IUserRepository repository) => _repository = repository;

    public string ActivateUser(int userId)
    {
        var user = _repository.GetById(userId);
        user.IsActive = true;
        _repository.Save(user);
        return $"User {user.Name} activated.";
    }
}

// А вот сам тест, где мы и используем Moq
[Test]
public void ActivateUser_ShouldActivateAndSaveUser()
{
    // 1. Arrange (Готовим поле для драки)
    var mockRepo = new Mock<IUserRepository>();
    var testUser = new User { Id = 1, Name = "Alice", IsActive = false };

    // Настраиваем мок: когда вызовут GetById с единичкой — вернуть нашего тестового юзера
    mockRepo.Setup(repo => repo.GetById(1)).Returns(testUser);

    var service = new UserService(mockRepo.Object);

    // 2. Act (Делаем, что должны)
    var result = service.ActivateUser(1);

    // 3. Assert (Проверяем, что натворили)
    Assert.That(result, Is.EqualTo("User Alice activated."));
    Assert.That(testUser.IsActive, Is.True);
    // А вот это важное: проверяем, что метод Save вызвался ровно один раз с нашим юзером
    mockRepo.Verify(repo => repo.Save(testUser), Times.Once);
}

Основные приёмы в Moq, которые надо знать:

  • Setup() — этим ты говоришь моку: «Слушай сюда, когда будут вызывать такой-то метод, веди себя вот так».
  • Returns() — а этим уточняешь: «И вот это вот возвращай, понял?».
  • Verify() — это уже после драки проверка: «Так, пацан, а вызывал ли ты вообще метод Save, и если да, то сколько раз и с чем?». Без этого вообще смысла нет.