CMakeMiddleCoding

Как включить и настроить CTest (тестирование CMake) в проекте?

CTest включается командой enable_testing() в CMakeLists.txt, тесты добавляются через add_test(). Запускается командой ctest из build-директории с флагами -V для вывода и --output-on-failure для CI.

Настройка CTest в CMake-проекте

CTest — компонент CMake для запуска тестов. Он не привязан к конкретному фреймворку: можно тестировать через Google Test, Catch2, Boost.Test или любой исполняемый файл, возвращающий ненулевой код при ошибке.

Минимальная настройка

cmake_minimum_required(VERSION 3.20)
project(MyApp CXX)

enable_testing()  # ОБЯЗАТЕЛЬНО — активирует CTest

add_executable(my_app src/main.cpp)

# Добавляем тест: имя + команда
add_test(NAME SmokeTest COMMAND my_app --selftest)

Интеграция с Google Test

include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/v1.14.0.zip
)
FetchContent_MakeAvailable(googletest)

add_executable(unit_tests
  tests/test_math.cpp
  tests/test_parser.cpp
)
target_link_libraries(unit_tests PRIVATE GTest::gtest_main)

# Автоматически регистрирует все TEST() как CTest-тесты
include(GoogleTest)
gtest_discover_tests(unit_tests)

Параметры add_test

# Рабочая директория
add_test(
  NAME IntegrationTest
  COMMAND integration_runner
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdata
)

# Передача переменных окружения
set_tests_properties(IntegrationTest PROPERTIES
  ENVIRONMENT "DB_URL=sqlite::memory:;LOG_LEVEL=debug"
  TIMEOUT 30       # секунды
  LABELS "slow"    # для фильтрации
)

Запуск тестов

# Сборка и запуск
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build

cd build
ctest                        # тихий режим
ctest -V                     # verbose: весь stdout тестов
ctest --output-on-failure    # показывать вывод только упавших (идеал для CI)
ctest -R "Unit"              # только тесты, чьё имя содержит "Unit"
ctest -L "slow"              # тесты с лейблом slow
ctest -j4                    # параллельный запуск (4 потока)
ctest --repeat until-fail:5  # повторять до первого падения

Интеграция с CI (GitHub Actions)

- name: Configure
  run: cmake -B build -DCMAKE_BUILD_TYPE=Release

- name: Build
  run: cmake --build build --parallel

- name: Test
  working-directory: build
  run: ctest --output-on-failure --parallel 4

CTestTestfile и CDash

CTest умеет отправлять результаты на CDash-сервер через ctest -D Experimental или ctest -D Continuous. Настройка хоста — в файле CTestConfig.cmake:

set(CTEST_PROJECT_NAME "MyApp")
set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
set(CTEST_DROP_METHOD "https")
set(CTEST_DROP_SITE "my-cdash.example.com")
set(CTEST_DROP_LOCATION "/submit.php?project=MyApp")

Подводные камни

  • enable_testing() должен быть вызван в корневом CMakeLists.txt, а не только в поддиректории — иначе ctest не найдёт тесты из add_subdirectory().
  • gtest_discover_tests() запускает тестовый бинарник при конфигурации (cmake-фазе), поэтому он должен быть собран до вызова — используйте gtest_add_tests() как альтернативу если это проблема.
  • Тест считается упавшим, если процесс завершился с ненулевым кодом — убедитесь, что ваш runner правильно пробрасывает exit code.
  • По умолчанию ctest не пересобирает проект — запускайте cmake --build перед ctest явно или используйте cmake --build build --target test.
  • Параллельный запуск (-j) опасен, если тесты пишут в одни и те же файлы или порты — явно задавайте RESOURCE_LOCK через set_tests_properties.
  • TIMEOUT по умолчанию не установлен — зависший тест блокирует весь CI. Всегда устанавливайте разумный таймаут.

Common mistakes

  • Объяснять CTest только по синтаксису, без жизненного цикла и стоимости.
  • Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
  • Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
  • Трактовать CMake как shell-скрипт вместо описания графа таргетов.

What the interviewer is testing

  • Кандидат формулирует точную модель для CTest, а не только определение.
  • Пример компилируем, безопасен по lifetime и соответствует версии технологии.
  • Названы trade-off, ограничения и диагностируемые симптомы ошибки.
  • Разделяет configure/generate/build и использует target-based подход.

Sources

Related topics