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

В чем разница между mock и stub?

1.8 Middle🔥 151 комментариев
#Тестирование

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

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

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

Различие между Mock и Stub в контексте автоматизированного тестирования

В автоматизированном тестировании, особенно при использовании модульного тестирования (Unit Testing), часто применяются тестовые двойники (Test Doubles) для замены реальных зависимостей тестируемого компонента. Mock и Stub являются двумя ключевыми типами таких двойников, но они выполняют разные функции и используются в различных сценариях. Понимание различий критично для написания чистых, эффективных и правильно структурированных тестов.

Основное философское отличие

Различие между Mock и Stub часто объясняется через их цель в тесте:

  • Stub используется для предоставления тестируемому модулю контролируемых входных данных. Его главная задача — заменить реальный объект, чтобы тест мог выполняться в изоляции, и обеспечить предопределенные ответы на вызовы методов.
  • Mock используется для проверки выходных взаимодействий тестируемого модуля. Его главная задача — заменить реальный объект и затем удостовериться (verify), что тестируемый модуль вызвал его методы с ожидаемыми параметрами в ожидаемой последовательности.

Проще говоря: Stub помогает тесту запуститься (обеспечивает входные данные), а Mock проверяет, как тестируемый код взаимодействовал с внешним миром (проверяет выходные взаимодействия).

Stub: Подставка для данных

Stub — это объект с предопределенным (жестко заданным или программируемым) поведением, который заменяет реальную зависимость только для того, чтобы тест мог выполниться. Stub не содержит логики проверки взаимодействий.

Основные характеристики Stub:

  • Предоставляет контролируемые ответы на вызовы методов.
  • Не проверяет, как и сколько раз эти методы были вызваны.
  • Его реализация часто очень простая и возвращает фиксированные значения.
  • Используется для управления состоянием (state) тестируемого объекта — мы проверяем конечное состояние объекта после взаимодействия с Stub.

Пример Stub в C#:

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

// Интерфейс зависимости
public interface IUserRepository
{
    User GetUserById(int id);
}

// Реализация Stub для теста
public class StubUserRepository : IUserRepository
{
    public User GetUserById(int id)
    {
        // Предопределенный ответ для определенного ID
        if (id == 1)
        {
            return new User { Id = 1, Name = "Test User", IsActive = true };
        }
        // Для других ID можем возвращать null или другое предопределенное значение
        return null;
    }
}

// Тест, использующий Stub
[Test]
public void UserService_Should_ActivateExistingUser()
{
    // Создаем Stub, который гарантированно вернет активного пользователя для ID=1
    var stubRepository = new StubUserRepository();
    var userService = new UserService(stubRepository);

    var result = userService.ProcessUser(1);

    // Проверяем СОСТОЯНИЕ: что сервис корректно обработал полученного пользователя
    Assert.That(result, Is.Not.Null);
    Assert.That(result.Name, Is.EqualTo("Test User"));
    // Stub сам не проверял, был ли вызван GetUserById.
}

Mock: Проверка взаимодействий

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

Основные характеристики Mock:

  • Содержит ожидания (expectations) относительно того, какие методы должны быть вызваны, с какими параметрами и в каком порядке.
  • После выполнения теста проводится проверка (verification) этих ожиданий.
  • Часто реализуется с помощью библиотек мокинга (Moq, NSubstitute).
  • Используется для проверки поведения (behavior) тестируемого объекта — мы проверяем, как объект взаимодействовал с Mock.

Пример Mock в C# с использованием библиотеки Moq:

Рассмотрим сервис, который должен вызывать метод отправки сообщения в зависимости от определенных условий.

// Интерфейс зависимости
public interface INotificationService
{
    void SendEmail(string to, string body);
}

// Тест, использующий Mock
[Test]
public void OrderService_Should_SendEmail_WhenOrderIsCompleted()
{
    // 1. Создаем Mock и устанавливаем ОЖИДАНИЯ
    var mockNotificationService = new Mock<INotificationService>();
    // Настраиваем метод. Мы ожидаем, что он будет вызван с определенными аргументами.
    mockNotificationService.Setup(service => service.SendEmail("customer@email.com", "Your order is complete."));

    var orderService = new OrderService(mockNotificationService.Object);
    var completedOrder = new Order { Id = 123, CustomerEmail = "customer@email.com" };

    // 2. Выполняем тестовое действие
    orderService.FinalizeOrder(completedOrder);

    // 3. ПРОВЕРЯЕМ ВЗАИМОДЕЙСТВИЕ: убеждаемся, что ожидаемый метод был вызван.
    mockNotificationService.Verify(service => service.SendEmail("customer@email.com", "Your order is complete."), Times.Once());
    // Тест провалится, если SendEmail не был вызван или был вызван с другими аргументами.
}

Сравнение в таблице

КритерийStubMock
Основная цельОбеспечить тест необходимыми входными данными.Проверить выходные взаимодействия тестируемого кода.
Что проверяетсяСостояние (State) тестируемого объекта после операции.Поведение (Behavior) тестируемого объекта (какие вызовы были сделаны).
Проверка вызововНе проверяет, были ли методы зависимости вызваны.Обязательно проверяет, были ли методы вызваны (с правильными аргументами и порядком).
Тип тестаЧаще используется в тестах на результат (возвращаемое значение или состояние).Чаще используется в тестах на взаимодействие (collaboration tests).

Практические рекомендации

  • Используйте Stub, когда вам нужно просто подменить данные, чтобы ваш код мог выполниться, и вы хотите проверить его конечный результат или состояние.
  • Используйте Mock, когда вам важно убедиться, что ваш код корректно вызвал определенный метод другой системы (например, отправку email, запись в лог, вызов внешнего API).
  • Современные библиотеки (Moq, NSubstitute) часто позволяют одним объектом выполнять обе роли: вы можете настроить его как Stub (для возвращения значений), а затем проверить его как Mock. Однако концептуальное разделение остается важным для понимания сути теста.
  • Чрезмерное использование Mock может привести к тестам, которые слишком тесно связаны с реализацией (проверяют не "что" делает код, а "как" он это делает), что делает их хрупкими. Stub часто дает менее связанные тесты.

Таким образом, выбор между Mock и Stub определяется целью вашего теста: проверяете ли вы конечное состояние объекта или же проверяете его коммуникацию с внешними зависимостями.