Automating Gen™ GUI Testing with Python

Jerald Herstein
Gen-dev
Published in
5 min readApr 18, 2023

Gen applications are meant to work as designed based upon the data model and the associated action diagram. To ensure that the code works the way the Gen Developer intended, we recommend testing to make sure the final product meets end-user requirements.

When writing code in programming languages like C, Java, or Python, tools exist that can be used to create unit tests to guarantee that the individual pieces work as expected. For example, in Java, suppose you have a function called sum that adds two numbers together:

public class Addition {
public static final int sum(int a, int b) {
return a + b;
}
}

You could then write a unit test to assure that the function adds the two numbers correctly:

public class AdditionTests {
@Test
public void testSum() {
assertEquals(5, Addition.sum(2, 3));
assertEquals(5, Addition.sum(3, 2));
}
}

Gen generated code, however, does not have this sort of atomic functionality. The code can be generated in C, C#, Java, or COBOL; and the code can run on the mainframe, Linux, Unix, or Windows. As a result, it is very difficult to create a unit test structure for Gen applications.

The Gen development team has found a Python package that can be used to automate testing of Gen models for Windows GUI applications and Cooperative applications with a Windows GUI client.

Pywinauto for GUI Applications

The Gen development team has successfully used Pywinauto, an Open Source package of Python modules, to automate the testing of Windows GUI applications (i.e. Windows or Cooperative packaging). Pywinauto is a package of Python modules that can interact with a Microsoft Windows GUI by allowing a program to send mouse and keyboard actions to the GUI. These interactions can be combined with Python’s TestCase class to allow assertions to be applied to the results.

As an example, let’s take a look at how one might create an automated test for the Gen Sample model using Pywinauto

from pywinauto.application import Application
import unittest

class TestSample(unittest.TestCase):
def testAdd(self):
app = Application(backend="win32")
app.start("GUIMENU.EXE")
dlg = app['Corporate Management']
dlg.wait('ready', timeout=5)
dlg.menu_select("List -> Divisions")
divDlg = app['CorporateDivisions']
divDlg.wait('ready')
divDlg.menu_select("Edit -> Add")
app.DivisionDetails.DivisionNumberEdit.set_text("11")
app.DivisionDetails.DivisionNameEdit.set_text("DIV1")
app.DivisionDetails.DivisionManagerNumberEdit.set_text("0")
app.DivisionDetails.OK.click()
divDlg.wait('ready', timeout=3)
count = divDlg.DivisionNameListBox.item_count()
self.assertEqual(count, 1, "Division not added")
app.set_focus()
app.close

This example shows how Pywinauto can be used to test Gen GUI-based applications.

app = Application(backend="win32")

The first statement initializes the backend process that will be used to drive the application. Pywinauto supports two different backends. Win32 can handle older Windows applications built using MFC and some WinForms. There is also a “uia” backend for MS UI automation, which supports newer Windows interface objects.

app.start("GUIMENU.EXE")

The next statement starts the Gen application. The start function executes like a Windows command line, so arguments can be added as needed. Once the Application object has started the GUI application, the test program has access to the windows and controls (or UI widgets) in the application.

Sample Model Main Window
dlg = app['Corporate Management']

After starting the application, the next step is getting access to the main window. Pywinauto has several ways to access windows and controls. In this case, the test is retrieving the window into a variable from a dictionary entry. This scheme is best when there are embedded spaces in the window name, as there are in the example.

dlg.wait('ready', timeout=5)

An important part of any GUI testing is providing sufficient wait times between events. In the sample code, the test will wait for the main window to be ready for input or 5 seconds. If the window takes too long to get ready, the test will timeout rather than attempt to continue.

dlg.menu_select("List -> Divisions")
divDlg = app['CorporateDivisions']
divDlg.wait('ready')

The Sample model uses menus to move between windows. Pywinauto’s menu_select function handles this menu selection. The arrow (->) represents a parent to child menu relationship. In the example, this is the first place the test code interacts with the model application. As the code sample shows, the menu selection causes a new window to appear and the test uses the dictionary approach to get the new window. The test waits for the new window to get ready, this time without a timeout.

Sample Model: Corporate Divisions Window
divDlg.menu_select("Edit -> Add")
app.DivisionDetails.DivisionNumberEdit.set_text("11")
app.DivisionDetails.DivisionNameEdit.set_text("DIV1")
app.DivisionDetails.DivisionManagerNumberEdit.set_text("0")

The test then makes a menu selection on the Corporate Divisions window. For the next several lines, where the test is entering data onto the screen, the test uses a different Pywinauto access method for the window and the fields. Python can resolve attributes dynamically, while Pywinauto provides a best-match capability for object names. This allows the test creator to access the windows and fields directly by their name rather than pulling them from the dictionary. The only requirement for using this method of access is that the name cannot contain whitespace or any character that would not be valid for a Python variable name.

Sample Model: Division Details Window
app.DivisionDetails.OK.click()
divDlg.wait('ready', timeout=3)

Next, the test code clicks the OK button, which should cause the Sample model to store the new division in the database and close the Division Details window. The test waits 3 seconds for the process to complete and the Corporate Divisions window to become active again.

Sample Model: Corporate Divisions after Add
count = divDlg.DivisionNameListBox.item_count()
self.assertEqual(count, 1, "Division not added")

Finally, the testing code can evaluate the results to make sure the model code is working correctly. In this case, the test is just evaluating that a record was successfully added; however, additional assertions could be used to make sure that the data retrieved from the database matches the data that was entered.

app.set_focus()
app.close

Once the tests are complete, the Python code shuts down the sample model executable and exits. Because the testing framework is based on the Python UnitTest class, the results can be handled by Python for reporting and indications about whether the tests have passed or failed.

Conclusion

While the nature of Gen makes unit testing of model actions impractical, testing packages exist that can help developers make sure the overall model behavior meets end-user expectations. Python with Pywinauto is one of those tools that has been used by the Gen development team to evaluate changes to the Gen runtimes with great success. We hope you will use it to test your Gen applications to increase your confidence that your models work they way you desire.

--

--

Jerald Herstein
Gen-dev
Writer for

Software Engineer at Broadcom. Working on Gen (tm), a tool for generating applications from models.