Google Test: пример из книги по TDD
в обучении
Предыдущие посты в этой серии постов:
- Как начать изучать тестирование программ на C++;
- Начало изучения системы сборки программ CMake;
- Интеграция модульных тестов в проект на C++.
В предыдущих постах речь шла об изучении инструментов для модульного тестирования программ на языке C++. Когда мы с ними более-менее познакомились, я решил, что теперь нужно показать, как можно использовать эти инструменты, на каком-нибудь практическом примере.
Подходящий пример нашелся в книге «Modern C++ Programming with Test-Driven Development» Джеффа Лангра (вышла в 2013 году), которую я уже упоминал ранее.

Напомню, речь в этой книге идет про TDD (Test-Driven Development, по-русски «Разработка через тестирование»). По этой методике сначала пишется тест, затем под этот тест добавляется программный код. Причем мы пишем не сразу все тесты, а добавляем по одному, сразу же обеспечивая соответствующий программный код. То есть это постепенная (инкрементная) разработка.
Всего в книге 11 глав. В первой главе, которая называется «Global Setup», рассказано об инструментах, которые будут использованы, и о том, как их установить на компьютеры с разными операционными системами. Я эту главу прочел, но никак на нее не полагался. Мы использовали систему сборки CMake, компилятор MSVC, редактор кода VS Code и тестирующий фреймворк «Google Test» по-своему, я это описывал в предыдущих постах.
В плане установки инструментов стремно полагаться на книгу, написанную 13 лет назад, причем автор предпочитает Linux (хотя в книге есть инструкции и для Windows), а мы работаем на компьютерах с Windows 10.
Во второй главе, которая называется «Test-Driven Development: A First Example» (занимает 40 страниц книги из 335 всего), автор описывает, как работать по методике TDD, на конкретном примере: создание класса Soundex, реализующего алгоритм сравнения строк по звучанию. Этот алгоритм так и называется: Soundex. На входе — любое слово из английского языка, на выходе — четырехсимвольный код. Предполагается, что у слов со схожим звучанием будет похожий или идентичный четырехсимвольный код, полученный по этому алгоритму.
Мы потратили на разбор этого примера 4 занятия, каждое — по две пары (для тех, кто не в курсе: под «парой» в учебных заведениях подразумевают два академических часа). Автор очень хорошо и подробно объясняет каждый свой шаг. Получилось интересно и полезно. Тесты добавляются постепенно, в конце получается 12 тестов.
«Google Mock» влился в «Google Test»
Кое-что сначала было непонятно, но мы сумели разобраться.
Например, в вышеупомянутом примере автор включает (#include) в исходный файл с тестами библиотеку gmock/gmock.h. Насколько я понимаю, раньше (в период с 2008 года по 2019 год) существовал такой отдельный тестовый фреймворк «Google Mock». На GitHub остался архив этого фреймворка: https://github.com/google/googlemock. Там сказано следующее:
This project has been absorbed into the GoogleTest project.
В вышеуказанном примере Джефф Лангр для создания тестов использует макросы TEST (подробнее) и TEST_F (подробнее). Эти макросы есть и в современном фреймворке «Google Test».
Внутри тестов в обсуждаемом примере Джефф Лангр использует макросы-утверждения ASSERT_THAT (подробнее).
Мы же, поскольку начали работать с фреймворком «Google Test», включили в исходный файл с тестами библиотеку gtest/gtest.h, как показано в учебнике для начинающих на официальном сайте этого фреймворка, и успешно создали тесты с помощью макросов TEST и TEST_F. А вот макросы-утверждения ASSERT_THAT использовать не получилось, так как при сборке выдавалась синтаксическая ошибка: компилятор не находил определения этого макроса в библиотеке gtest/gtest.h.
Поначалу мы решили, что макрос ASSERT_THAT вышел из употребления в какой-то момент за прошедшие 13 лет с момента написания разбираемой книги. Довольно быстро мы заменили этот макрос макросами, начинающимися на последовательности ASSERT_ и EXPECT_, которые есть в библиотеке gtest/gtest.h (подробнее читайте в учебнике для начинающих). Например, утверждение из первого теста обсуждаемого примера в книге выглядит так:
ASSERT_THAT(encoded, testing::Eq("A"));
Мы заменили это утверждение следующим:
EXPECT_EQ(encoded, "A");
Это вполне работающая стратегия в данном случае, у нас всё получилось. Наши варианты кода тестов даже получились более читаемыми и более короткими, чем в книге, как видно из примера выше.
Позже выяснилось, что макрос-утверждение ASSERT_THAT вовсе не устарел и не вышел из употребления (таким образом получается, что и описываемую книгу тоже нельзя считать устаревшей, исходя из использования этого макроса; тесты из книги можно использовать точно в том же виде, в каком они описаны в книге). Как я понимаю, библиотека gmock/gmock.h при присоединении к фреймворку «Google Test» вовсе не влилась в библиотеку gtest/gtest.h, как можно подумать. Эти библиотеки продолжают существовать по отдельности, просто теперь они существуют в рамках общего фреймворка «Google Test».
Таким образом, для использования макроса-утверждения ASSERT_THAT достаточно в исходный файл с тестами включить еще и библиотеку gmock/gmock.h. Пример:
// tests/tests-for-lib.cpp
#include <gtest/gtest.h> // заголовочный файл фреймворка Google Test
#include <gmock/gmock.h> // для макроса ASSERT_THAT
#include "../modules/lib.h" // заголовочный файл тестируемого модуля
TEST(FactorialTestSuite, NegativeInputs)
{
ASSERT_THAT(-1, testing::Eq(factorial(-5))); // из Google Mock
EXPECT_EQ(-1, factorial(-5)); // из Google Test
}
Кроме этого, не забудьте указать подключение библиотеки gmock в скрипте системы сборки CMake. Пример:
# tests/CMakeLists.txt
project(tests)
add_executable(tests tests-for-lib.cpp)
target_link_libraries(tests
PUBLIC
lib
gtest
gmock
gtest_main
)
Таким образом, теперь мы можем использовать возможности обоих фреймворков в исходном файле с тестами одновременно, они не конфликтуют друг с другом. Полностью, со всеми возможными подробностями, я писал об интеграции модульных тестов в проект на языке C++ в прошлом посте этой серии постов.
В этой книге я оставил непрочитанными еще 9 глав, с третьей по одиннадцатую. Думаю, там еще много интересного можно найти. Но по нашему плану мы уже перешли к изучению автоматического документирования программ на языке C++. Возможно, в качестве инструмента возьмем Doxygen.