Qt & QML Development
2026-06-16
14 minutes reading

Practical GUI testing for Qt applications with Squish

Kacper Kaleta
Kacper Kaleta Qt Developer

GUI testing is not unit testing with buttons attached. As a Qt developer, I can cover a lot with Qt Test: model logic, signal-slot behavior, validation rules, serialization, request construction, response handling, and all the other places where application behavior is accessible directly from code.

Squish solves a different problem. It drives the running application, interacts with the actual GUI, reads runtime object properties, and verifies what the user-facing application is doing after the event loop, QML layer, widgets, focus handling, and platform integration all get involved.

That difference is not academic. A Qt Test case can tell me that the logic behind a login state works; a Squish test can tell me that the login dialog actually appears, can be closed, and leaves the application in the expected visible state.

Why Squish still matters in Qt projects

Squish matters when the GUI itself becomes part of the product contract. In many Qt projects, especially in industrial, embedded, medical, or certification-heavy environments, the question is not only whether the business logic is correct, but whether the delivered application behaves correctly through its real interface.

That is where Squish earns its place. It is not just clicking pixels and hoping for the best; it can work with the Qt object tree, Object Map entries, object properties, and runtime state, which makes it much more interesting than a plain screen-recorder-style tool.

I do not use Squish as a replacement for Qt Test. I use it for a different layer. Qt Test validates logic close to the implementation. Squish validates behavior through the AUT - the Application Under Test - after the application is actually launched and the GUI exists as a live system.

The uncomfortable market reality around Squish

Squish has been sitting in a very narrow and very strong niche inside Qt development for a long time. When someone asks for a mature tool for automated GUI testing of Qt applications, with object-level access instead of only image recognition, Squish is usually the first serious answer.

I do not love that. As an engineer, I prefer ecosystems with pressure, competition, and multiple viable implementation philosophies. With Squish, the impression is often that the tool has less direct competition than it should have, especially when the requirement is deep Qt integration rather than generic desktop automation.

There are tools that can help around the edges. Robot Framework can organize automation nicely. SikuliX (Oculix since March 2026) can be useful for image-based workflows. Custom Python scripts can solve small local problems. But those are not the same thing as a tool that understands a Qt application as a Qt application.

Testudos is the first newer alternative I would explicitly mention in this context. We had a chance to test it, and it looks interesting, especially because it targets Qt teams directly and presents a Python-based workflow, access to the Qt object tree and properties, headless CI execution, and embedded target support. I would not call it a drop-in Squish replacement after a short evaluation. I would call it a serious signal that this niche may finally become less lonely.

What I like about Squish as a Qt developer

Squish becomes useful when it sees more than the screen. The strong part is not that it can replay a click; the strong part is that it can inspect the application under test through object names, properties, hierarchy, and runtime state.

That matters in real Qt applications. A button can be visible but disabled because a business rule, product variant, device state, permission flag, or backend response changed something under the surface. Pixel-level tools see a rectangle. Squish can help me ask a better question: what object is this, what are its properties, and why is it in this state?

Object Map is the center of that workflow. It gives tests stable names for GUI objects, but those names are backed by object properties discovered in the AUT. In a small demo, Object Map feels like plumbing. In a real client application, it becomes one of the most important maintenance artifacts in the whole GUI test suite.

What I dislike about Squish IDE

Squish IDE is the part I complain about first. The tool can be powerful and still feel unpleasant to write in, and Squish IDE very much lives in that category for me.

The editor does not feel like modern Qt Creator, CLion, PyCharm, or Visual Studio Code. The shortcuts are odd, navigation is clunky, and small editing habits that are basically automatic in other tools become surprisingly annoying. This sounds cosmetic until I spend a full day inside the tool. Then it is not cosmetic anymore. It is friction.

The frustrating part is that I cannot simply ignore Squish IDE completely. I can edit Python files elsewhere, and I usually prefer to, but the actual Squish runtime, object inspection, recorder, debugger, Object Map tooling, and test execution still pull me back into the Squish environment.

The smallest useful Squish scenario

A minimal GUI test should still verify behavior, not just replay movement. For the tutorial example, I would start with a deliberately small Qt application flow: launch the AUT, open a login dialog, close it, and verify that the dialog appeared and disappeared as expected.

That scenario is intentionally boring. Good. Boring scenarios are excellent for tool evaluation because they remove product-specific noise. If a GUI automation tool cannot make a clean, inspectable, repeatable test out of a simple login dialog flow, I do not want to meet it inside a medical workflow with ten product variants and three nested modal dialogs.

This example also shows the entire Squish loop. I configure a test suite, create a test case, record an interaction, inspect the generated script, add verification, run the test, and check the result.

Step 1: Start Squish IDE

Squish testing starts inside Squish IDE. I launch the IDE because that is where the test suite, test case, recorder, Object Map, debugger, and execution results are tied together.

Start Squish IDE

