„Bei jeder Veröffentlichung dauert es einen ganzen Arbeitstag, jeden Bildschirm von Hand durchzuklicken, um alles zu prüfen.“ „Wir haben letzten Monat etwas repariert, und ein anderer Bildschirm direkt daneben ist kaputtgegangen — der Kunde hat es gefunden.“ „Das Web-Team hat seine Regressionstests mit Selenium oder Playwright automatisiert, aber die Desktop-App wurde nie angefasst.“ Diese Art von Beratungsanfrage hören wir oft von Teams, die WinForms-/WPF-Businessanwendungen lange gepflegt haben.
Gleichzeitig hören wir ebenso oft von Teams, die begeistert damit begonnen haben, Tests für jeden Bildschirm zu schreiben, den Aufwand aber ein Jahr später aufgaben, weil sie mit der Pflegelast nicht Schritt halten konnten. Wählt man Werkzeug und die Grenze „wie weit gehen wir“ falsch, kostet UI-Automatisierungstest am Ende mehr als manuelles Testen. Versteht man jedoch den zugrunde liegenden Mechanismus, entwirft Tests, die nicht leicht brechen, und beschränkt den Umfang auf Smoke-Tests, wird es zu einer Investition, die „zwei Tage vor jeder Veröffentlichung“ in „zwanzig unbeaufsichtigte Minuten jede Nacht“ verwandelt. Dieser Artikel arbeitet das gesamte Muster durch, das wir bei realen Projekten verwenden: die Grundlage — wie UI Automation (UIA) funktioniert —, den aktuellen Stand der Werkzeuge (FlaUI / WinAppDriver / Appium), die Implementierung mit FlaUI, den Entwurf für Robustheit sowie die unbeaufsichtigte Ausführung in CI.
1. Zuerst das Fazit
- UI-Automatisierungstest steht an der Spitze der Testpyramide. Er ist langsam, zerbrechlich und aufwendig zu diagnostizieren, wenn er fehlschlägt, sodass er kein Ersatz für Unit- und Integrationstests ist. Sichern Sie Logik in den darunterliegenden Schichten ab, und beschränken Sie UI-Tests auf Smoke-Tests, die bestätigen, dass „die App startet und die Hauptabläufe funktionieren“ (Kapitel 6).
- Die Grundlage ist Windows UI Automation (UIA). Sie finden Elemente, indem Sie den im Desktop verwurzelten Automatisierungsbaum durchlaufen, identifizieren sie anhand von Eigenschaften wie AutomationId / Name, und bedienen sie über Steuerungsmuster (Invoke, Value, SelectionItem und so weiter).12
- Prüfen Sie vor dem Schreiben von Code, wie die Elemente der Ziel-App tatsächlich mit inspect.exe oder Accessibility Insights for Windows erscheinen. inspect ist ein Legacy-Tool, das dem Windows SDK beiliegt; Accessibility Insights ist inzwischen das offiziell empfohlene Werkzeug.3
- Unsere empfohlene Werkzeugkombination ist FlaUI + xUnit / NUnit. FlaUI ist eine MIT-lizenzierte OSS-Bibliothek, die UIA dünn umschließt, sowohl UIA2 als auch UIA3 unterstützt und weiterhin aktiv gepflegt wird.4
- WinAppDriver, einst Microsofts offizielle Wahl, hatte seit v1.2.1 im November 2020 keine stabile Veröffentlichung mehr, und da der Server selbst Closed Source ist, kann die Community nicht einmal Fehler darin beheben. Wir empfehlen nicht, es für neue Arbeit einzuführen (Kapitel 3).5
- Grob 80 % der Testrobustheit hängen von der Konvention Ihres Entwicklungsteams für die Vergabe von AutomationId ab. In WPF ist das
x:NameoderAutomationProperties.AutomationId; in WinFormsName/AccessibleName. Verbieten Sie Suchen, die sich auf Anzeigetext (Name) verlassen, koordinatenbasierte Klicks undThread.Sleep— schreiben Sie Tests stattdessen mit bedingtem Warten (Retry) und dem Page-Object-Muster (Kapitel 4-5).67 - Die unbeaufsichtigte Ausführung in CI erfordert eine interaktive Desktop-Sitzung. Sie funktioniert nicht mit einem als Dienst konfigurierten Agenten und bricht bei gesperrtem Bildschirm oder getrennter RDP-Sitzung. Der Standardaufbau ist ein selbst gehosteter Runner, konfiguriert mit automatischer Anmeldung (Autologon) (Kapitel 7).8
2. Wie es funktioniert — der UI-Automation-Baum, Eigenschaften und Muster
2.1 Der Automatisierungsbaum
UI-Automatisierungstest-Werkzeuge erkennen den Bildschirm nicht als Bild. Windows besitzt eine offizielle Plattform namens UI Automation (UIA), die es assistiven Technologien wie Screenreadern erlaubt, die UI einer App programmatisch zu lesen und zu bedienen, und die Testautomatisierung fährt auf demselben Gleis mit.1
In UIA werden jedes offene Fenster und die darin enthaltenen Steuerelemente als Baumstruktur offengelegt, die im Desktop verwurzelt ist. Schaltflächen, Textfelder, Rasterzeilen — jedes davon ist ein Automatisierungselement im Baum. Neben der Rohansicht, die jedes Element enthält, besitzt der Baum auch gefilterte Ansichten: eine Steuerungsansicht, beschränkt auf Elemente, die tatsächlich zu bedienende Steuerelemente sind, und eine Inhaltsansicht, beschränkt auf inhaltstragende Elemente.1 Testcode durchläuft im Grunde die Steuerungsansicht, um Elemente zu finden.
Der entscheidende Punkt hier ist, dass Sie nur bedienen können, was im Baum offengelegt ist, nicht das, was visuell sichtbar ist. Bildschirme, die aus Standardsteuerelementen gebaut sind, legen sich sauber in den Baum offen, doch selbst gezeichnete Listen, von einer Grafikbibliothek gerenderte Zeichenflächen oder Teile eines Drittanbieter-Rasters erscheinen im Baum möglicherweise als nichts weiter als „ein einziges Element“. Was der Baum offenlegt, setzt unmittelbar die Obergrenze dessen, was UI-Automatisierungstest abdecken kann — weshalb das Prüfen des Baums vor dem Schreiben von Code (Abschnitt 2.3) das Erste ist, was zu tun ist.
2.2 Eigenschaften und Steuerungsmuster
Eigenschaften sind die Hinweise, mit denen Sie das gewünschte Element im Baum identifizieren. In der Praxis zählen diese vier:
| Eigenschaft | Inhalt | Rolle beim Testen |
|---|---|---|
| AutomationId | Ein vom Entwickler vergebener Bezeichner; unabhängig von der Sprache (Gebietsschema) | Der bevorzugte Suchschlüssel. Sollte unter Geschwisterelementen eindeutig sein6 |
| Name | Der von der Anzeige abgeleitete Name (z. B. die Beschriftung einer Schaltfläche) | Für Menschen leicht lesbar, bricht aber bei Textänderungen oder bei Lokalisierung der App |
| ControlType | Die Art des Steuerelements — Button, Edit, ComboBox usw. | Hilft, die Suche einzugrenzen |
| ClassName | Der Name der Implementierungsklasse (z. B. ein WinForms-Klassenname) | Letztes Mittel; anfällig gegenüber Implementierungsänderungen |
AutomationId ist offiziell definiert als etwas, das „unabhängig vom Gebietsschema gleich bleiben sollte“ und „unter Geschwisterelementen eindeutig sein sollte“ — eine Eigenschaft, die eigens dazu dient, UI-Automatisierungstests zu erlauben, Elemente über Sprachen und Versionen hinweg zuverlässig zu finden.6 Anders gesagt: Die UI einer App zu testen, die keine AutomationId vergeben hat, bedeutet, auf zerbrechliche Hinweise wie Anzeigetext oder Baumposition zurückzufallen. Das ist Thema von Kapitel 5.
Steuerungsmuster sind, wie Sie ein gefundenes Element bedienen. UIA legt die funktionalen Fähigkeiten eines Steuerelements — „kann geklickt werden“, „hat einen Wert“, „kann ausgewählt werden“ — als eine Menge von Mustern offen, unabhängig vom Steuerelementtyp. Die Dokumentation selbst beschreibt die Beziehung zwischen Steuerungsmustern und der UI als „analog zur Beziehung zwischen einem COM-Objekt und seinen Schnittstellen“: Sie fragen ein Element ab, welche Muster es implementiert, und bedienen es dann über dieses Muster.2 Wenn Sie mit COM vertraut sind, denken Sie daran als das UI-Äquivalent zu QueryInterface. Die wichtigsten Muster sind:
| Muster | Was es tut | Typische Steuerelemente |
|---|---|---|
| Invoke | Führt die Standardaktion aus (entspricht einem Klick) | Schaltflächen, Menüeinträge |
| Value | Holt/setzt einen Wert | Textfelder |
| SelectionItem / Selection | Wählt Elemente aus, ruft den Auswahlstatus ab | Listen, Comboboxen, Registerkarten |
| Toggle | Schaltet ein/aus | Kontrollkästchen |
| ExpandCollapse | Klappt auf/zu | Comboboxen, Baumelemente |
| Text | Liest Textinhalt | Dokumente, Rich Text |
| Window | Maximieren/Minimieren/Schließen | Oberste Fenster |
| Scroll / ScrollItem | Bildlauf, ein Element in Sicht bringen | Listen, Raster |
Testcode, der „auf eine Schaltfläche klickt“, ist unter der Haube in Wahrheit: „das Invoke-Muster dieses Elements holen und Invoke() aufrufen“. Er berechnet keine Koordinaten und sendet keine Mausereignisse — er ruft die vom Steuerelement selbst offengelegte Operation auf. Dieser Unterschied ist es, der Tests unabhängig von Fensterposition oder DPI stabil macht.
2.3 Prüfen, was „tatsächlich sichtbar“ ist, mit inspect.exe und Accessibility Insights
Der beste Weg, um zu sehen, welche AutomationId / Name / ControlType / Muster die Elemente der Ziel-App tatsächlich offenlegen, ist, sich das Original mit einem Werkzeug anzusehen.
- inspect.exe: Das klassische, dem Windows SDK beiliegende Werkzeug (zu finden unter
bin\<version>\<platform>im SDK-Installationsverzeichnis). Wählen Sie ein Element mit der Maus oder dem Tastaturfokus aus, und es listet die UIA-Eigenschaften und -Muster auf und erlaubt auch, die Baumnavigation zu prüfen. Allerdings ist es offiziell als Legacy-Werkzeug positioniert, und die Migration zu Accessibility Insights wird empfohlen.3 - Accessibility Insights for Windows: Microsofts aktuell empfohlenes Werkzeug. Seine Live-Inspect-Funktion — bei der man die UIA-Eigenschaften eines Elements nur durch Bewegen der Maus oder des Fokus prüfen kann — ist praktisch, und es bringt auch automatisierte Barrierefreiheitsprüfungen (FastPass) mit.3
- FlaUInspect: Der dem FlaUI-Projekt beiliegende Inspector. Er erlaubt, den Baum aus der UIA2- und UIA3-Perspektive zu betrachten, die FlaUI tatsächlich verwendet, und lohnt sich daher zur Installation neben FlaUI, wenn Sie damit Tests schreiben.4
Als Faustregel gilt: Das Erste, was bei der Einführung von UI-Automatisierungstest zu tun ist, ist nicht „Testcode schreiben“ — es ist, die Hauptbildschirme in einem inspect-artigen Werkzeug zu öffnen und eine Bestandsaufnahme zu machen, wie umfangreich die AutomationId-Abdeckung bereits ist. Stellt sich diese als spärlich heraus, ist es letztlich schneller, zuerst mit einer Nachbesserung auf App-Seite zu beginnen (Kapitel 5).
3. Die Werkzeugoptionen — warum wir FlaUI empfehlen, und wo WinAppDriver heute steht
Sie können UIA direkt über COM ansprechen, doch in der Praxis verwendet man eine Wrapper-Bibliothek. Hier der Stand der Optionen für 2026, basierend auf selbst verifizierten Fakten.
| Werkzeug | Form | Status (Stand 2026) | Empfehlung für neue Einführung |
|---|---|---|---|
| FlaUI | .NET-Bibliothek (MIT) | Aktiv gepflegtes OSS. v5.0.0 veröffentlicht im Februar 20254 | Erste Wahl |
| WinAppDriver | Server nach WebDriver-Protokoll (Microsoft) | Letzte stabile Veröffentlichung v1.2.1 war im November 2020. v1.3 bleibt ein RC von Juli 2020. Über 1.100 offene Issues5 | Effektiv zum Stillstand gekommen. Für neue Arbeit meiden |
| Appium Windows Driver | Windows-Treiber von Appium | Nutzt intern WinAppDriver, erbt daher dieselben Einschränkungen9 | Nur, wenn Sie bereits Appium-Bestände haben |
| Coded UI Tests | Visual-Studio-Funktion | Veraltet in VS 2019, entfernt in VS 202610 | Migrationsziel |
3.1 FlaUI — aktuell der praktischste dünne Wrapper um UIA
FlaUI ist eine .NET-Bibliothek, die UI-Automatisierungstest für Windows-Apps (Win32 / WinForms / WPF / Store-Apps) unterstützt, entworfen als Wrapper um Microsofts native UIA-Bibliotheken.4 Der Paketaufbau trennt die gemeinsamen Teile in FlaUI.Core, wobei FlaUI.UIA2 / FlaUI.UIA3 je nach gewünschter UIA-Implementierung gewählt wird.
Das FAQ von FlaUI erklärt genau, wie man zwischen UIA2 und UIA3 wählt: UIA2 ist eine reine .NET-Implementierung, die neuere Funktionen wie Touch nicht unterstützt und bei WPF- oder Store-Apps schlechter funktioniert; UIA3 ist die neuere Implementierung, ideal für WPF-/Store-Apps, kann aber bei WinForms-Apps auf Probleme stoßen, die UIA2 nicht hat.4 Mit anderen Worten, die praktische Faustregel lautet: UIA3 für WPF und UIA2 für WinForms versuchen, und dasjenige verwenden, das sich gegen die tatsächliche App als stabil erweist. Beide über NuGet einbinden und zwischen ihnen wechseln zu können, ist eine der Annehmlichkeiten daran, dass FlaUI ein dünner Wrapper ist.
FlaUI enthält kein eigenes Testframework, weshalb Sie es mit xUnit / NUnit / MSTest kombinieren und als gewöhnliches Testprojekt schreiben. Auf demselben Test-Runner und derselben CI-Pipeline zu laufen wie Ihre Unit-Tests, zahlt sich im Tagesbetrieb erheblich aus.
3.2 WinAppDriver — ehrlich gesagt, effektiv zum Stillstand gekommen
WinAppDriver ist ein UI-Test-Server von Microsoft, der es erlaubt, Windows-Apps über dasselbe WebDriver-Protokoll zu steuern, das auch Selenium verwendet, und wurde eine Zeit lang als Standardwahl behandelt. Als die Coded UI Tests von Visual Studio als veraltet markiert wurden, verwies Microsoft selbst auf „Selenium für Web, Appium + WinAppDriver für Desktop und UWP“ als Migrationspfad.10
Doch ein Blick auf GitHub heute zeigt, dass die letzte stabile Veröffentlichung, v1.2.1, im November 2020 erschien, ohne stabile Veröffentlichung seither. v1.3 bleibt als nie final gewordenes Release Candidate (v1.2.99) von Juli 2020 stecken, und es gibt inzwischen mehr als 1.100 offene Issues.5 Noch beunruhigender ist, dass das GitHub-Repository nur Dokumentation, Beispiele und den Issue-Tracker enthält — der Quellcode des Servers selbst wurde nie veröffentlicht. Das bedeutet, die Community kann nicht einmal Fehler darin beheben. Unser Urteil ist, dass „es ist offiziell, also ist es sicher“ 2026 kein ausreichender Grund für eine Neueinführung ist. Haben Sie bereits Testbestände, die auf WinAppDriver laufen, müssen Sie sie nicht sofort wegwerfen, doch stoppen Sie den Ausbau darauf, und lenken Sie neue Testabläufe stattdessen zu FlaUI.
3.3 Appium Windows Driver und andere Optionen
Appium Windows Driver (appium-windows-driver) ist Appiums Treiber zur Bedienung von Windows-Apps, doch seine tatsächliche Implementierung ist „eine Schnittstelle zu dem von Microsoft bereitgestellten WinAppDriver“, wobei die gesamte Schwerarbeit von WinAppDriver selbst erledigt wird.9 Er erbt damit den Stillstand von WinAppDriver vollständig. Er bleibt eine Option für Teams, die sich über Mobile und Web hinweg auf Appium standardisiert haben, oder die Testcode in Java oder Python schreiben möchten — doch selbst dann sollten Sie ihn erst einführen, nachdem Sie die geerbten Einschränkungen und die begrenzte Zukunft von WinAppDriver verstanden haben.
WinUI-3-Apps (Windows App SDK) unterstützen ebenfalls UIA und können daher mit FlaUI (UIA3) getestet werden. Allerdings gibt es weniger angesammelte Praxiserfahrung im Vergleich zu WinForms/WPF, was eine vorherige Verifikation mit inspect-artigen Werkzeugen umso wichtiger macht, angesichts der Eigenheiten, wie einzelne Steuerelemente offengelegt werden. Zur Wahl des UI-Frameworks selbst siehe „Wie man zwischen WinForms, WPF und WinUI wählt — Eine praxisnahe Entscheidungstabelle“.
4. Eine minimale FlaUI-Implementierung — starten, finden, bedienen, überprüfen
Genug Theorie — schauen wir uns funktionierenden Code an. Dies ist ein gewöhnliches Testprojekt mit FlaUI.UIA3 (oder FlaUI.UIA2, falls sich WinForms als instabil erweist) und xUnit, aus NuGet eingebunden.
4.1 Die Grundform eines Smoke-Tests
Ein Test für den Ablauf „App starten, Auftragserfassungsdialog öffnen, einen Datensatz speichern und bestätigen, dass das Ergebnis in der Statusleiste erscheint“ sieht so aus:
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Tools;
using FlaUI.UIA3;
using Xunit;
public class OrderSmokeTest
{
[Fact]
public void OrderEntry_MainFlowWorks()
{
using var app = Application.Launch(@"C:\App\OrderManager.exe");
using var automation = new UIA3Automation();
try
{
// Wartet, bis das Hauptfenster erscheint
var window = app.GetMainWindow(automation);
// Das Element anhand der AutomationId finden und als Button bedienen
window.FindFirstDescendant(cf => cf.ByAutomationId("NewOrderButton"))
?.AsButton().Invoke();
// Der Dialog öffnet sich asynchron, daher bedingt auf sein Erscheinen warten
var dialog = Retry.WhileNull(
() => window.FindFirstDescendant(
cf => cf.ByAutomationId("OrderDialog"))?.AsWindow(),
timeout: TimeSpan.FromSeconds(5)).Result;
Assert.NotNull(dialog);
// Text über das Value-Muster eingeben (keine Tastatursimulation)
dialog.FindFirstDescendant(cf => cf.ByAutomationId("CustomerNameBox"))
.AsTextBox().Text = "Test Trading Co.";
dialog.FindFirstDescendant(cf => cf.ByAutomationId("SaveButton"))
.AsButton().Invoke();
// Den Abschluss des Speicherns ebenfalls per bedingtem Warten am Status-Label prüfen
var saved = Retry.WhileFalse(
() => window.FindFirstDescendant(
cf => cf.ByAutomationId("StatusLabel"))
?.Name.Contains("Saved") == true,
timeout: TimeSpan.FromSeconds(10));
Assert.True(saved.Success);
}
finally
{
app.Close();
}
}
}
Es gibt hier nur vier Bausteine.
- Starten:
Application.Launchstartet den Prozess (Sie können sich auch mitApplication.Attachan eine bereits laufende App anhängen).GetMainWindowwartet, bis das Hauptfenster verfügbar wird. - Finden:
FindFirstDescendant(cf => cf.ByAutomationId(...))ist die Baumsuche.cfist eine Bedingungsfabrik, und Sie können auchByName/ByControlType/And-Bedingungen kombinieren. Wie oben erwähnt, sollte AutomationId Ihr bevorzugter Suchschlüssel sein. - Bedienen: Konvertieren Sie das gefundene Element in einen typisierten Wrapper wie
AsButton()/AsTextBox()/AsComboBox()und bedienen Sie es.Invoke()ist das Invoke-Muster, und die EigenschaftTextist das Value-Muster — die Steuerungsmuster aus Abschnitt 2.2 stehen unmittelbar hinter diesen Aufrufen. - Überprüfen: Bestätigen Sie die in der UI erscheinenden Ergebnisse (Beschriftungen, Zeilenzahlen, Fenstertitel und so weiter). Möchten Sie den Datenbankinhalt direkt prüfen, spricht nichts dagegen, die DB direkt aus dem Testcode zu lesen.
4.2 Mit Retry warten — Sleep zu schreiben ist ein Verlust
Die größte Quelle für Instabilität bei UI-Tests ist das Timing. Bis sich ein Dialog öffnet, bis Daten fertig geladen sind, bis eine Schaltfläche aktiviert wird — die UI ändert sich ständig asynchron, und wenn der Testcode annimmt, „es muss inzwischen bereits angezeigt werden“, erhält man einen Test, der nur auf langsameren Maschinen fehlschlägt.
Das bedeutet nicht, dass Thread.Sleep(3000) einzufügen eine gute Behebung ist — es ist die schlechteste. Auf einer schnellen Maschine verschwendet man Zeit mit Warten auf nichts; auf einer langsamen reicht es nicht und der Test schlägt trotzdem fehl; und über die gesamte Suite hinweg summiert sich die Gesamtausführungszeit immer weiter. Die Antwort ist ein bedingtes Warten — abfragen, bis die Bedingung erfüllt ist, und bei Timeout aufgeben —, und FlaUI stellt genau dafür eine dedizierte Retry-Klasse bereit.4
// Bis zu 5 Sekunden warten, bis es nicht mehr null ist (d. h. das Element erscheint)
var element = Retry.WhileNull(
() => window.FindFirstDescendant(cf => cf.ByAutomationId("ResultGrid")),
timeout: TimeSpan.FromSeconds(5),
interval: TimeSpan.FromMilliseconds(200),
throwOnTimeout: true).Result;
// Warten, bis die Bedingung wahr wird (d. h. die Schaltfläche wird aktiviert)
Retry.WhileFalse(
() => saveButton.IsEnabled,
timeout: TimeSpan.FromSeconds(5),
throwOnTimeout: true);
Retry.WhileNull / WhileFalse / WhileTrue / WhileException und ähnliche Hilfsmethoden werden bereitgestellt, und Sie können Timeout, Abfrageintervall sowie ob bei Timeout eine Ausnahme geworfen wird, angeben.4 Ein Hinweis: Seit Version 2.0 hat FlaUI implizite Wiederholungen bei seinen Find-Methoden entfernt und die Regel eingeführt, dass „wo gewartet wird, der Testcode ausdrücklich angeben muss“.4 FindFirstDescendant betrachtet nur „den Baum in genau diesem Augenblick“, machen Sie es also zur Regel, dass jede Suche nach einem asynchron erscheinenden Element in Retry eingeschlossen sein muss. Diese Idee — eine ausdrückliche Wartebedingung zu bauen, statt Timing mit Sleep zu übertünchen — ist eine allgemeine Faustregel für Windows-Programmierung insgesamt, nicht nur für UI-Tests (siehe „Warum Sie Ereigniswarten gegenüber Sleep(1) unter Windows vorziehen sollten“).
5. Entwurf für Robustheit — Konventionen sowohl auf App- als auch auf Testseite
Die Gründe, warum UI-Tests „wegen zu hoher Pflegelast aufgegeben“ werden, lassen sich im Grunde auf vier Dinge zurückführen: Suchen, die von Anzeigetext abhängen, koordinatenbasierte Klicks, Sleep und ein Mangel an gemeinsamer Struktur. Wir bekämpfen jedes davon mit einer Konvention.
5.1 Immer AutomationId auf App-Seite vergeben
Dies ist der wichtigste Punkt überhaupt. Die Testrobustheit wird, noch bevor es um die Schreibweise des Testcodes geht, dadurch bestimmt, ob die App stabile Bezeichner offenlegt. AutomationId ist genau die dafür vorgesehene Eigenschaft: Die Spezifikation verlangt, dass sie gebietsschemaunabhängig und unter Geschwisterelementen eindeutig ist.6
In WPF wird bei einem Element mit x:Name dieser Name als UIA-seitiger Bezeichner verwendet, sodass jedes bereits benannte Steuerelement ohne zusätzlichen Aufwand testbar ist. Möchten Sie einen explizit setzen — etwa innerhalb einer Datenvorlage —, setzen Sie die angehängte Eigenschaft AutomationProperties.AutomationId.67
<!-- x:Name wird unverändert zum Bezeichner -->
<Button x:Name="SaveButton" Content="Save" Click="OnSave" />
<!-- AutomationProperties.AutomationId explizit setzen, etwa für Vorlageninhalte -->
<Button AutomationProperties.AutomationId="DeleteRowButton"
Content="Delete"
Command="{Binding DeleteCommand}" />
In WinForms wird Control.Name — der im Designer gesetzte Name (der Wechsel von button1 zu saveButton) — zur UIA-seitigen Identifikation verwendet. Nach unserer Erfahrung lassen sich Formulare, bei denen Name nach Konvention vergeben wurde, meist unverändert per AutomationId durchsuchen, doch da die Erscheinung je nach Framework-Generation und Steuerelementtyp variiert, bestätigen Sie die tatsächliche AutomationId immer mit einem inspect-artigen Werkzeug, bevor Sie sie als Suchschlüssel in einem Test verwenden. Beachten Sie auch, dass die UIA-Eigenschaft Name, die zum von Screenreadern vorgelesenen Namen wird, bei vielen Steuerelementen von der Text-Eigenschaft abgeleitet wird, doch bei Steuerelementtypen wie TextBox oder ListView, die Text nicht wiederverwenden, müssen Sie AccessibleName explizit setzen.11 Die Tatsache, dass die Einrichtung der AutomationId nicht nur dem Testen dient, sondern genau dieselbe Arbeit wie die Barrierefreiheitsunterstützung ist, ist ein gutes Argument, um intern Budget zu sichern.
Die Konvention selbst kann einfach sein. Wir fügen den Coding-Standards unserer Kunden diese zwei Zeilen hinzu:
- Jedes auf einem Bildschirm platzierte Steuerelement, das plausibel bedient oder gegen das geprüft werden könnte, sollte einen aussagekräftigen
Name(WinForms) /x:NameoderAutomationProperties.AutomationId(WPF) erhalten - Sobald ein Bezeichner aus einem Test referenziert wird, muss jede Umbenennung zusammen mit der Testseite geschehen (den Bezeichner wie eine öffentliche API behandeln)
5.2 Koordinatenbasierte Klicks verbieten
Operationen der Form „auf Bildschirmkoordinaten (830, 412) klicken“ brechen in dem Moment, in dem sich Fensterposition, Auflösung, DPI-Skalierung, Design oder Schrifteinstellungen ändern. Besonders DPI erzeugt massenhaft Fehlschläge nach dem Muster „lokal bestanden, in CI fehlgeschlagen“, angesichts von Umgebungsunterschieden wie einer Entwicklungsmaschine bei 100 % gegenüber einer CI-Maschine bei 150 % (wie DPI funktioniert, steht in „High-DPI-Unterstützung in WinForms“). Wie wir in Kapitel 2 gesehen haben, ist das Bedienen über UIA-Steuerungsmuster koordinatenunabhängig. FlaUI besitzt zwar APIs für direkte Mausoperationen, doch legen Sie im Voraus fest, dass sie nur für Operationen genutzt werden, die sich nicht über ein Muster ausdrücken lassen — etwa Drag-and-Drop oder eine Zeichenfläche. Und selbst dann berechnen Sie die relative Position aus dem BoundingRectangle des Elements statt aus Bildschirmkoordinaten.
5.3 Struktur mit dem Page-Object-Muster an einer Stelle bündeln
Codieren Sie Suchcode wie FindFirstDescendant(cf => cf.ByAutomationId("CustomerNameBox")) direkt in Testkörper fest, müssen Sie am Ende jeden Test reparieren, wann immer sich das Bildschirmlayout ändert. Die Standardbehebung ist das Page-Object-Muster: eine Klasse pro Bildschirm (oder Dialog) erstellen und Elementsuch- sowie -bedienungslogik darin einschließen.
public sealed class OrderDialogPage
{
private readonly Window _dialog;
public OrderDialogPage(Window dialog) => _dialog = dialog;
// Elementsuchcode existiert nur innerhalb dieser Klasse
private TextBox CustomerName =>
_dialog.FindFirstDescendant(cf => cf.ByAutomationId("CustomerNameBox")).AsTextBox();
private Button Save =>
_dialog.FindFirstDescendant(cf => cf.ByAutomationId("SaveButton")).AsButton();
private Label Status =>
_dialog.FindFirstDescendant(cf => cf.ByAutomationId("StatusLabel")).AsLabel();
// Tests sehen nur in Geschäftsbegriffen ausgedrückte Operationen
public void Register(string customerName)
{
CustomerName.Text = customerName;
Save.Invoke();
// Auch die Elementsuche und Null-Prüfung innerhalb von Retry durchführen.
// Auf Bildschirmen, bei denen das Status-Label nach dem Speichern neu
// erzeugt/gezeichnet wird, ist es normal, dass die Suche kurzzeitig null
// liefert oder wirft (ohne ignoreException würde die erste Ausnahme den
// Aufruf sofort scheitern lassen)
Retry.WhileFalse(() => Status?.Name.Contains("Saved") == true,
timeout: TimeSpan.FromSeconds(10), throwOnTimeout: true,
ignoreException: true);
}
}
Der Testkörper lässt sich jetzt in Geschäftsbegriffen als new OrderDialogPage(dialog).Register("Test Trading Co.") schreiben, und die Auswirkung einer Bildschirmänderung beschränkt sich auf eine einzige Korrektur im Page Object. Wollen Sie UI-Tests dauerhaft für eine App mit mehr als etwa 10 Bildschirmen betreiben, ist dieses Muster effektiv unverzichtbar. Ein Vorbehalt: Suchen Sie jedes von einer Wartebedingung referenzierte Element immer jedes Mal erneut über eine Eigenschaft, wie oben bei Status. Cachen Sie das Suchergebnis in einem Feld, halten Sie am Ende ein veraltetes Element fest, das beim Neuzeichnen verschwunden ist, und warten bis zum Timeout.
5.4 Testunabhängigkeit — keinen Zustand zwischen Tests weitergeben
Halten Sie jeden UI-Test unabhängig. Erzeugen Sie Reihenfolgeabhängigkeiten wie „Test 3 setzt Daten voraus, die von Test 2 erzeugt wurden“, kaskadiert ein einziger Fehlschlag, und Sie verlieren auch die Möglichkeit, Tests neu zu ordnen. Die Prinzipien sind:
- Jeder Test (oder jede Testklasse) startet die App selbst und schließt sie zuverlässig ab (über
finallyoder eineIDisposable-Fixture) - Der Test selbst richtet alle nötigen Vorbedingungsdaten ein. Der App eine Startoption zu geben, die es Tests erlaubt, testspezifische Konfigurationsdateien/Datenbanken einzuschieben, macht dies erheblich einfacher
- Die Aufräumarbeit im Fehlerfall nicht vergessen. Verbliebene Prozesse oder hängengebliebene modale Dialoge eines vorherigen Fehlschlags werden zur Ursache des nächsten Testfehlschlags (Kapitel 7)
6. Wie weit man mit UI-Tests gehen sollte — eine auf Smoke-Tests zentrierte Grenze
Sobald das Werkzeug steht, ist es verlockend, jeden Bildschirm testen zu wollen — doch das ist genau der Wendepunkt. UI-Tests sind Größenordnungen langsamer als Unit-Tests (Sekunden bis mehrere Zehn Sekunden pro Stück), zerbrechlicher (sie müssen mit jeder UI-Änderung aktuell gehalten werden) und langsamer zu diagnostizieren, wenn sie fehlschlagen (ist es ein App-Bug, ein Test-Bug oder die Umgebung?). Genau diese Kostenstruktur ist der Grund, warum die Spitze der Testpyramide schmal gezeichnet wird — einen UI-Test zu verwenden, um etwas abzusichern, das die darunterliegenden Schichten bereits absichern können, ist immer ein Nettoverlust.
In eine Entscheidungstabelle gebracht, sieht das so aus:
| Was Sie absichern wollen | Passende Schicht | Grund |
|---|---|---|
| Berechnungen, Umwandlungen, Geschäftsregeln | Unit-Tests | Schnell und stabil. Sie über die UI abzudecken, ist von vornherein aussichtslos |
| DB-Zugriff, Datei-E/A, externe Anbindung | Integrationstests | Das Reale ohne UI-Bedarf nutzen (siehe wo die Grenze verläuft) |
| ViewModel-/Präsentationslogik | Unit-Tests | Mit MVVM ohne UI testbar |
| Startet, Hauptablauf funktioniert, kann speichern | UI-Smoke-Test | Dies ist das Hauptschlachtfeld für UI-Testen |
| Kritische Abläufe, die bisher manuelle Prüfungen verpasst haben | UI-Test (Regression) | Nur dort hinzufügen, wo tatsächlicher Schaden entstand |
| Bildschirmlayout / visueller Bruch | Visuelle Prüfung / Screenshot-Vergleich | Dies als Assertions zu schreiben, ist Pflegehölle — die Anzahl klein halten |
| Abstürze, Handle-Lecks und andere Fehlzustände | Eine andere Grundlage | Außerhalb des Umfangs von UI-Testen (siehe Application Verifier) |
Der von uns empfohlene Einstieg ist mit „etwa 10 Smoke-Tests“: „es startet und der Hauptbildschirm erscheint“, „die wichtigsten Stammdaten-Bildschirme lassen sich öffnen“, „ein repräsentatives Dokument lässt sich anlegen, suchen und in der Druckvorschau anzeigen“, „beim Beenden erscheint kein Fehler“. Automatisieren Sie die zehn wichtigsten Abläufe, die Sie zuvor immer vor jeder Veröffentlichung von Hand bestätigt haben. In diesem Umfang dauert das Schreiben nur ein bis zwei Wochen, nächtliche Läufe passen in 20–30 Minuten, und die Pflegelast bleibt realistisch. Sobald Sie den Nutzen spüren, fügen Sie Regressionstests einzeln für Regressions-Bugs hinzu, die tatsächlich echten Schaden verursacht haben. Ein Plan, der auf „jeden Bildschirm, jedes Feld“ abzielt, bricht dagegen fast sicher unterwegs zusammen.
Eine weitere wichtige Investitionsrichtung ist, Logik aus der UI herauszuziehen, statt weitere UI-Tests hinzuzufügen. Ein Bildschirm, bei dem Geschäftslogik direkt in Ereignishandlern geschrieben ist, lässt sich nur durch einen UI-Test absichern, doch verschieben Sie diese Logik in ein ViewModel oder eine Dienstklasse, wird sie durch Unit-Tests absicherbar, und der UI-Test muss nur noch die „Verdrahtung“ bestätigen. Stellen Sie fest, dass Sie eine große Anzahl von UI-Tests benötigen, ist das unserer Erfahrung nach oft ein Design- statt ein Testproblem. Zur Frage, wie man Tests insgesamt schichtet, siehe „Wo die Grenze zwischen Unit-Tests und Integrationstests verläuft“, und für die Praxis der unteren Testschichten siehe „Mindestanforderungen für einen benutzerdefinierten Logger und eine Checkliste für Integrationstests“.
7. Fallstricke bei CI und unbeaufsichtigter Ausführung — UI-Tests laufen nicht ohne Desktop
UI-Tests, die Sie von Hand geschrieben haben, auf der eigenen Maschine laufen zu lassen, ist entspannt. Die Fallstricke konzentrieren sich in der Stufe „jede Nacht unbeaufsichtigt in CI ausführen“, und anders als bei Web-UI-Tests (die in einem headless Browser vollständig durchlaufen) liegt die Wurzel von allem darin, dass Desktop-App-Tests eine echte, interaktive Desktop-Sitzung verlangen.
7.1 Eine interaktive Sitzung ist erforderlich — es läuft nicht auf einem als Dienst gestarteten Agenten
CI-Agenten (Azure-Pipelines-Agenten, GitHub-Actions-Self-Hosted-Runner usw.) werden normalerweise als Windows-Dienste resident gehalten. Doch ein Dienst besitzt keinen Benutzer-Desktop, sodass Sie von einer aus ihm gestarteten App keine Fenster bedienen können. Die eigene Dokumentation von Azure Pipelines erklärt unmissverständlich, dass ein Agent, der UI-Tests für eine Desktop-App ausführt, als interaktiver Prozess mit aktivierter automatischer Anmeldung (Autologon) konfiguriert werden muss — nicht als Dienst.8 Sie merkt außerdem an, dass von Microsoft gehostete Agenten (die von der Cloud bereitgestellten gemeinsamen Runner) Tests mit sichtbarer UI überhaupt nicht unterstützen — dort laufen nur headless Browsertests.8 Mit anderen Worten: eine selbst gehostete Maschine (physisch oder VM) ist für UI-Testen einer Desktop-App effektiv zwingend.
Das offiziell dokumentierte Sicherheitsrisiko einer Autologon-Konfiguration ist, dass „jeder mit physischem Zugriff auf diese Maschine das automatisch angemeldete Konto nutzen kann“.8 Die Voraussetzung ist ein dediziertes Testkonto auf einer dedizierten Maschine (VM), auf der keine Produktionsanmeldedaten gespeichert sind.
7.2 Bildschirmsperren, RDP-Trennungen, Auflösung — die klassischen Bruchweisen
Selbst nach der Einrichtung einer interaktiven Sitzung bleiben Fallstricke. Hier eine Tabelle mit Symptomen und Gegenmaßnahmen.
| Fallstrick | Symptom | Gegenmaßnahme |
|---|---|---|
| Agent als Dienst gestartet | Überhaupt keine Elemente gefunden, oder die App startet nie | Als interaktiven Prozess + Autologon neu konfigurieren8 |
| Bildschirmsperre / Bildschirmschoner | Eingabeoperationen erreichen die App nicht und schlagen fehl | Den Bildschirmschoner als Teil der Autologon-Einrichtung deaktivieren. Eine GPO-Ausnahme für Richtlinien beantragen, die eine Sperre auslösen8 |
| RDP über die „X“-Schaltfläche trennen | Die Sitzung sperrt sich im Moment der Trennung, und jeder nachfolgende Test schlägt fehl | Vor dem Trennen tscon <Sitzungs-ID> /dest:console ausführen, um die Sitzung zur Konsole zurückzuholen8 |
| Umgebungsunterschiede bei Auflösung/DPI | Lokal bestandene Tests schlagen nur in CI fehl | Die Auflösung fixieren (Azure Pipelines besitzt dafür eine Aufgabe). Skalierung einheitlich auf 100 % setzen8 |
| Tests parallel ausführen | Konkurrenz um Maus/Tastatur/Fokus beeinträchtigt sich gegenseitig | UI-Tests einzeln, seriell, pro Maschine ausführen. Stattdessen durch Hinzufügen weiterer Maschinen (VMs) parallelisieren |
| Überreste eines vorherigen Fehlschlags | Ein hängengebliebener Prozess oder modaler Dialog blockiert den nächsten Start | Zielprozesse vor Teststart bereinigen. Teardown immer in einem finally ausführen |
Die RDP-Falle tappt man besonders leicht hinein, daher etwas mehr Detail: Verbinden Sie sich per Remote auf die Testmaschine, um Anpassungen vorzunehmen, schließen Sie das Fenster und melden Sie sich ab, endet diese Sitzung gesperrt, und jeder nachfolgende UI-Test schlägt weiter fehl. Der von der offiziellen Dokumentation empfohlene Workaround ist, vor dem Trennen %windir%\System32\tscon.exe <ID> /dest:console von einer Administrator-Eingabeaufforderung aus auszuführen, um die Sitzung zur Konsole zurückzuholen.8 Stellen Sie sicher, dass dies im Betriebshandbuch Ihrer Testmaschine steht.
Auch DPI und Auflösung verdienen Vorsicht. Es ist nicht ungewöhnlich, dass eine CI-VM bei 1024x768 mit Standardskalierung belassen wird, was das Layout verändert — Sie können auf Unterschiede stoßen wie „eine auf der Entwicklungsmaschine sichtbare Schaltfläche erfordert jetzt Bildlauf, um sie zu sehen“. Das Eliminieren koordinatenbasierter Klicks fängt das meiste davon auf, doch es ist trotzdem besser, die Umgebung zu fixieren. Dieselben Prüfpunkte aus „High-DPI-Unterstützung in WinForms“ gelten hier unmittelbar, einschließlich des DPI-Bewusstseinsstatus der App selbst.
7.3 Nachweise im Fehlerfall aufbewahren — Screenshots und Protokolle
Schlägt ein unbeaufsichtigter UI-Test fehl, sagt ein Protokoll, das nur „Element nicht gefunden“ meldet, nichts über die Ursache aus. Bauen Sie von Anfang an ein, bei einem Fehlschlag einen Screenshot zu speichern. FlaUI besitzt Funktionalität zum Erfassen des Bildschirms oder eines Elements, rufen Sie diese also aus dem Fehler-Hook Ihres Testframeworks auf und speichern Sie sie als CI-Artefakt.
// Aus einem Fehler-Hook o. Ä. aufrufen. Ausgabe in das CI-Artefaktverzeichnis.
FlaUI.Core.Capturing.Capture.Screen()
.ToFile(Path.Combine(artifactDir, $"{testName}_{DateTime.Now:HHmmss}.png"));
Stellen Sie zusätzlich zu Screenshots sicher, dass sich das eigene Protokoll der App (wie weit die Verarbeitung kam) und das Protokoll des Tests (welche Operationen erfolgreich waren) per Zeitstempel querverweisen lassen — das macht die Trennung von „App-Bug, Test-Bug oder Umgebung“ drastisch schneller. Hinweise zur nächtlichen Testausführung über die Aufgabenplanung (Sitzungstypen, Aufgaben, die mit 0x1 enden, und Ähnliches) finden Sie in „Wenn Aufgabenplanungs-Aufgaben nicht laufen oder mit 0x1 beendet werden“.
8. Zusammenfassung
UI-Automatisierungstest ist nicht etwas, das „einfach funktioniert, sobald man ein Werkzeug installiert“ — er läuft nur weiter, wenn ein Verständnis des Mechanismus, Mitwirkung auf App-Seite, eine klare Grenze für den Umfang und eine gut entworfene Ausführungsumgebung alle zusammen vorhanden sind. Zusammengefasst als Kernpunkte:
- Die Grundlage ist UI Automation. Elemente per AutomationId im Baum finden, über Steuerungsmuster bedienen. Beginnen Sie mit einer Bestandsaufnahme, wie die Ziel-App mit inspect / Accessibility Insights aussieht
- Das Werkzeug ist FlaUI + xUnit / NUnit. WinAppDriver hat seit 2020 keine stabile Veröffentlichung mehr — für neue Einführung meiden
- Robustheit entsteht durch Konvention. AutomationId auf App-Seite vergeben, koordinatenbasierte Klicks verbieten, Sleep zugunsten bedingten Wartens über Retry verbieten, und Struktur mit Page Object bündeln
- Umfang von etwa 10 Smoke-Tests aus. Logik in Unit- und Integrationstests hinunterdrücken, und UI-Tests rein darauf beschränken, zu bestätigen, dass „der Hauptablauf funktioniert“
- Die Grundform von CI ist eine selbst gehostete Maschine + eine interaktive Sitzung + Autologon. Die Fallstricke von Bildschirmsperren, RDP-Trennungen und Auflösungsunterschieden über Betriebsverfahren schließen, und im Fehlerfall immer einen Screenshot aufbewahren
„Zwei Tage manuelle Prüfung vor jeder Veröffentlichung“ lässt sich realistisch, zu vertretbaren Kosten, durch angemessen bemessene UI-Automatisierungstests ersetzen. Hat Ihre App umgekehrt derzeit nirgends eine AutomationId vergeben, oder ist Logik direkt in Bildschirmereignisse geschrieben, ist es letztlich schneller, zuerst mit kleinen Nachbesserungen auf App-Seite zu beginnen, bevor überhaupt ein Test geschrieben wird. Wir können ab der ersten Bestandsaufnahme des aktuellen Zustands Ihrer App helfen, einschließlich wo man anfängt und wie man eine Smoke-Test-Suite aufbaut.
Verwandte Artikel
- Wo die Grenze zwischen Unit-Tests und Integrationstests verläuft
- Eine Windows-Grundlage für Fehlzustandstests mit Application Verifier aufbauen
- Mindestanforderungen für einen benutzerdefinierten Logger und eine Checkliste für Integrationstests
- PowerShell-Tests mit Pester pflegen — ein praktisches Muster, um Betriebsskripte widerstandsfähiger zu machen
- High-DPI-Unterstützung in WinForms — Warum die UI auf 4K-Monitoren verschwimmt oder zerbricht, und wie Sie das praktisch beheben
Verwandte Beratungsbereiche
Die Komura Soft LLC übernimmt die Einführung von UI-Automatisierungstest für WinForms-/WPF-Anwendungen (Bestandsaufnahme, AutomationId-Nachbesserung, Aufbau einer vollständigen Smoke-Test-Suite, CI-Umgebungsentwurf), die Migration bestehender Testbestände zu FlaUI sowie Design-Reviews der gesamten Teststrategie.
- Technische Beratung / Design-Review
- Windows-App-Entwicklung
- Nutzung von Altbeständen / Migrationsunterstützung
- Kontakt
Referenzlinks
-
Microsoft Learn, UI Automation Overview. Zum im Desktop verwurzelten Automatisierungsbaum, den Rohansicht-/Steuerungsansicht-/Inhaltsansicht-Filtern, Elementeigenschaften und Steuerungsmustern, sowie dazu, wie assistive Technologie und Testautomatisierung dieselbe Grundlage teilen. ↩ ↩2 ↩3
-
Microsoft Learn, UI Automation Control Patterns Overview. Zum Entwurf von Steuerungsmustern (die Analogie zu COM-Schnittstellen), zu Mustern wie Invoke / Value / SelectionItem, und dazu, wie ein einzelnes Steuerelement mehrere Muster implementieren kann. ↩ ↩2
-
Microsoft Learn, Accessibility tools - Inspect. Dazu, dass inspect.exe dem Windows SDK beiliegt und UIA-Eigenschaften und -Muster anzeigen kann, sowie dazu, dass es als Legacy-Werkzeug positioniert ist, wobei Accessibility Insights stattdessen empfohlen wird. ↩ ↩2 ↩3
-
GitHub, FlaUI/FlaUI. Dazu, dass es ein UIA-Wrapper ist, der Win32 / WinForms / WPF / Store-Apps unterstützt, zum Paketaufbau FlaUI.Core / UIA2 / UIA3, zur UIA2-gegen-UIA3-Anleitung im FAQ, zu seiner MIT-Lizenz, zur v5.0.0-Veröffentlichung (Februar 2025) und laufenden Entwicklung, zum Retry-Hilfswerkzeug, und zur Entfernung impliziter Wiederholungen bei Find-Methoden seit 2.0. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
GitHub, microsoft/WinAppDriver. Dazu, dass die letzte stabile Veröffentlichung v1.2.1 im November 2020 erschien, dass v1.3 ein nie veröffentlichtes Release Candidate von Juli 2020 bleibt, zu mehr als 1.100 offenen Issues, und dazu, dass das Repository überwiegend Dokumentation und Beispiele enthält, wobei der Quellcode des Servers selbst nicht veröffentlicht wurde. ↩ ↩2 ↩3
-
Microsoft Learn, Use the AutomationID Property. Dazu, dass AutomationId ein gebietsschemaunabhängiger Bezeichner ist, unter Geschwisterelementen eindeutig sein muss, als Suchschlüssel in Testskripten verwendet wird, sowie dazu, dass Steuerelemente in WPF ohne ID (x:Name) oder x:Uid AutomationId nicht unterstützen. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, AutomationProperties.AutomationId Attached Property. Zur Definition der angehängten Eigenschaft im Namensraum System.Windows.Automation zum Setzen einer Zeichenkette, die ein Element in WPF eindeutig identifiziert. ↩ ↩2
-
Microsoft Learn, UI testing considerations (Azure Pipelines). Dazu, dass UI-Testen einer Desktop-App einen als interaktiven Prozess mit aktiviertem Autologon konfigurierten Agenten erfordert, dazu, dass von Microsoft gehostete Agenten Tests mit sichtbarer UI nicht unterstützen, zur Sperre durch eine RDP-Trennung und dem tscon-Workaround, zur Aufgabe zur Konfiguration der Bildschirmauflösung, sowie zum Sammeln von Screenshots/Video im Fehlerfall. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
GitHub, appium/appium-windows-driver. Dazu, dass Appium Windows Driver eine Schnittstelle zu Microsofts WinAppDriver ist, sowie zur Warnung im README, dass der WinAppDriver-Server seit langem ohne Pflege ist. ↩ ↩2
-
Microsoft Learn, Use Coded UI tests to test your code. Dazu, dass Coded UI Tests veraltet sind, wobei Visual Studio 2019 die letzte Version mit vollständiger Unterstützung ist, und dazu, dass Appium + WinAppDriver der empfohlene Migrationspfad für Desktop-/UWP-Apps war (die Entfernung in VS 2026 ist in demselben Learn-Migrationsleitfaden dokumentiert). ↩ ↩2
-
Microsoft Learn, WinForms: Setting the accessible name on a control. Dazu, wie die UIA-Eigenschaft Name für viele WinForms-Steuerelementtypen von der Text-Eigenschaft abgeleitet wird, und dazu, dass Steuerelemente wie TextBox / ListBox das explizite Setzen von AccessibleName erfordern. ↩
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
CI/CD für WinForms-/WPF-Anwendungen in der Praxis ── Vom Build über die Signierung bis zur Distribution mit GitHub Actions automatisieren
Ein praktischer Leitfaden zur Einrichtung von CI/CD für WinForms-/WPF-Anwendungen mit GitHub Actions. Behandelt eine minimale YAML für Bu...
Entra-ID-Authentifizierung in WinForms/WPF-Apps integrieren — Eine praxistaugliche Architektur mit MSAL.NET und dem WAM-Broker
Ein praxisnaher Blick auf die Integration der Entra-ID-Authentifizierung (früher Azure AD) in WinForms/WPF-Desktop-Apps: das Public-Clien...
Infobereich-Symbole und Toast-Benachrichtigungen in Windows-Apps — Fallstricke von NotifyIcon und die richtige AppNotification-Wahl
Ein praktischer Leitfaden dafür, eine geschäftliche Windows-Anwendung im Infobereich (System Tray) resident zu halten und den Benutzer üb...
Datum, Uhrzeit und Zeitzonen in Business-Apps — Von den Fallstricken von DateTime über das UTC-Speicherprinzip bis zum Testdesign
Zeitstempel verschieben sich nach einer Servermigration um neun Stunden; nur beim Auslandsbüro springt das Datum auf den Vortag zurück – ...
High-DPI-Unterstützung in WPF — Warum es trotz „eigentlich DPI-bewusst“ immer noch verschwimmt und durchblutet, und wie Sie es beheben
WPF layoutet die UI in DIPs (1/96 Zoll) und ist von Anfang an System DPI Aware, doch das Verschieben eines Fensters auf einen Monitor mit...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
UI-Threading und Timer
WPF-/WinForms-UI-Thread, asynchrone Abläufe, Dispatcher und Timer-Entscheidungen.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Was ist das beste Werkzeug, um UI-Tests von WinForms- und WPF-Apps zu automatisieren?
- Die empfohlene Werkzeugkombination ist FlaUI zusammen mit einem gewöhnlichen Testframework wie xUnit oder NUnit. FlaUI ist eine MIT-lizenzierte Open-Source-Bibliothek, die Windows UI Automation dünn umschließt, sowohl UIA2 als auch UIA3 unterstützt (grob: UIA3 für WPF, UIA2 für WinForms versuchen) und aktiv gepflegt wird, mit v5.0.0 veröffentlicht im Februar 2025. WinAppDriver, einst Microsofts offizielle Wahl, hatte seit v1.2.1 im November 2020 keine stabile Veröffentlichung mehr, hat über 1.100 offene Issues, und der Quellcode des Servers wurde nie veröffentlicht — wir empfehlen es daher nicht für neue Arbeit; Coded UI Tests wurden in VS 2019 als veraltet markiert und in VS 2026 entfernt.
- Wie finden und klicken UI-Automatisierungstests Steuerelemente, ohne Bildschirmkoordinaten zu verwenden?
- Sie setzen auf Windows UI Automation (UIA), dieselbe Plattform, die auch Screenreader nutzen. Jedes Fenster und Steuerelement wird als Element in einem im Desktop verwurzelten Baum offengelegt; Tests finden Elemente anhand von Eigenschaften — wobei AutomationId der bevorzugte Suchschlüssel ist, da er als gebietsschemaunabhängig und unter Geschwisterelementen eindeutig definiert ist — und bedienen sie über Steuerungsmuster wie Invoke (klicken), Value (Text setzen) und SelectionItem. Da der Test die vom Steuerelement selbst offengelegte Operation aufruft, statt Mausereignisse an Koordinaten zu senden, bleibt er unabhängig von Fensterposition oder DPI stabil. Prüfen Sie die App vor dem Schreiben von Code mit inspect.exe oder Accessibility Insights for Windows, um zu sehen, was tatsächlich offengelegt wird.
- Wie viele UI-Tests sollten wir schreiben, und was sollten sie abdecken?
- Beginnen Sie mit etwa 10 Smoke-Tests, die die wichtigsten Abläufe abdecken, die Sie zuvor vor jeder Veröffentlichung von Hand geprüft haben: die App startet und der Hauptbildschirm erscheint, wichtige Stammdaten-Bildschirme öffnen sich, ein repräsentatives Dokument lässt sich anlegen und suchen, und beim Beenden erscheint kein Fehler. UI-Tests stehen an der Spitze der Testpyramide — sie sind langsam, zerbrechlich und aufwendig zu diagnostizieren —, sichern Sie Berechnungen, Geschäftsregeln und Datenzugriff daher stattdessen mit Unit- und Integrationstests ab. Pläne, die auf jeden Bildschirm und jedes Feld abzielen, brechen fast immer unter der Pflegelast zusammen; wenn Sie viele UI-Tests benötigen, ist das oft ein Designproblem, und das Verlagern von Logik in ViewModels oder Dienste macht sie unit-testbar.
- Warum bestehen meine UI-Tests lokal, schlagen aber in CI fehl?
- Desktop-UI-Tests benötigen eine echte interaktive Desktop-Sitzung, anders als headless Browsertests. Ein als Windows-Dienst laufender CI-Agent besitzt keinen Benutzer-Desktop, sodass die Elementsuche gänzlich fehlschlägt — der Agent muss auf einer selbst gehosteten Maschine als interaktiver Prozess mit automatischer Anmeldung (Autologon) konfiguriert werden, da von Microsoft gehostete Agenten sichtbare UI-Tests überhaupt nicht unterstützen. Weitere klassische Ursachen sind Bildschirmsperren und Bildschirmschoner, das Trennen von RDP über die X-Schaltfläche (was die Sitzung sperrt — verwenden Sie stattdessen tscon, um sie zur Konsole zurückzuholen), sowie Unterschiede in Auflösung oder DPI zwischen Entwicklungsmaschine und CI-VM. Speichern Sie bei einem Fehlschlag immer einen Screenshot, damit Sie App-Bugs, Test-Bugs und Umgebungsprobleme unterscheiden können.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.