In the last post I talked about how to integrate tests in the build environment. When we talk about testing I always think on unit testing for my classes and methods but I usually forget UI testing.
With Qt we have a really nice environment to test GUI as well. Widgets implement all the power of signals and slots, and Qt provides a wide range of signals so we can keep track of what happened and what’s currently happening. The fact that we can work with QSignalSpy makes all or testing easier. At least if we know what to test, because some times is not that straight-forward with widgets. Specially with QListWidget, QTableWidget and QTreeWidget classes.
In the Qt5 repository we can finde a lot of tests for those classes:
Testing what the user does
If you take a look in the tests I linked before there are a lot of widget-based testing. That’s great because is what is intended to be done from the Qt perspective. It ensures as that this base classes are working as they should be. However, there are two scenarios that are not taken into account on those tests:
- When the user modifies the date using the signal-slot mechanism
- When the user edits the table directly, skipping the widget-based management of the list/tables
When we want to automatize the testing for the UI we need to simulate what the user would do instead of testing how the widgets have to behave. Let’s rephrase it using QTableWidget as example.
So, when a user interacts with a table it doesn’t access the QTableWidgetItem and modifies it’s value. The user uses the QTableWidget and the mechanisms that it provides to modify the data. In addition to that, we need to test if the QTableWidget behaves as it should be when we simulate how the user acts. That means that we need to send mouse clicks and wait for the QTableWidget reaction using th QSignalSpy.
Automatic GUI testing
Don’t worry that here is where the coding part starts!
I needed to test my sample app and it included an editable table. It means that we need to test if the user selects a cell and also if the user edits one. I’ll post the code and then explain it. Before that, I just want to clarify that these tests are thought from the perspective of some data added into the table using a form. That’s why I’m checking those QSignalSpy.
Testing QListWidget/QTableWidget/QTreeWidget selection
The testing code starts on the QSignalSpy spy3 instantiation. There we want to test if the QTableWidget emits a cellClicked signal when the user performs that action. The next thing we’re trying is to simulate what the user does when it clicks the cell in the first row and the second column.
We need to simulate a mouseClick (line 46) on a point in the viewport of the QTableWidget that is the one that actually takes the action. After that we check if we get the signal and then we check that the row and column numbers in the signal are the same we wanted to check.
H3 Testing QListWidget/QTableWidget/QTreeWidget cell edition
This test is a bit harder to understand because of how the edition works on the list/table widgets. In the links I sent in the beginning there are a couple of editItem methods but they don’t actually test what the user has edited. Let’s start.
The test starts by assuming that the QListWidget has items (even if they’re empty).
After that we take the visual rectangle of the item we want to test (where the user actions will be simulated). After that we simulate two mouse actions. The reason why we do that is to first set the focus on the viewport of the list and then perform the double click that actually opens the edition mode of the cell.
After waiting for the response, we get all the children in the viewport, because what these widget do is to create a QLineEdit that takes the text the user inputs and store it in the list/table widget. When we find a widget that fulfills that condition we analyze if the contents where what we were expecting (according to other tests in that project).
If it success, we set the new text to the line edit, and then, we need to close the line edit so the addition has stop. But we still have to notify the parent widget that we have finished editing. We do that by adding a Key_End event.
The last line is where we acces the testing widget, extract the item in the rect we selected before and see which text it contains.
With that last action we’re testing that the user as set some that in a line edit triggered by the QListWidget. That the data has transmitted perfect from the line edit to the list and from the list to the QTableWidgetItem.