At this stage I am not writing a test yet. I am entering the Squish workflow and preparing the environment that will know how to launch and control the AUT.

Step 2: Create a new test suite

The test suite defines the main testing context. I open the New Test Suite wizard from the File menu and start configuring a suite for a Qt application.

Create a new test suite

This is where Squish starts asking the questions that matter: what kind of application I am testing, which scripting language I want, and where the AUT is located.

Step 3: Configure the Qt test suite

The Qt test suite configuration is where I connect Squish to the actual application. In this example, I choose a Qt test suite, select Python as the scripting language, and specify the AUT.

Wizard Step 1

Step 3 - Wizard Step 1

Wizard Step 2

Step 3 - Wizard Step 2

Wizard Step 3

Step 3 - Wizard Step 3

Wizard Step 4

Step 3 - Wizard Step 4

I normally choose Python here because it is readable, easy to extend, and practical for test automation. Squish also supports other scripting options, but Python is usually the least surprising choice for this kind of work.

Step 4: Verify that the test suite exists

A configured suite is the first concrete artifact in the workflow. After the wizard finishes, the new test suite appears in the Test Suites view and becomes the place where test cases, scripts, configuration, and Object Map data will live.

Step 4: Verify that the test suite exists

This step is simple, but I always check it carefully. If the suite is not where I expect it to be, or if the wrong configuration is being picked up, debugging later becomes irritating very quickly.

Step 5: Create a test case

A test case gives the recorded interaction a home. I create a new test case from the File menu, and Squish prepares the location where the generated script will be stored.

New Test Case Menu

Step 5 - New Test Case Menu

Test Case Wizard

Step 5 - Test Case Wizard

This distinction matters. The suite describes the testing environment. The test case describes a specific scenario inside that environment.

Step 6: Confirm that the test case is ready

The created test case should now appear inside the suite. At this point, the structure is ready, but the test still does not do anything useful.

Step 6: Confirm that the test case is ready

I treat this as a checkpoint. Before recording, I want to know that Squish is writing into the correct suite and the correct test case, not some stale directory or copied configuration from a previous experiment.

Step 7: Start recording

Recording is the fastest way to expose how Squish sees the AUT. I start recording, Squish launches the application, and the recorder begins capturing user interactions.

Record Button

Step 7 - Record Button

Record Settings

Step 7 - Record Settings

I do not treat recording as the final test. I treat it as scaffolding. Squish gives me the first version of the script, and then I decide what the test should actually assert.

Step 8: Interact with the AUT

The recorded interaction should be tiny and intentional. In this example, I launch the AUT, open a login dialog, and close it.

AUT Launched

Step 8 - AUT Launched

Login Dialog

Step 8 - Login Dialog

Dialog Closed

Step 8 - Dialog Closed

This is not impressive as a product scenario, but it is perfect as a Squish scenario. It confirms that Squish can launch the AUT, observe the GUI, record interaction, and later replay the same behavior.

Step 9: Review the generated script

The generated script is a starting point, not a finished test. After recording stops, Squish creates a script that reproduces the interaction I performed manually.

Step 9 - Generated Script

This is the moment where I stop trusting the recorder blindly. Recorded scripts often contain useful object references and interaction code, but they do not necessarily express the intent of the test.

Step 10: Add verification to the script

Verification turns recorded interaction into a real test. For the login dialog flow, I want checks that confirm the dialog appears after the opening action and disappears after the closing action.

Step 10 - Edited Script

This is the part I care about most. Clicking a button is not enough. A test should fail when the application state is wrong, not merely succeed because Squish managed to replay yesterday’s hand movement.

Step 11: Run the test and inspect the result

The test run proves whether the script is repeatable. Squish replays the recorded actions, executes the added checks, and reports whether the scenario passed without errors.

This video is worth including because GUI testing is temporal. A screenshot can show that a dialog existed at one moment. A test video shows that the AUT launched, the interaction happened, the dialog state changed, and the test completed.

Why the generated test is only the beginning

The recorder gives me mechanics, not judgment. It can capture a click, a wait, a lookup, or a close action, but I still have to decide what behavior matters.

For this test, the important behavior is not “I clicked something.” The important behavior is “the login dialog became visible, then stopped being visible after the close action.” That is the difference between automation and testing. Tiny sentence. Big difference.

Why Object Map becomes expensive in real applications

Object Map is powerful because it creates stable names for GUI objects. It is also expensive because real client applications are messy, and those stable names have to survive product variants, custom widgets, layout changes, and inconsistent object properties.

In a small tutorial, Object Map looks straightforward. In a client project, it becomes a living contract between the test suite and the AUT. If developers rename objects, restructure QML, change widgets, or introduce dynamic properties, the Object Map may need attention.

That is not a reason to avoid Squish. It is a reason to budget for maintenance. GUI automation without maintenance is fiction.

The Wayland problem I hit immediately

