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, эквивалентны отсутствующим.
Связанные понятия
Полезные ресурсы
🔤 Английские термины в этой теме
Слова из этой темы, которые русскоязычные часто произносят неверно. Кнопка 🔊 произносит голосом браузера.
- fixture /ˈfɪkstʃər/ «фИксчер» разбор: Английский тестирования, безопасности и фронтенда
- coverage /ˈkʌvərɪdʒ/ «кАверидж» разбор: Английский тестирования, безопасности и фронтенда
- suite /swiːt/ «суит», как *sweet* разбор: Тестирование: flaky, coverage, mock, fixture, regression
- operator /ˈɒpəreɪtər/ ударение на первый слог в обоих разбор: Операторы: not equal, greater or equal, arrow
- minutes /ˈmɪnɪts/ протокол встречи, произносится как «минуты» разбор: Митинги и инциденты
- usage /ˈjuːsɪdʒ/ «Юсидж», с глухим «с» разбор: Пишем README и документацию на английском