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

Что такое Mock?

2.0 Middle🔥 261 комментариев
#Сборка и инструменты

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

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

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

Основное определение

Mock — это объект-заместитель, который:

  • Имитирует поведение реального объекта (например, БД, HTTP клиента)
  • Позволяет контролировать его входы и выходы
  • Проверяет, как тестируемый компонент взаимодействует с этим объектом
  • Заменяет медленные/сложные зависимости

Различие между Mock, Stub, Fake и Spy

Это часто путают, но это разные инструменты:

// Интерфейс реальной зависимости
class DatabaseInterface {
public:
  virtual ~DatabaseInterface() = default;
  virtual User getUser(int id) = 0;
  virtual void saveUser(const User& user) = 0;
  virtual void deleteUser(int id) = 0;
};

// ============================================
// 1. STUB — просто возвращает заранее подготовленные данные
// ============================================
class StubDatabase : public DatabaseInterface {
public:
  User getUser(int id) override {
    return User{id, "John", "john@example.com"};
  }
  void saveUser(const User& user) override {}
  void deleteUser(int id) override {}
};

// Используется когда нужны фиксированные ответы
// Не проверяет, вызывались ли методы

// ============================================
// 2. MOCK — проверяет вызовы и их параметры
// ============================================
class MockDatabase : public DatabaseInterface {
public:
  MOCK_METHOD(User, getUser, (int id), (override));
  MOCK_METHOD(void, saveUser, (const User& user), (override));
  MOCK_METHOD(void, deleteUser, (int id), (override));
};

// Пример использования с Google Mock
TEST(UserServiceTest, SavesUserCorrectly) {
  MockDatabase mock_db;
  
  // Ожидаем вызов saveUser с конкретным пользователем
  EXPECT_CALL(mock_db, saveUser(_))
    .Times(1)  // Точно один раз
    .WillOnce(Return());
  
  // Тестируем бизнес-логику
  UserService service(&mock_db);
  service.registerUser("Alice", "alice@test.com");
  
  // Проверка: был ли вызван saveUser?
};

// ============================================
// 3. FAKE — работающая реализация (но не реальная)
// ============================================
class FakeDatabase : public DatabaseInterface {
private:
  std::map<int, User> in_memory_db;
public:
  User getUser(int id) override {
    return in_memory_db[id];
  }
  void saveUser(const User& user) override {
    in_memory_db[user.id] = user;
  }
  void deleteUser(int id) override {
    in_memory_db.erase(id);
  }
};

// Реально работает, но хранит данные в памяти
// Быстрее реальной БД

// ============================================
// 4. SPY — обёрнутый реальный объект (записывает вызовы)
// ============================================
class SpyDatabase : public DatabaseInterface {
private:
  RealDatabase real_db;
public:
  std::vector<std::string> call_history;
  
  User getUser(int id) override {
    call_history.push_back("getUser(" + std::to_string(id) + ")");
    return real_db.getUser(id);  // Реальный вызов!
  }
  // ...
};

// Использует реальное поведение, но отслеживает вызовы

Практический пример Mock в C++ с Google Mock

#include <gmock/gmock.h>
#include <gtest/gtest.h>

// Интерфейс, который мы будем мокировать
class EmailService {
public:
  virtual ~EmailService() = default;
  virtual bool sendEmail(const std::string& to, const std::string& body) = 0;
  virtual int getQueueSize() = 0;
};

// Класс, который мы тестируем (зависит от EmailService)
class UserNotifier {
private:
  EmailService* email_service;
public:
  explicit UserNotifier(EmailService* service) : email_service(service) {}
  
  void notifyUser(const std::string& email, const std::string& message) {
    if (!email.empty()) {
      email_service->sendEmail(email, message);
    }
  }
};

// ============================================
// MOCK класс
// ============================================
class MockEmailService : public EmailService {
public:
  MOCK_METHOD(bool, sendEmail, (const std::string&, const std::string&), (override));
  MOCK_METHOD(int, getQueueSize, (), (override));
};

// ============================================
// ТЕСТ с использованием Mock
// ============================================
class UserNotifierTest : public ::testing::Test {
protected:
  MockEmailService mock_email;
  UserNotifier notifier{&mock_email};
};