Wayland can break Squish tests for reasons unrelated to test logic. On modern Linux, Wayland’s security model can prevent operations that GUI automation tools commonly need, including screenshots and synthesized keyboard input.

The symptom looked stupid at first. Some tests failed not because the script was wrong, not because the AUT was wrong, and not because Object Map was wrong. Squish simply could not grab a screenshot or perform nativeType.

The name nativeType is also slightly cursed. It sounds like type-system terminology, but in this context it means synthesizing keyboard input. Not my favorite naming moment.

The xcb workaround

For a Qt AUT, the clean workaround is to force the application to use the xcb platform. I can do it through a command-line argument:

I can also do it through an environment variable:

I prefer the environment variable in the suite’s envvars file. That keeps the workaround local to the Squish suite and avoids changing the rest of my desktop session.

Installing X11 bits globally or forcing applications onto XWayland system-wide feels like an overreaction. Setting QT_QPA_PLATFORM=xcb for one Squish suite is a smaller cut. Surgical. Much better.

The xcb workaround has a cost

The AUT must stay activated when I use the xcb workaround. If I switch focus to another window, even on another monitor, the test flow can break.

That is annoying, but it is not surprising. GUI automation depends on focus, window activation, input routing, and the display server. When I automate a GUI, I am not only testing my code. I am also negotiating with the desktop environment.

This is exactly why GUI test failures need careful interpretation. A red test can mean broken business logic, broken object lookup, broken timing, broken focus, broken compositor behavior, or just a developer who clicked Slack on the second monitor at the wrong time.

The suite.conf trap

Squish can pick up the wrong suite.conf if I leave configuration files in the wrong place. I learned this through an avoidable but memorable mistake.

I had a suite-specific envvars file tied to suite.conf, and I did not want to commit my local Linux-only QT_QPA_PLATFORM=xcb change. I also did not want to stash it because I was editing Git history and did not want the stash to become stale after rewritten commits.

So I copied both files one level above the project root. Backup. Temporary. Innocent. Famous last words.

Squish happily detected and used that copied suite.conf instead of the correct one. Then it produced errors that were loud, vague, and impressively unhelpful. The test was not broken. The AUT was not broken. My directory hygiene was broken.

How I now handle local Squish configuration

I do not leave Squish suite files near the project tree unless I want Squish to consider them. Temporary copies of suite.conf, envvars, and related files go somewhere boring and clearly outside discovery paths.

This is not glamorous advice, but it saves time. Squish configuration discovery is useful until it discovers the wrong thing. After that, it becomes a horror story with stack traces.

Why documentation is still a real onboarding problem

Squish needs practical documentation more than feature documentation. The official material can describe APIs, but the first useful question for a new user is often much simpler: what should I click, what should appear, where is the generated script, and how do I know the suite is configured correctly?

Older Froglogic documentation came up as valuable because it explained mechanisms, not only surface-level usage. That distinction matters for Squish. If I understand the mechanism, I can debug the test. If I only copied the wizard steps, I am stuck when the GUI, Object Map, or runtime does something unexpected.

That is why I would teach Squish with screenshots, videos, and failure examples. Show the happy path, then show the traps. Otherwise the first real failure feels personal.

What this tutorial actually demonstrates

This small tutorial demonstrates the complete Squish feedback loop. It starts in Squish IDE, creates a Qt suite, creates a test case, records a simple AUT interaction, reviews the generated script, adds verification, and runs the test successfully.

The point is not the login dialog. The point is the workflow. Once that workflow is clear, the same structure can be expanded to product-specific dialogs, configuration screens, device-control panels, embedded UI flows, and certification-relevant regression scenarios.

I would rather teach one small scenario correctly than ten impressive scenarios badly. A precise login dialog test is more useful than a giant recorded script nobody wants to maintain.

My final take

Squish is a sharp tool for Qt GUI testing, not a pleasant toy recorder. It gives me access to the running AUT, Object Map, Qt object properties, and realistic GUI interaction, which is exactly what I need when the interface itself must be tested.

Squish also makes me pay attention to things that ordinary unit tests never touch. Squish IDE behavior, suite.conf, envvars, Wayland, xcb, focus, screenshots, nativeType, Object Map maintenance, and generated script cleanup all become part of the engineering work.

I am fine with that trade-off. I do not expect GUI automation to be effortless. I expect it to be inspectable, repeatable, and honest about what layer of the application it is testing. Squish can do that. Just do not leave a stray suite.conf one directory above your project and then act surprised when the tool decides to ruin your morning.

Contact us

We'll address every query and pinpoint
the ideal strategy for your project's success.

Fill out the form, and we’ll get back to you shortly.

Lukas Kosiński

Lukas Kosiński

Chief Executive Officer

The administrator of the personal data is Somco Software sp. z o.o., 13 Gen. Ottokara Brzoza-Brzeziny St., 05-220 Zielonka, KRS: 855688. The personal data are processed in order to answer the question contained in the contact form. More information, including a description of data subjects rights, is available in the information clause .