GoogleTest / Catch2 / doctest: юнит-тесты

Тема дорожной карты · Modern C++ Developer

Юнит-тестирование в C++ держится на трёх фреймворках. GoogleTest — стандарт индустрии для крупных кодовых баз (Chromium, LLVM, Android), идёт в комплекте с GoogleMock. Catch2 — фреймворк с выразительным синтаксисом SECTION и BDD-стилем, любимый в средних проектах и библиотеках. doctest — самый лёгкий по времени компиляции, позволяет держать тесты рядом с кодом прямо в заголовках. Все три интегрируются с CTest, CI и IDE, и выбор между ними — вопрос масштаба проекта и стиля команды.

Как это работает

В GoogleTest тест объявляется макросом TEST(SuiteName, TestName), а проверки — семействами EXPECT_* (продолжить после провала) и ASSERT_* (прервать тест). Фикстуры наследуются от ::testing::Test и используются через TEST_F, параметризованные тесты — через TEST_P и INSTANTIATE_TEST_SUITE_P. Фреймворк сам печатает значения операндов при провале EXPECT_EQ(a, b), а флаги --gtest_filter и --gtest_repeat управляют запуском.

Catch2 использует один макрос REQUIRE(expr) для любых проверок и разбирает выражение через перегрузку операторов, поэтому при провале выводит значения обеих сторон. Ключевая особенность — SECTION: тест выполняется заново для каждой секции, и общая подготовка перед секциями работает как фикстура без наследования. Есть генераторы (GENERATE), матчеры, бенчмарки и BDD-макросы SCENARIO/GIVEN/WHEN/THEN. Начиная с версии 3 Catch2 собирается как обычная библиотека, а не единый заголовок.

doctest повторяет идеи Catch2 (TEST_CASE, SUBCASE, CHECK), но спроектирован так, чтобы заголовок компилировался за доли секунды, а макросы полностью исчезали при определении DOCTEST_CONFIG_DISABLE. Благодаря этому тесты можно писать прямо в исходных файлах библиотеки рядом с проверяемым кодом.

Во всех трёх случаях запуск тестов из CMake оформляется через gtest_discover_tests, catch_discover_tests или doctest_discover_tests, после чего ctest видит каждый тест по отдельности.

Когда применять

GoogleTest выбирайте для больших команд и проектов, где нужен богатый набор матчеров, моки через GoogleMock, смерть-тесты и совместимость с инструментами вроде Bazel и Android. Catch2 удобен для библиотек и проектов среднего размера, где важна читаемость тестов, а моки нужны редко. doctest берите, когда критично время сборки, тестов тысячи, или хочется держать примеры использования внутри самой библиотеки. Любой из фреймворков стоит сочетать с санитайзерами и фаззингом: юнит-тесты проверяют логику, а инструменты рантайма — память и неопределённое поведение.

Типичные ошибки

Тесты, зависящие от порядка выполнения или общего глобального состояния, начинают падать при фильтрации и параллельном запуске. ASSERT_* в GoogleTest нельзя использовать в функциях, возвращающих значение, — макрос содержит return. В Catch2 подготовка перед SECTION выполняется столько раз, сколько секций, и дорогая инициализация замедляет прогон в разы. Сравнение чисел с плавающей точкой через EXPECT_EQ вместо EXPECT_NEAR или матчера WithinAbs даёт ложные срабатывания. Один гигантский файл тестов с единым заголовком фреймворка компилируется минутами — раскладывайте тесты по единицам трансляции и заранее собирайте main в отдельную библиотеку. И не забывайте про add_test/discover_tests: тесты, которые не запускает CI, эквивалентны отсутствующим.

Связанные понятия

Полезные ресурсы

🔤 Английские термины в этой теме

Слова из этой темы, которые русскоязычные часто произносят неверно. Кнопка 🔊 произносит голосом браузера.

Весь словарь терминов →