TEST_F(UserNotifierTest, NotifiesUserWithEmail) {
  // Arrange: настраиваем ожидания
  EXPECT_CALL(mock_email, sendEmail("user@example.com", "Hello!"))
    .Times(1)           // Ровно один раз
    .WillOnce(testing::Return(true));
  
  // Act: вызываем тестируемый код
  notifier.notifyUser("user@example.com", "Hello!");
  
  // Assert: Google Mock автоматически проверит вызовы
}

TEST_F(UserNotifierTest, SkipsNotificationIfEmailEmpty) {
  // sendEmail не должен быть вызван
  EXPECT_CALL(mock_email, sendEmail)
    .Times(0);  // Никогда
  
  // Пустой email
  notifier.notifyUser("", "Hello!");
  
  // Тест пройдёт, потому что sendEmail не был вызван
}

TEST_F(UserNotifierTest, FailureHandling) {
  // Имитируем ошибку отправки
  EXPECT_CALL(mock_email, sendEmail(_, _))
    .WillOnce(testing::Return(false));
  
  bool result = notifier.notifyUser("user@example.com", "Test");
  // Можно проверить обработку ошибки
}

Почему Mock важен

1. Изоляция компонентов

// ❌ Без Mock — зависит от реальной БД
void badTest() {
  RealDatabase db;  // Медленно, требует конфигурации
  UserService service(&db);
  service.registerUser("Test");  // Реально записывает в БД
  // Тест зависит от внешней системы!
}

// ✅ С Mock — независимый тест
void goodTest() {
  MockDatabase mock_db;
  EXPECT_CALL(mock_db, saveUser(_)).Times(1);
  UserService service(&mock_db);
  service.registerUser("Test");
  // Быстро, предсказуемо, не зависит от БД
}

2. Скорость

Без Mock (реальные зависимости):
- HTTP запрос к API: 100-500 ms
- Запрос к БД: 10-50 ms
- Обращение к файловой системе: 1-10 ms
Итого: сотни миллисекунд на один тест

С Mock:
- In-memory объект: < 1 ms
Итого: тест выполняется за микросекунды

1000 тестов = 100+ минут (без Mock) vs 1 минута (с Mock)

3. Предсказуемость

// Реальная БД — непредсказуема
class PaymentService {
  bool processPayment() {
    if (database.isDown()) return false;      // Не контролируем
    if (network.isDown()) return false;       // Не контролируем
    return api.charge(amount);                // Может быть rate-limited
  }
};

// Mock позволяет тестировать все сценарии
TEST(PaymentTest, HandlesDBDown) {
  MockDatabase mock;
  EXPECT_CALL(mock, query(_)).WillOnce(Throw(DatabaseException()));
  PaymentService service(&mock);
  ASSERT_FALSE(service.processPayment());
}

4. Проверка взаимодействия

// Убедиться, что компоненты взаимодействуют правильно
TEST(AnalyticsTest, TracksUserAction) {
  MockAnalytics mock_analytics;
  
  // Проверяем не только возвращаемое значение,
  // но и ВСЕ вызовы, которые сделаны
  EXPECT_CALL(mock_analytics, logEvent("user_signup", _));
  EXPECT_CALL(mock_analytics, logEvent("welcome_email_sent", _));
  EXPECT_CALL(mock_analytics, logEvent("user_registered", _))
    .Times(1);
  
  UserService service(&mock_analytics);
  service.registerNewUser("alice@example.com");
}

Инструменты для Mock в C++

  1. Google Mock (gmock) — самый популярный

    • Полная поддержка expectations
    • Гибкая синтаксис
    • Отлично интегрируется с Google Test
  2. Catch2 — встроенная поддержка mock (простая)

  3. trompeloeil — декларативные мокирования

  4. Hippomocks — header-only, простой API

Best practices

Хорошо:

  • Mock только внешние зависимости (БД, API, файловая система)
  • Проверять поведение, не реализацию
  • Один Mock на один интерфейс

Плохо:

  • Мокировать всё подряд
  • Чрезмерная специфичность (проверка каждого вызова)
  • Комплексные Mocks, которые имитируют реальное поведение (используйте Fake)

Mock — это мощный инструмент, но как и любой инструмент, нужно использовать его правильно и с умом.