Erster Stand des Repositories AG_QGIS/Projektwissen (bisher Arbeitstitel "Workspace", ADR 0006). Der lokale Ordner heisst weiter AG_QGIS, weil darin auch die Plugin-Klone liegen. Git verbindlich (ADR 0007), aus dem bisherigen Wiki-Konzept entwickelt: - docs/leitfaden/05_git_arbeitsweise.md mit Kanal-Kette, Branch-Namen und Gitea-Branch-Schutz. - Der Einstiegspunkt eines Fixes richtet sich nach dem Kanal, in dem der Fehler sitzt; ein Fix an testing oder stable ist erst mit der Rueckfuehrung nach unten abgeschlossen. Das fehlte bisher. - Der stabile Kanal heisst stable, nicht main: der Release-Workflow in Repository:hidden/workflows fuehrt git checkout "$CHANNEL" aus, der Branchname muss also dem Kanalnamen entsprechen. Er existiert in keinem Plugin-Repo und ist vom Maintainer anzulegen. - Der Mensch drueckt ab: .claude/settings.json sperrt push, merge, tag. - scripts/hallo.py und /hallo holen den Stand aller Klone, per Fast-Forward wo gefahrlos; nie mergen, stashen oder verwerfen. Ticketzugriff: - scripts/gitea.py liest Issues, Kommentare und PRs ueber die oeffentliche API, ohne Token, nur GET. Schreibendes nur als Entwurf in entwuerfe/. - Skill sn-ticket. Tests (ADR 0009): - docs/leitfaden/06_tests.md: Testebenen, Testpflicht, und wie eine Fachperson beurteilt, ob Tests fachlich sinnvoll sind. - scripts/testkatalog.py erzeugt docs/tests/testkatalog.md per ast-Parsing, ohne Import und ohne QGIS. Befund: nur 100 von 332 Tests haben einen Docstring, sn_basis keinen einzigen von 87. - Vorlage und erste Testkonzept-Seite mit Mermaid. Dabei gefunden und gegen origin geprueft: _ermittle_gebietstyp_aus_fbschl hat keinen Test, obwohl PR #44/#45 genau diese Funktion tolerant gemacht hat. - docs/konzept/07_qgis_tests_plan.md: Stufenplan fuer Tests gegen echtes QGIS. Dabei aufgefallen: QGIS 3.40 ist seit Januar 2026 End of Life, qgis_min steht aber noch darauf. Werkzeuge (ADR 0008): - 04_werkzeuge_einrichten.md nur noch Claude Code als CLI und in VS Code. - .vscode/ entfernt, brauchbare Einstellungen als persoenliche Vorlage. Ausserdem: README als Einstieg neu aufgebaut, Mermaid-Diagramm in schnittstellen.md repariert (unmaskierte Klammern, rendert seither nicht). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
13 KiB
Plan: automatisierte Tests gegen echtes QGIS
Stand 04.09.2026. Vorschlag zur Abstimmung im Team. Ergänzt ../leitfaden/06_tests.md
um die zweite Testebene. Recherchestand und Quellen am Ende.
1. Worum es geht
Heute laufen 332 Tests im Mock-Modus — ohne QGIS, in unter einer Sekunde. Das deckt Logik, Parsing und Prüfregeln ab. Es deckt nicht ab, was erst im Zusammenspiel mit QGIS passiert: ob ein Layer wirklich entsteht, ob ein WFS-Ergebnis verarbeitbar ist, ob ein Drucklayout eine Seite erzeugt, ob ein Dock sich sauber wieder abmeldet. Genau dort sitzen die Fehler, die heute nur durch manuelles Klicken auffallen — und die zwischen Qt5 und Qt6 unterschiedlich ausfallen.
Es gibt seit Jahren einen etablierten Weg, QGIS-Plugins ohne Bildschirm und ohne menschliche Interaktion gegen eine echte QGIS-Instanz zu testen. Dieser Plan beschreibt, wie wir dort in vier Stufen hinkommen.
Der Mock-Modus bleibt. Er wird nicht ersetzt. Er ist die schnelle Ebene, die bei jedem Commit läuft; QGIS-Tests sind die langsame Ebene, die bei jedem Pull Request läuft. Wer die Mocks abschafft, tauscht Sekunden gegen Minuten und verliert die Wrapper-Disziplin, die den Qt5/Qt6-Code zusammenhält.
2. Vorab: unsere Zielversionen stimmen nicht mehr
Recherchestand 04.09.2026, geprüft am QGIS-Release-Fahrplan und an den Docker-Tags:
| Version | Status | |
|---|---|---|
| LTR | 3.44 | aktueller Langzeit-Release |
| Stable | 4.2 | wird am 25.09.2026 zum LTR |
| 3.40 | 3.40.15 (Januar 2026) | End of Life, keine Updates mehr |
workspace.toml sagt qgis_min = "3.40", alle .gitea/templates/metadata.template sagen
qgisMinimumVersion=3.40. Das ist keine Testfrage, sondern eine Produktentscheidung: Welche QGIS-Versionen
unterstützen wir? Sie muss vor Stufe 2 fallen, weil sie die Test-Matrix bestimmt. Vorschlag zur Diskussion:
3.44 als Minimum, 4.2 als zweites Matrix-Ziel, 3.40 nur solange Bestandsinstallationen es erzwingen.
3. Wie es technisch funktioniert
Der Kern ist qgis.testing.start_app() aus dem QGIS-Quellbaum. Es baut eine vollständige QgsApplication mit
registrierten Datenprovidern auf, in einem temporären Profil, und ist idempotent — mehrfacher Aufruf schadet
nicht. Plugins lädt es bewusst nicht; das eigene Plugin instanziiert der Test selbst.
import os
if not os.getenv("DISPLAY"):
os.environ["QT_QPA_PLATFORM"] = "offscreen" # muss VOR dem App-Start gesetzt sein
from qgis.testing import start_app, unittest
from qgis.testing.mocked import get_iface
QGIS_APP = start_app()
IFACE = get_iface() # echtes QMainWindow, echter QgsMapCanvas, echter LayerTree
get_iface() liefert kein selbstgebautes Mock, sondern ein QgisInterface-Objekt mit echtem Hauptfenster,
echtem Kartenfenster und echtem Layerbaum. Das ist deutlich realitätsnäher als alles, was wir selbst nachbauen
könnten, und es kostet uns keine Pflege.
QT_QPA_PLATFORM=offscreen ist der Schalter, der das Ganze ohne Bildschirm laufen lässt — auf dem CI-Server
genauso wie unter Windows.
4. Der Stufenplan
flowchart LR
S1["<b>Stufe 1</b><br/>start_app() lokal<br/><i>klein</i>"]
S2["<b>Stufe 2</b><br/>Gitea Actions<br/>Matrix 3.44 / 4.2<br/><i>mittel</i>"]
S3["<b>Stufe 3</b><br/>pytest-qgis<br/><i>klein</i>"]
S4["<b>Stufe 4</b><br/>Render-Vergleich<br/>Plan41<br/><i>groß</i>"]
S1 --> S2 --> S3 --> S4
style S1 fill:#e8f6ec,stroke:#2f9e44
style S2 fill:#e6f2ff,stroke:#2b7dd9
style S3 fill:#e6f2ff,stroke:#2b7dd9
style S4 fill:#fff4e6,stroke:#d9822b
Stufe 1 — QGIS-Tests lokal, ohne CI
Ziel: Eine erste echte QGIS-Suite je Plugin, die ein Entwickler auf seinem Windows-Rechner starten kann.
- Neuer Ordner
tests/qgis/je Plugin-Repo, getrennt von den Mock-Tests intests/. Beide Suiten bleiben unabhängig lauffähig. - Start unter Windows über die OSGeo4W-Umgebung:
C:\OSGeo4W\bin\python-qgis-ltr.bat -m pytest tests\qgis -v. Ein normalespython -m pytestfindet die QGIS-Bindings nicht. - Die vorhandenen Skripte
tests/test_qgis.batundtests/start_osgeo4w_qgis.batwerden dabei ersetzt. Sie enthalten heute fest verdrahtete Pfade (D:\OSGeo,D:\QGISQT5) und funktionieren nur auf einem Rechner. - Erste Kandidaten, weil dort der Mock-Modus am wenigsten aussagt: Layer anlegen und stylen (
sn_basis), Verarbeitung einer WFS-Antwort (sn_verfahrensgebiet), Erzeugen eines Drucklayouts (sn_plan41).
Aufwand: klein. Kein neues Werkzeug, keine Infrastruktur. Nutzen: sofort, aber nur für die, die es lokal ausführen.
Stufe 2 — Gitea Actions mit Docker
Ziel: Jeder Pull Request wird automatisch gegen zwei QGIS-Generationen geprüft, ohne dass jemand eine zweite QGIS-Version installiert.
Das ist der größte Hebel des ganzen Plans. Der Qt5/Qt6-Unterschied ist in fallstricke.md ein Dauerthema, und
er lässt sich mit Bordmitteln kaum abdecken — hier fällt er automatisch auf.
Die offiziellen Images qgis/qgis bringen QGIS samt Bindings mit. Verifizierte Tags (Stand 04.09.2026):
3.44, 3.44.14, 4.0, 4.2, 4.2.2, ltr, stable. QGIS 4 gibt es nur auf Qt6-Distributionen
(Debian trixie, Ubuntu questing) — kein 4.x-noble.
name: QGIS-Tests
on:
pull_request:
branches: [unstable, testing, stable]
jobs:
qgis:
runs-on: ubuntu-latest # Label muss auf einen Docker-fähigen act_runner zeigen
strategy:
fail-fast: false
matrix:
qgis: ["3.44", "4.2"]
steps:
- uses: actions/checkout@v4
- name: Tests in QGIS ${{ matrix.qgis }}
run: |
docker run --rm -v "$PWD":/src -w /src \
-e QT_QPA_PLATFORM=offscreen \
qgis/qgis:${{ matrix.qgis }} \
bash -c "python3 -m venv /venv --system-site-packages \
&& /venv/bin/pip install -q pytest \
&& /venv/bin/pytest tests/qgis -v"
Zu klären vor der Umsetzung:
- Der vorhandene Gitea-Runner trägt das Label
alpine-latest(siehedocs/vorlagen/gitea_workflow_tests.yaml). Für Docker-in-Docker braucht es einen Runner mit Docker-Zugriff. Das ist eine Frage an den Maintainer der Runner-Infrastruktur, keine Codefrage. - Die Fachplugins brauchen im Container auch
sn_basis. Das Muster steckt schon indocs/vorlagen/gitea_workflow_tests.yaml: die Nachbar-Repos dazuklonen und unter ihren Paketnamen ablegen.
Aufwand: mittel, im Wesentlichen Infrastruktur. Nutzen: hoch und dauerhaft.
Stufe 3 — pytest-qgis
Ziel: Die bekannten Stolperfallen von QGIS-Tests nicht selbst lösen müssen.
pytest-qgis (Version 4.1.1, August 2026, Python ≥ 3.10, QGIS ≥ 3.34, Qt6-tauglich seit 3.0.0) bringt mit:
| Was | Warum wichtig |
|---|---|
Fixture qgis_app |
startet die App einmal je Sitzung |
Fixture qgis_iface |
patcht qgis.utils.iface automatisch — sonst ist es None |
Fixture qgis_new_project |
leeres Projekt je Test; sonst wirken Layer aus Test A in Test B nach |
| automatisches Layer-Aufräumen | verhindert die bekannten Abstürze beim Beenden |
Fixture qgis_processing |
Processing-Registry initialisiert |
--qgis_disable_exit |
Notausgang, falls es beim Beenden doch kracht |
Aufwand: klein — etwa eine halbe Stunde plus Umstellung der bis dahin geschriebenen Tests. Empfehlung: erst nach Stufe 2, damit die Umstellung einmal an einer laufenden CI verifiziert wird.
Stufe 4 — Render-Vergleich für Plan41
Ziel: Erkennen, wenn sich das Aussehen eines Plans ungewollt ändert.
Für ein Plugin, dessen Zweck das Zeichnen von Plänen nach § 41 FlurbG ist, ist das fachlich der wertvollste Testtyp — und der teuerste. Mit einem verifizierten Fallstrick:
QgisTestCase.render_layout_check()undrender_map_settings_check()suchen Referenzbilder übersetControlPathPrefix()in einem zur Compile-Zeit einkompilierten Pfad des QGIS-Quellbaums. In einer installierten QGIS-Version existiert dieser Pfad nicht. Die bequemen Helfer sind für Plugins nicht nutzbar.
Gangbar sind stattdessen:
QgsLayoutExporter.renderPageToImage()und ein eigener, toleranter Bildvergleich, oderQgsRenderCheckerdirekt, das als einzigessetControlImagePath()anbietet.
Bedingungen, ohne die es nur Ärger macht:
- Referenzbilder im CI-Container erzeugen, nie auf einem Entwickler-Windows. Schriftrendering und Antialiasing unterscheiden sich zwischen den Plattformen und erzeugen sonst dauerhaft rote Tests.
- Toleranz einplanen (Farbabweichung und Anteil abweichender Pixel), nicht auf Byte-Gleichheit prüfen.
- Mit zwei bis drei Referenzplänen anfangen, nicht mit fünfzig.
Aufwand: groß. Erst angehen, wenn Stufen 1–3 stabil laufen.
5. Was wir bewusst nicht machen
| Ansatz | Warum nicht |
|---|---|
| YAML-Testframework für Processing-Algorithmen | An nose2, utilities.py und CTest im QGIS-Quellbaum gebunden. Für externe Plugins nur mit Portierungsaufwand nutzbar. Stattdessen processing.run(...) und assertLayersEqual. |
qgis_setup.sh / qgis_testrunner.sh (Plugin im echten QGIS laden) |
Kein pytest-Reporting; Erfolg wird über String-Suche in der Standardausgabe erkannt. Nur nötig, wenn der Plugin-Ladevorgang selbst geprüft werden soll. |
pytest-xdist (Parallelisierung) |
Bei unserer Teamgröße und Suite-Laufzeit unnötige Komplexität. |
| Eigenes Docker-Image bauen | qgis/qgis plus pip install im Job reicht. |
pb_tool |
Deployment-Helfer, kein Testwerkzeug. Lokal Geschmackssache. |
6. Bekannte Fallstricke
Wandern bei der Umsetzung nach docs/wissen/fallstricke.md:
QT_QPA_PLATFORMmuss vor dem Erzeugen derQgsApplicationgesetzt sein. Danach wirkt es nicht mehr.qgis.utils.ifaceist nachstart_app()None. Entwederpytest-qgisverwenden oder selbstqgis.utils.iface = get_iface()setzen — und darauf achten, keine Module zu früh zu importieren.QgsProject.instance()ist global. Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig.- Abstürze beim Beenden sind ein bekanntes QGIS-Thema, wenn Layer- oder Canvas-Objekte noch referenziert
werden. Gegenmittel:
pytest-qgisoder--qgis_disable_exit. offscreenhat keinen Fenstermanager. Fokus,activeWindow()und Drag-and-drop verhalten sich anders. Für Widget-Tests notfallsxvfb-run -adavorsetzen (nur Linux).- Modale Dialoge blockieren den Test. Reihenfolge der Gegenmittel: den modalen Aufruf per
monkeypatchersetzen;open()stattexec()verwenden; erst als letztesQTimer.singleShotmit einem Klick-Handler. - Locale. Die Container laufen mit
LANG=C.UTF-8, Entwickler-Windows meist auf Deutsch. Tests, die formatierte Zahlen oder Datumsangaben vergleichen, brechen sonst nur an einem der beiden Orte. - QGIS 4 / PyQt6.
exec_()gibt es nicht mehr, Enum-Kurzformen (Qt.UserRole) auch nicht. Das trifft Testcode genauso wie Produktivcode. Für den Umstieg gibt es das Prüfwerkzeugpyqgis4-checker.
7. Vorschlag zur Reihenfolge
- Entscheidung zu den Zielversionen (Abschnitt 2) — blockiert Stufe 2, eigenes Issue.
- Stufe 1 an einem Plugin als Machbarkeitsnachweis, am besten
sn_basis(keine Abhängigkeiten). - Runner-Frage klären — hat der Gitea-Runner Docker?
- Stufe 2 für
sn_basis, dann für die Fachplugins. - Stufe 3, sobald mehr als eine Handvoll QGIS-Tests existiert.
- Stufe 4 als eigenes Vorhaben mit eigenem Konzept.
Die Stufen 1 bis 3 sind zusammen etwa der Aufwand, den heute ein größeres Fach-Issue kostet. Stufe 4 ist ein eigenes Vorhaben.
Quellen
Recherchestand 04.09.2026.
qgis.testing(start_app,QgisTestCase): https://github.com/qgis/QGIS/blob/master/python/testing/__init__.pyqgis.testing.mocked.get_iface(): https://github.com/qgis/QGIS/blob/master/python/testing/mocked.py- Docker-Images und Test-Helfer: https://github.com/qgis/QGIS/blob/master/.docker/README.md
- Tag-Liste: https://hub.docker.com/r/qgis/qgis/tags
- Release-Fahrplan (LTR-Termine): https://qgis.org/resources/roadmap/
- Unit-Testing und Render-Checks: https://docs.qgis.org/testing/en/docs/developers_guide/unittesting.html
QgsRenderCheckervs.QgsMultiRenderChecker: https://github.com/qgis/QGIS/blob/master/src/core/qgsrenderchecker.hpytest-qgis: https://github.com/GispoCoding/pytest-qgis · https://pypi.org/project/pytest-qgis/- Modale Dialoge im Test: https://pytest-qt.readthedocs.io/en/latest/note_dialogs.html
- Migration zu Qt6/QGIS 4: https://plugins.qgis.org/docs/migrate-qgis4/ · https://github.com/qgis/pyqgis4-checker
- Gitea Actions, Runner-Labels: https://docs.gitea.com/usage/actions/act-runner/
Vorbilder mit echter QGIS-Testsuite, als Muster zum Abschauen:
3liz/lizmap-plugin— Matrix über drei QGIS-Versionen, eigenerstart_app()-Ersatz, dem hiesigen Setup am nächstenopengisch/qfieldsync— Dialog-Tests gegen echtes QGIS, Matrix inklusive QGIS 4opengisch/qgis_plugins_test_demo— kleines Referenz-Repo, unter anderem ein Drucklayout-Test