CI integration
veriCue tests run headless out of the box: Qt's offscreen platform plugin renders without a display server, and the pytest fixtures spawn your instrumented app automatically.
The essentials
Three environment variables cover most CI setups:
| Variable | Value | Purpose |
|---|---|---|
QT_QPA_PLATFORM | offscreen | Headless rendering, no X11/Wayland needed |
VERICUE_TEST_APP | path to your app binary | Tells the pytest fixtures what to spawn |
MYAPP_VERICUE_TOKEN | a secret | Auth token, if your app sets one |
Screenshots (screenshot, screenshot_compare) work fine offscreen - widgets render into the offscreen buffer.
Pytest fixtures
Installing the Python client (pip install vericue) auto-registers a pytest plugin with three fixtures:
vericue_test_app(module-scoped) - spawns the binary at$VERICUE_TEST_APPwithQT_QPA_PLATFORM=offscreen, passes--port 0and reads the actual port from the app'sVERICUE_PORT=<port>stdout line. Yields the port; terminates the app on module teardown.vericue_client- a connected, handshakenVeriCueClientagainst that app. Disconnects on test teardown. This is the one you want:pythonasync def test_login(vericue_client): await vericue_client.type_text("MainWindow/userField", "alice") await vericue_client.mouse_click("MainWindow/loginButton") props = await vericue_client.get_properties("MainWindow/statusLabel", ["text"]) assert props["text"] == "Logged in"vericue_client_factory- for tests needing several concurrent connections (e.g. multi-client scenarios).
For the port-discovery handoff to work in your own app, print the port after start():
printf("VERICUE_PORT=%d\n", server.serverPort());Reports
The plugin writes an HTML report and JUnit XML at session end. Configure via CLI (--vericue-report-html, --vericue-report-xml, --vericue-report-name) or pyproject.toml:
[tool.pytest.ini_options]
vericue_report_html = "reports/vericue.html"
vericue_report_xml = "reports/vericue.xml"
vericue_report_name = "MyApp UI suite"Most CI systems ingest the JUnit XML natively (GitHub checks, GitLab reports: junit:, Jenkins).
GitHub Actions
A complete workflow: build the app with veriCue enabled, run the UI suite headless, publish reports.
name: ui-tests
on: [push, pull_request]
jobs:
ui-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: '6.7.*'
- name: Build app (veriCue embedded)
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_VERICUE=ON
cmake --build build --parallel
- name: Install test deps
run: pip install vericue pytest pytest-asyncio
- name: Run UI tests
env:
QT_QPA_PLATFORM: offscreen
VERICUE_TEST_APP: build/my-app
run: |
pytest tests/ui -v \
--vericue-report-html reports/vericue.html \
--vericue-report-xml reports/vericue.xml
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-reports
path: reports/GitLab CI
ui-tests:
image: your-qt-build-image
script:
- cmake -B build -DENABLE_VERICUE=ON && cmake --build build --parallel
- pip install vericue pytest pytest-asyncio
- export QT_QPA_PLATFORM=offscreen
- export VERICUE_TEST_APP=build/my-app
- pytest tests/ui -v --vericue-report-xml reports/vericue.xml
artifacts:
when: always
reports:
junit: reports/vericue.xml
paths:
- reports/Parallel jobs
Because vericue_test_app starts the app with --port 0 (OS-assigned port), any number of jobs or pytest-xdist workers can run on the same machine without port collisions - each spawned app instance gets its own port and its own client connection.
Concurrent automation sessions in CI
With an organization key file (or trial), a session is one live client connection - it frees as soon as the connection closes. With floating licensing, a session is one running app process: each instrumented app checks out a lease at startup and returns it on exit. Either way, a typical CI job - one app instance, one client - uses one session at a time. Size floating pools to the maximum number of simultaneous jobs, not the number of tests.

