Ответ
Mock-объект (заглушка) — это специально созданный объект, который имитирует поведение реальной зависимости (например, базы данных, сетевого сервиса, файловой системы) в модульных тестах. Его цель — изолировать тестируемый код от внешних систем, чтобы тесты были быстрыми, стабильными и предсказуемыми.
Зачем это нужно в C++ проектах:
- Скорость: Замена медленной БД или сетевого вызова быстрой заглушкой.
- Стабильность: Тесты не зависят от доступности внешних сервисов или состояния БД.
- Фокусировка: Тестируется только логика конкретного модуля, а не его зависимостей.
- Проверка взаимодействий: Mock позволяет убедиться, что тестируемый код корректно вызывает методы зависимостей с правильными аргументами.
Пример с использованием Google Mock (gmock):
Допустим, у нас есть класс DataProcessor, который зависит от интерфейса IDatabase.
// 1. Интерфейс зависимости
class IDatabase {
public:
virtual ~IDatabase() = default;
virtual bool connect(const std::string& connectionString) = 0;
virtual std::vector<int> fetchImportantData() const = 0;
};
// 2. Реальный класс, который мы хотим протестировать
class DataProcessor {
IDatabase* db;
public:
DataProcessor(IDatabase* database) : db(database) {}
int calculateSum() {
if (!db->connect("my_db")) {
throw std::runtime_error("Connection failed");
}
auto data = db->fetchImportantData();
return std::accumulate(data.begin(), data.end(), 0);
}
};
// 3. Создаем Mock-класс, реализующий интерфейс (обычно с помощью макросов GMOCK)
#include <gmock/gmock.h>
class MockDatabase : public IDatabase {
public:
MOCK_METHOD(bool, connect, (const std::string&), (override));
MOCK_METHOD(std::vector<int>, fetchImportantData, (), (const, override));
};
// 4. Пишем модульный тест с использованием Mock
#include <gtest/gtest.h>
TEST(DataProcessorTest, CalculateSumSuccess) {
// Arrange
MockDatabase mockDb;
DataProcessor processor(&mockDb);
// Настраиваем ожидания: метод connect будет вызван 1 раз с любым аргументом и вернет true
EXPECT_CALL(mockDb, connect(testing::_))
.Times(1)
.WillOnce(testing::Return(true));
// Настраиваем: метод fetchImportantData вернет заданный вектор
std::vector<int> fakeData = {1, 2, 3, 4, 5};
EXPECT_CALL(mockDb, fetchImportantData())
.Times(1)
.WillOnce(testing::Return(fakeData));
// Act
int result = processor.calculateSum();
// Assert
EXPECT_EQ(result, 15); // 1+2+3+4+5
// Google Mock автоматически проверит, что все ожидаемые вызовы (EXPECT_CALL) произошли.
}
Отличие Mock от других заглушек:
- Stub — просто возвращает заранее заданные данные, не проверяет вызовы.
- Fake — имеет упрощенную, но рабочую реализацию (например, база данных в памяти).
- Mock — как в примере выше: настраивает ожидания и проверяет, как его методы были вызваны.
В C++ экосистеме популярны фреймворки для мокирования: Google Mock (часть Google Test), FakeIt, Trompeloeil.