WPF Test Automation and WinForms UI Testing Without Code (and Without an Accessibility Tree)
WPF test automation and WinForms UI testing without code: what your app exposes to a test tool, what it hides, and how to record, check and run a test on every build.
10 min readBy Ait Nohaila, Business Development Representative
The short answer
To automate UI tests for a WPF application, record a workflow once in GUI Test Studio: every click, key and check becomes a test that finds each control through the app's accessibility information, or by sight when it says nothing.
This guide is for a QA engineer or a developer with a WPF or Windows Forms (WinForms) application to test.It shows what such an app tells a test tool and what it hides, records a test step by step, picks the checks that survive a redesign, runs the test on every build, and says when a code-first library is still the better choice.
What a WPF or WinForms app exposes to a test tool, and what it hides
WPF and WinForms both describe their standard controls to Windows through UI Automation: a type, a name, a place on the screen and, when the developer set one, an AutomationId.A screen reader uses that description, and so does every test tool that finds controls through locators.The same app also holds controls that say nothing at all.
| Kind of control | What UI Automation sees | How a test finds it |
|---|---|---|
| A standard WPF control with an AutomationId: Button, TextBox, ListView, DataGrid | Its type, its name, its place, and the id the developer gave it | Through what the app says, at once |
| A standard WinForms control: Button, TextBox, DataGridView | Its type, its text and the control's Name as its id, when it was set | Through what the app says |
| A WinForms app whose controls kept their default names | Types and texts, with ids such as button1 that mean little | Through the control's text and place, and by sight |
| A control the app paints itself: a chart, a gauge, a ribbon, a canvas | One rectangle, unless its developers wrote its accessibility | By sight |
| An owner-drawn grid or list | Rows without their cells, or cells behind placeholder names | By sight, cell by cell |
| A third-party control suite | It depends on the vendor: from a full description to one pane | Through what the app says where it does, by sight where it does not |
| A picture, an icon, a drawing | An image with no text | By sight |
Microsoft's own documentation says that a control without a UI Automation provider is largely opaque to it, which can report little more than its location.Which Windows apps have no accessibility tree lists the kinds of application where this is the rule rather than the exception.
Automated testing of a WPF application: record it step by step
Automated testing of a WPF application starts with one recording, in GUI Test Runner-Recorder.Nothing is added to the app: no test build, no library, no AutomationId you did not already have.The example below records an order-entry app, and a WinForms app is recorded the same way.See Record a test for every option.
- 1
Click Record
Open GUI Test Runner-Recorder and click Record.Put your app in the state the test should start from: the first screen of the workflow, with nothing else open on it.
- 2
Answer "What do you want to test?" with An app
Pick An app and choose your program among the running ones, or browse to its .exe if it is not open yet.Tick the box that starts the app with the test and closes it at the end, so every run begins from a fresh app.
The whole screen is always recorded.Choosing An app only decides where the automatic checks come from. - 3
Use the app as a tester would
Type the customer's name, pick a product in the list, click Save, and read the confirmation.Every click and key is recorded, and the recorder cuts a picture of each control you click, just before you click it.Press the Stop key when the workflow is done.
- 4
Answer "What should this test verify?"
Three cards ask what the test should check: the visual elements you clicked, the text on the screen, and what appeared while you worked, such as a dialog or a confirmation.Keep the defaults for a first test, and save.
The checks are chosen after the recording, when you have just seen what the app did. - 5
The test is saved in a folder of its own, and runs
A test named Orders is saved as
Orders\Orders.GTS, in a folder of its own, where every run's results and history are kept beside it.Click Run Test: the test replays your clicks and keys as real input, aimed at where each control stands now, and ends in a Confidence Report that says what passed and what changed.
What the recording kept about each control decides how it is found on the next run.
- For the Save button, the app said Button, Save and SaveButton, and the test keeps that beside the button's picture.
- For the chart, the app said nothing worth keeping, and the picture alone finds it.
You can also skip the recording and describe the workflow in a prompt to Claude, ChatGPT or a local AI model, one we provide or one you bring: the test is created the same way, with the same checks.
WinForms UI testing: the checks that survive a redesign
WinForms UI testing, like WPF testing, lives or dies on checks that catch a real change and ignore the harmless ones.GUI Test Studio offers three kinds, each a block on the test's timeline that anyone on the team can add, move or edit.
Visual Assert
A Visual Assert checks that an element is on the screen and looks as recorded: a toolbar icon, a status light, a chart that must show the same curve.
Text Assert
A Text Assert checks the words an element shows, asking the app first and reading the screen when the app cannot tell, and a Dynamic Text Assert accepts a value that changes by its shape, such as an order number.
Layout Assert
A Layout Assert checks that the elements of a screen are arranged as recorded, so a column that moved or a button that lost its place is caught even when each element still looks right.
All three ignore the noise every screen has and still catch what a person would call a change: a wrong colour, a missing icon, a word that changed case, a control that slid out of place.Visual checks goes through the settings of each one.
The Search Scope
Every check looks for its element inside a Search Scope, an area around where the element was recorded.The scope keeps look-alikes apart: a Save button in the toolbar and a Save button in a dialog are told apart by where each one stands.A check that has not found its element in its scope after a second looks over the whole screen too, so a window that opened further to the right does not fail the test, and the Confidence Report says where the element was found.
A redesign of the code behind a control
A test keeps working through the changes a WPF or WinForms app goes through.
- A control rewritten from WinForms to WPF is still found: the app still names it, and it still looks the same.
- A control moved to another panel is found where it stands now.
- A renamed class or a new control library changes nothing the test relies on.
A dark theme, a high-contrast theme, another language
The app's accessibility information does not change with a theme, so a check that asks the app holds in light and dark alike.The picture a Visual Assert compares does change, and a baseline profile keeps one expected version per environment: run the test with the Dark mode profile, accept each new look once in the Confidence Report, and the same test then checks the light look and the dark look, each against the right version.
A locator breaks when the code changes.A check that asks the app and looks at the screen breaks when the screen is wrong, which is the one failure you want.
Windows desktop test automation on every build
Windows desktop test automation pays off when it runs without anyone starting it. gtr-cli, the command-line runner, runs a test exactly like the Run Test button and answers the way a pipeline expects: a failed test fails the build, a JUnit file fills the test tab, and every picture of what failed is kept in one folder.
gtr-cli run tests --countdown 0 --junit results\junit.xml --artifacts results\artifactsOne option names the CI tool, and the failures then show up in its own log, as annotations or test entries, with no plugin to install:
| CI tool | The command | What the tool shows |
|---|---|---|
| TeamCity | gtr-cli run tests --countdown 0 --format teamcity --artifacts results\artifacts | Every test in the Tests tab, the failures with their reasons |
| Azure DevOps | gtr-cli run tests --countdown 0 --format azure --junit results\junit.xml --artifacts results\artifacts | A logging command per failure, and the JUnit file in the Tests tab |
| GitHub Actions | gtr-cli run tests --countdown 0 --format github --junit results\junit.xml --artifacts results\artifacts | An annotation per failure on the run, and the artifacts folder to download |
A desktop test replays on a real screen, so the build agent needs an unlocked Windows desktop session: a virtual machine with a fixed screen size works well.Run tests in your pipeline lists every option, and Run your end-to-end tests on every build sets a TeamCity build up step by step, with the same command for Jenkins, GitHub Actions, GitLab and Azure DevOps.
FlaUI, pywinauto and other WPF UI testing tools: when code is the right choice
Two open-source libraries are what a developer would otherwise reach for.FlaUI is a .NET library for automated UI testing of Windows applications, WPF and WinForms among them, that wraps Microsoft's UI Automation libraries for a test written in C#.pywinauto, on its GitHub page, is a set of Python modules to automate the Windows GUI, with two back ends: the Win32 API, its default, and UI Automation.
Both are a fair choice for a code-first team whose controls are all accessible: the tests live in the app's own repository, in a language the team writes every day, beside the unit tests, and a developer who knows the control tree can write a precise locator for each element.The cost is paid in developer time, for every locator and for every redesign that changes one, and an element the app does not describe needs another way in.
The two are not enemies.A team with a WPF app full of custom controls often keeps its unit tests in code and records its end-to-end tests, so the people who know the workflows best can keep them, and the controls the app does not describe are tested too.
Key takeaways
- 1WPF and WinForms describe their standard controls, and say nothing about a chart, a gauge, an owner-drawn grid or many third-party controls.
- 2One recording tests both kinds: the GTS Engine + AI asks the app first and finds the control by sight when the app says nothing.
- 3Pick checks that survive a redesign: a Visual Assert, a Text Assert and a Layout Assert fail only when the screen is wrong, and a baseline profile covers a dark theme.
- 4Run it on every build with gtr-cli, in TeamCity, Azure DevOps, GitHub Actions, Jenkins or GitLab, on an unlocked Windows desktop.
- 5FlaUI and pywinauto still make sense for a code-first team whose controls are all accessible; the cost is developer time for every locator.
Questions
How do you automate UI tests for a WPF application?
Record a workflow once in GUI Test Studio: open the app, click, type and read what appears, the way a tester would.Every action and every check becomes a test with no code, and gtr-cli runs it again on every build.Each control is found through the app's accessibility information, or by sight when the app says nothing about it.
Can you test a WinForms app that has no AutomationIds?
Yes.A standard WinForms control still reports its type and its text, and a control that reports nothing useful is found by sight, by its picture on the screen.Nothing has to be added to the application for the test to work.
Do I need to add AutomationIds to my WPF app for test automation?
Not with GUI Test Studio.An AutomationId helps, because the app then names the control precisely, and GUI Test Studio keeps it when there is one.Without it, the control's name, type and place, and its picture, are enough.
How do you test custom-drawn or third-party controls in a WPF app?
By sight.A chart, a gauge, an owner-drawn grid or a third-party control often shows Windows one empty rectangle.GUI Test Studio finds such a control by its picture in its Search Scope, acts on it with real mouse and keyboard input, and checks what it shows the same way.
What is a FlaUI alternative that needs no code?
GUI Test Studio.FlaUI is a .NET library that drives Windows UI Automation from C# code, so every element needs a locator in a test project.GUI Test Studio records the workflow instead, keeps the app's accessibility information by itself, and finds by sight what the app does not describe.
Does WPF test automation work with a dark theme?
Yes.The app's accessibility information does not change with a theme, and for the checks that compare a picture, a baseline profile keeps one expected version per theme.The same test runs against the light look and the dark look, and compares each with the right version.
Can WPF and WinForms tests run in a CI/CD pipeline?
Yes. gtr-cli runs the tests on a Windows build agent with an unlocked desktop session, fails the build when a test fails, writes a JUnit file and keeps every picture of a failure.It works with TeamCity, Azure DevOps, GitHub Actions, Jenkins and GitLab.
Does anything have to be installed inside the application?
No.GUI Test Studio watches the screen, asks Windows what the app already describes about itself, and uses the mouse and keyboard like a person.There is no library, no test build and no agent inside the app.
Microsoft, Windows, WPF, Windows Forms, FlaUI, pywinauto, TeamCity, Azure DevOps, GitHub, GitLab and Jenkins are trademarks or projects of their respective owners.
Ready to record your first test?
Every feature, for 15 days.
Rather see it on the kind of app you test?Book a free 30-minute call with our team.