Добавить в корзинуПозвонить
Найти в Дзене

Пишем фейковые объекты для тестов c Google Mock

В модульном тестировании фейк - это ограниченная реализация более сложного механизма, выполненная для упрощения процесса. Примером может служить использование объекта класса std::map вместо реальной базы данных. Другими словами, фейк - это упрощенная, но рабочая реализация зависимости тестируемого функционала. Есть два основных способа использовать gmock и gtest для фейков: Пусть мы разрабатываем систему хранения данных, где клиент взаимодействует с базой не напрямую, а через класс DBStorer. Одним из его методов является SaveAndGetData, который сохраняет строку, передаваемую в качестве параметра в базе, и возвращает все сохраненные строки. Это плохой метод с точки зрения принципа единой ответственности, но его вполне можно встретить на практике. Внутри DBStorer сохраненение данных происходит при помощи объекта, реализующего интерфейс хранения IStorage. Для того чтобы протестировать метод SaveAndGetData создаем фейковый класс FakeStorage, наследующий от IStorage и хранящий данные в

В модульном тестировании фейк - это ограниченная реализация более сложного механизма, выполненная для упрощения процесса. Примером может служить использование объекта класса std::map вместо реальной базы данных.

Другими словами, фейк - это упрощенная, но рабочая реализация зависимости тестируемого функционала.

Есть два основных способа использовать gmock и gtest для фейков:

  1. Создание фейка вручную, не используя gmock. Вы можете реализовать интерфейс (абстрактный класс) и использовать макросы gtest (ASSERT_EQ, EXPECT_TRUE и т.д.) для проверки результатов работы кода с фейком.
  2. Делегирование вызовов из мока в фейк. Можно создать мок-объект с помощью gmock, но настроить его так, чтобы он перенаправлял все вызовы фейк-объекту. При этом можно проверять количество и порядок вызовов методов с помощью EXPECT_CALL. Для этого используется вызов Invoke или настройка поведения через ON_CALL(...).WillByDefault(...).

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

-2

Одним из его методов является SaveAndGetData, который сохраняет строку, передаваемую в качестве параметра в базе, и возвращает все сохраненные строки. Это плохой метод с точки зрения принципа единой ответственности, но его вполне можно встретить на практике.

Внутри DBStorer сохраненение данных происходит при помощи объекта, реализующего интерфейс хранения IStorage. Для того чтобы протестировать метод SaveAndGetData создаем фейковый класс FakeStorage, наследующий от IStorage и хранящий данные в std::map.

Исходный код описанных классов выглядит следующим образом:

-3

Теперь перейдем к написанию теста, демонстрирующего работу связки мок-фейк. При создании объекта класса DBStorer передаем в конструктор экземпляр мок класса. Далее при помощи макроса ON_CALL настраиваем проксирование вызовов Save и GatAll одноименным методам фейка.

-4

После этого ожидаем, что метод Save нашего мока будет вызван 3 раза с разными аргументами в результате трех вызовов SaveAndGetData. При этом фейк каждый раз возвращаются все строки, хранящиеся в "базе".

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

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

Также фейк может быть полезен в случае тестирования асинхронного кода или логики ожидания ответов, когда необходимо заставить "работать" метод заданное время, имитируя сетевое взаимодействие или запрос в базу данных.

Пример кода:

EXPECT_CALL(mock, Get(_)) .WillOnce(Invoke([](int id) {

std::this_thread::sleep_for(5s); // Задержка 5 секунд

return "Delayed Data";

}));

Здесь показан еще один способ использования метода Invoke с передачей в него лямбда функции в качетсве аргумента.

Можно создать фейк, который имитирует нестабильно работающую сеть. Например, каждая третья попытка - ошибка.

-5

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