GUI-Tests in ganzen Sätzen.
AiGuiTest testet grafische Oberflächen auf einem Linux-Desktop über VNC. Statt Pixelkoordinaten und Referenzbilder zu pflegen, schreibst du, was du erwartest:
- „Ist ein Terminalfenster mit Shell-Prompt geöffnet?“
- „Klicke den Schließen-Button des Geany-Fensters.“
// auf einen blick
Prompts statt Pixel.
Ein Vision-Sprachmodell sieht den Screenshot und beantwortet die Frage. Verschiebt sich ein Fenster oder ändert sich die Auflösung, bleibt der Test gültig.
Asserts als Fragen
Das Modell beschreibt erst, was es sieht, und urteilt dann streng.
Klickziele als Beschreibungen
Ein Template-Vergleich siebt die Kandidaten, das Modell wählt nur noch zwischen wenigen.
Erst explorativ, dann deterministisch
Prompts interaktiv in der Werkbank entwickeln, als JSON exportieren, im Testlauf ausführen, ohne Modell wiederholen.
Nachvollziehbar
Jeder Lauf legt Screenshots, Bildausschnitte und ein JSON-Protokoll ab; bei jedem Fehler wird der Bildschirm gesichert.
Schlank
Reines Python mit OpenCV, NumPy, requests und vncdotool. Kein Test-Framework nötig.
Lokales Modell
Jeder OpenAI-kompatible Endpunkt, erprobt mit Ollama und qwen3-vl:8b.
// der name ist der aufbau
Drei Teile. Nur einer davon braucht eine GPU.
Das Modell kann auf einem anderen Rechner laufen. AiGuiTest selbst läuft auf jeder Maschine mit Python.
Vision-Modell
Jedes Vision-Modell hinter einer OpenAI-kompatiblen /chat/completions-Schnittstelle. Screenshots gehen auf 1280 px verkleinert hin, Koordinaten werden automatisch zurückgerechnet; temperature=0, seed=1.
Bildschirm statt Schnittstelle
Das Zielsystem ist ein beliebiger X-Desktop mit VNC-Server (RFB). AiGuiTest holt Screenshots aus dem Framebuffer und schickt Tasten, Text und Klicks zurück – kein DOM, kein Accessibility-Baum.
Lauf mit Protokoll
Jeder Lauf schreibt Screenshots und run.json nach runs/<zeitstempel>/. Schlägt ein Schritt fehl, liegt error.png im Laufordner, und der Lauf endet mit Exit-Code 1.
// so funktioniert es
Ein Schritt, drei Wege.
Für jede Prüfung geht ein verkleinerter Screenshot mit einem Prompt an das Vision-Modell; die Antwort ist JSON.
Modellaufruf
Tasten und Text brauchen kein Modell. Ein assert fragt wiederholt, bis das Urteil passt – oder bis nach 20 s der Timeout greift.
// arbeitsablauf
Erst explorativ, dann deterministisch.
Prompts entstehen im Gespräch mit dem Modell. Wiederholt wird ohne.
- ErkundenModell: ja
In der Werkbank
aiguitest-workbench– Prompts ausprobieren, mit-n 5jeden fünfmal laufen lassen und so die Stabilität messen, dannexport steps.json. Das Passwort schreibt der Export nicht mit. - AusführenModell: ja
Als Testlauf
aiguitest-run --steps steps.json– Testlauf mit Protokoll inruns/<zeitstempel>/. Exit-Code0bestanden,1fehlgeschlagen (miterror.png),2keine VNC-Verbindung. - WiederholenModell: nein
Ohne Modell
aiguitest-run --replay runs/<zeitstempel>– dieselben Tasten, Texte undclick-Schritte per Template-Matching. Asserts undclick_markswerden dabei übersprungen.
$ ./start.sh aiguitest-run steps.json
// praxis
Links entsteht der Test, rechts liegt er im Repository.
Die Werkbank ist eine Kommandoschleife. Die Pfeiltasten-History bleibt in ~/.aiguitest_history erhalten.
> key ctrl-alt-t > ask Ist ein Terminalfenster geöffnet? [1] ok=True | Ein Terminalfenster mit Shell-Prompt ist sichtbar. > keep terminal_offen > marks der Schließen-Button (X) des Terminal-Fensters --needle images/close.png 2 Kandidaten [Needle images/close.png: 2 von 60, bester Score 1.000] -> Nummer 1, Mitte (531,138) > click > keep terminal_schliessen > export steps.json
Ohne Modell: --fake. Gegen ein PNG statt gegen VNC: --image screen.png.
{
"name": "Geany starten und wieder schließen",
"vnc": { "server": "vnc-host::5901" },
"steps": [
{ "type": "key", "name": "terminal öffnen", "key": "ctrl-alt-t" },
{ "type": "type", "text": "geany &" },
{ "type": "key", "key": "enter" },
{ "type": "screenshot", "name": "nach_dem_start" },
{ "type": "assert", "prompt": "Ist das Fenster des Texteditors Geany geöffnet?" },
{ "type": "click_marks",
"prompt": "der Schließen-Button (X) in der Titelleiste des Geany-Fensters",
"needle": "images/close.png" },
{ "type": "assert", "prompt": "Ist das Geany-Fenster noch sichtbar?",
"expected": false }
]
}
expected: false prüft das Gegenteil – „Fenster ist jetzt weg“.
key und type tragen sich selbst in die Liste ein. export schreibt immer die ganze Liste; einzelne Schritte für eine bestehende Datei holt man mit json. Ältere Dateien mit deutschen Schlüsseln (schritte, typ, erwartet) werden weiterhin gelesen.
// vitalwerte
Was im Betrieb zählt.
Gemessen, nicht versprochen.
Modellaufrufe im Replay
Token für einen Klick, wenn ein Needle vorher siebt
Kontrollläufe richtig, auch mit vertauschtem Ziel
fragt ein Assert nach, bevor er fehlschlägt
// warum needles
Zwischen zwei Kandidaten entscheiden kann es.
Ein Vision-Modell ist schlecht darin, Pixelkoordinaten zu schätzen, und schlecht darin, aus 60 nummerierten Kästen den richtigen zu finden. Gemessen an einem Desktop mit Terminal und Geany, Ziel: das × des Geany-Fensters.
| Weg | Kandidaten | Ergebnis | Token |
|---|---|---|---|
| Box direkt vom Modell | – | daneben, teils außerhalb des Bildes | 1965 |
| nummerierte Kandidaten | 60 | keine Antwort | 2942 |
| nummerierte Kandidaten mit Needle | 2 | richtig | 205 |
Ein Needle ist ein kleiner Bildausschnitt des gesuchten Elements, etwa ein Schließen-Knopf. Der Template-Vergleich beantwortet zuverlässig „ist das ein Schließen-Knopf?“, das Modell zuverlässig „in welchem Fenster?“. Vier von vier Kontrollläufen trafen richtig, auch mit vertauschtem Ziel.
// grenzen und hinweise
Was AiGuiTest nicht kann.
Steht hier, weil es sonst im ersten Projekt auffällt.
- Modellaufrufe sind langsam.Je nach Hardware mehrere Sekunden pro Assert. Für schnelle Wiederholungen gibt es
--replay. - Direkte Koordinaten vom Modell sind unzuverlässig.
findundclickliegen oft Dutzende Pixel daneben. Für Klicksclick_marksmit Needle nehmen. - Needles hängen von Theme und Auflösung ab.Pro Widget-Art braucht es einen, bei anderem Fensterstil einen neuen.
- Höchstens 60 Kandidaten.Von oben nach unten gezählt. Elemente weit unten im Bild erreicht
click_markswomöglich nicht; prüfen lässt sich dort trotzdem mitassert. - Gleich aussehende Elemente.Das × zweier Fenster unterscheidet der Template-Vergleich nicht – das entscheidet das Modell über die Beschreibung, oder ein Needle mit mehr Kontext.
- Replay prüft nicht.Wiederholt werden Tasten, Text und
click-Schritte. Asserts undclick_markswerden übersprungen. - Denkende Modelle brauchen Token-Luft.qwen3-vl verbraucht rund 2000 Token für sein Reasoning; AiGuiTest erlaubt deshalb 4000.
// einordnung
Die Nachbarn können jeweils etwas, das AiGuiTest nicht kann.
Wenn du ein DOM hast, nimm Playwright. Der Fall hier ist der andere.
| AiGuiTest | os-autoinst | SikuliX | Midscene | |
|---|---|---|---|---|
| Zugriff aufs System | nur RFB | nur RFB | lokal oder RFB | Browser, Android |
| Prüfungen | in Worten | Needles | Bild, OCR | in Worten |
| Modell im CI-Lauf | für Asserts; Replay ohne | entfällt | entfällt | ja |
| Inferenz | OpenAI-kompatibel, lokal erprobt | entfällt | entfällt | lokal oder Cloud |
| Gedacht für | X-Desktops über VNC | Betriebssysteme | Desktop | Web |
Die Needles kommen bei AiGuiTest nicht an die Stelle des Urteils, sondern davor: sie sieben die Klickkandidaten. Geurteilt wird in Sätzen – deshalb überlebt ein Test, wenn sich ein Fenster verschiebt.
// faq
Häufige Fragen.
Brauche ich eine GPU?
Für das Modell ja, praktisch gesehen. AiGuiTest selbst läuft auf jeder Maschine mit Python; das Modell kann auf einem anderen Rechner laufen.
Funktioniert es mit anderen Modellen?
Mit jedem Vision-Modell hinter einer OpenAI-kompatiblen /chat/completions-Schnittstelle. Erprobt ist qwen3-vl:8b über Ollama.
Kann ich Tests ohne Modell in CI laufen lassen?
Teilweise: --replay spielt Tasten und Klicks per Template-Matching nach, prüft aber keine Asserts. --selftest läuft ganz ohne VNC und Modell.
Warum Tastennamen statt Text bei key?
key sendet genau einen Tastendruck oder eine Kombination wie ctrl-alt-t. Für Text gibt es type.
// erstgespräch
Bevor die nächste Abnahme von Hand durchgeklickt wird.
Dreißig Minuten, ein Blick auf dein System, eine ehrliche Einschätzung – auch wenn sie lautet, dass os-autoinst für dich reicht.