Введение
Написанные тесты приносят пользу только если они регулярно выполняются — в идеале автоматически при каждом push и pull request. GitHub Actions — широко используемый сервис непрерывной интеграции, тесно встроенный в GitHub, позволяющий описать пайплайн сборки и тестирования декларативно в YAML-файле. В статье настроим CI-пайплайн, который собирает Qt-проект и запускает тесты на нескольких платформах.
Концепция
GitHub Actions выполняет workflow — последовательность шагов (steps) внутри job, запускаемых на виртуальной машине (runner) с указанной ОС. Для Qt-проектов типичный пайплайн включает: установку Qt (через готовый action jurplel/install-qt-action, который скачивает и кеширует нужную версию Qt без необходимости собирать его из исходников), конфигурацию и сборку через CMake, и запуск тестов через ctest (стандартный тестовый раннер CMake, который автоматически обнаруживает тесты, зарегистрированные через add_test()).
Матрица сборки (strategy.matrix) позволяет запускать один и тот же набор шагов на нескольких комбинациях ОС и конфигураций параллельно, что важно для кросс-платформенных Qt-приложений.
Пример кода
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build-and-test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
build_type: [Debug, Release]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: '6.7.0'
cache: true
- name: Configure CMake
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
- name: Build
run: cmake --build build --config ${{ matrix.build_type }}
- name: Run tests
working-directory: build
run: ctest --output-on-failure -C ${{ matrix.build_type }}
- name: Upload test results on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.os }}-${{ matrix.build_type }}
path: build/Testing/Temporary/LastTest.log
Пояснения к коду
Секция strategy.matrix запускает пайплайн в шести независимых комбинациях (три ОС, две конфигурации сборки), каждая на отдельном runner — GitHub Actions выполняет их параллельно, что значительно сокращает общее время по сравнению с последовательным запуском. Шаг jurplel/install-qt-action устанавливает и кеширует указанную версию Qt отдельно для каждой ОС, избавляя от необходимости вручную писать логику установки Qt под разные платформы.
ctest --output-on-failure запускает все тесты, зарегистрированные через add_test() в CMakeLists.txt, и выводит подробный лог только для тех тестов, которые провалились — это даёт компактный вывод при успехе и полную диагностику при сбое. Последний шаг загружает лог тестов как артефакт CI только в случае сбоя (if: failure()), что облегчает последующий разбор причин падения без необходимости воспроизводить проблему локально.
Подводные камни
- Headless-окружение CI без графического дисплея для тестов, требующих
QApplication(виджеты, QML) — на Linux runner нужно запускать тесты черезxvfb-run(виртуальный X-сервер) или использовать платформенный плагинoffscreen(QT_QPA_PLATFORM=offscreen), иначе тесты с GUI-зависимостями завершатся с ошибкой инициализации платформы. - Долгая установка Qt без кеширования. Без
cache: trueвinstall-qt-actionкаждый запуск CI заново скачивает и распаковывает несколько гигабайт Qt SDK, что заметно увеличивает время каждого прогона — кеширование между запусками экономит значительное время. - Несовпадение версии Qt в CI и у разработчиков локально. Если в
install-qt-actionзафиксирована конкретная версия Qt, а локально разработчики используют другую, специфичные для версии особенности поведения могут проявляться по-разному — стоит синхронизировать версии или явно документировать минимально поддерживаемую. - Падение одного теста матрицы не должно автоматически останавливать остальные, если не настроено иначе (
fail-fast: falseв стратегии) — по умолчанию GitHub Actions может отменить оставшиеся задания матрицы при первом сбое, что скрывает информацию о состоянии на других платформах.