Unit Testing Swing Applications with Marathon/JavaDriver
One of the main reasons we desist from unit testing GUI code is that it is a hard problem. Marathon/JavaDriver provides a simple way of unit testing swing user interface components.
Marathon/JavaDriver is a library to perform automatic Java application interaction, so Java programs can automatically perform the same interactions that user performs manually on the application. Marathon/JavaDriver is intended and primarily used as a backend for Marathon and MarathonITE GUI test automation tools.
Unit testing GUI applications is a hard task. Not only do you need to perform the verifications needed, but also structure the code so that unit testing is simplified. Marathon/JavaDriver excels at unit testing swing applications.
In this article we provide a quick overview what JavaDriver is and the basic components of a JavaDriver test. We also review the Selenium/WebDriver bindings JavaDriver provides to simplify unit testing swing applications.
JavaDriver is an implementation of Selenium/WebDriver that enables automating interactions for Java applications. It is based on client-server architecture.
JavaDriver client includes:
The JavaDriver server components include:
When using JavaDriver for Unit Testing Swing applications, the Client application and the Server application resides in the same JVM.
Unit Testing Swing Applications - Basic Steps
The following are the steps required for unit testing swing applications using JavaDriver. These steps apply to any test framework and application.
The Example Application
We will test a very basic application in this article. Assume that your main application is gated by a login screen. The user should provide valid credentials to get into the application. In our test application, the Login process just displays a message (and we ignore that in our tests — more later).
The LoginDialog is intentionally simple to demonstrate various JavaDriver functionalities. On entering valid credentials (that are hardcoded to bob/secret) a message is displayed and clicking OK terminates the application. If wrong credentials are entered, an error message is displayed and clicking OK clears the Username and Password fields. Clicking cancel displays a message and clicking OK terminates the application.
We can think about multiple scenarios for this simple dialog.
- Success Case
User enters valid credentials (username: bob, password: secret) and clicks on Login.
User Cancels the operation by clicking on Cancel.
- Wrong Login
User provides wrong credentials. In that case the username and password fields are cleared.
Notes on Unit Testing Swing GUI
Since the tests are executed in the same JVM as the application, we can’t have the application exiting. For unit testing swing applications, we need to structure the source code so that testing a single dialog need not depend on multiple unrelated areas. A small change in the LoginDialog API provides the hooks required for overriding this behaviour:
You can override the behaviour of Cancel button by implementing this method.
You can override the behaviour of Login button by implementing this method.
You can override the behaviour of Login with invalid Username and Password button by implementing this method.
- authenticate(String userName, String password)
You can plug a custom authentication mechanism by overriding this method.
Showing the User Interface
Displaying the JFrame or JDialog under test is the first thing that we need to do before we can access the dialog components.
We used an anonymous class to override the functionality of the LoginDialog in the above code snippet. Alternatively, we can create a Class that extends the LoginDialog and override the methods.
login object is created we display it using
setVisible method. Keep in mind that we can access UI components only from EDT – using
Creating a JavaDriver Instance
The JavaDriver implements the WebDriver interface. We use the WebDriver interface to access Selenium/WebDriver methods and access other objects. Instantiating the JavaDriver is required before we can access components from the dialog.
We create an instance of a WebDriver instance using the JavaDriver constructor.
Once we create an instance of the JavaDriver class, we use this instance to invoke methods and access other interfaces. We do this by assigning the instance to a variable and using that variable to call methods.
The following example creates a JavaDriver object and assigns it to a variable named
JavaProfileand set it to use
EMBEDDEDmode and for
SWING_APPLICATION. Pass this profile to the
JavaDriverconstructor to create an object and assign it to
Locating Components on the Dialog
Byobject instances. Marathon JavaDriver supports finding elements by using id, name, tagName, className and cssSelector. We explain them below:
Performing Actions on the Component(s)
WebElementinterface returned by one of our
findElementmethods. The JavaDriver supports the following WebElement interface methods to interact with a Component:
Verifying the Application Response
Once we performed actions on the application, we expect some way of checking the results. Either the results are provided by the application object(s) or some change in the state of the application dialog.
Accessing Component State
You can use
getAttribute methods to access a components’ state. Specifically, the
getAttribute method allows you to access any bean value from the component. For example,
component.toolTipText returns the tooltip provided for a component.
Waiting for a Response
When you perform some actions, the application may change the state of the UI. For example, the Login button might get enabled only when both the Username and Password fields are filled up. When manually using the application, we wait till the Login button is enabled before clicking on it. Selenium/WebDriver supports implicit and explicit waits so that the test code can wait till a state change occurs.
The above statement requests the driver to set the implicit wait to 10 seconds.
Explicit waits wait until an expected condition occurs on the application dialog, or until a maximum wait time elapses. To use an explicit wait, you create an instance of the
WebDriverWait class with a maximum wait time, and you invoke its
until method with an expected condition.
The WebDriver API provides an
ExpectedConditions class with methods for various standard types of expected conditions. The retuned expected condition objects can directly passed to the until method. You can also pass your own Function objects that return true/non-null value when the expected condition is met and false otherwise. The
until method checks repeatedly, until the maximum wait time elapses, for a true boolean return value or a non-null object reference, as an indication that the expected condition has occurred.
The above example waits till the loginBtn is clickable or 10 seconds whichever is earlier.
Putting it All Together
By putting all the pieces together we can create a complete test case.
Displaying the JFrame or JDialog under test is the first thing to be done. Assuming that your all tests in your test class exercises a single Dialog you can use the Fixtures to show the dialog and close it at the end.
You need to start the UI in the Swing Event Dispatcher Thread (EDT). Once that is done, you can create the driver.
The test is intentionally simple to show the basic functions that are available.
click()does what you expect them to do. Finally, an assertion that the login is succeeded is performed using JUnit