RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


JUnit and Its Extensions Make Unit Testing Your Java Code Much Easier : Page 2

In an age when any professional Java developer has to know how to properly unit test the code he or she writes, JUnit and its extensions can make writing your tests easier and more effective.

jfcUnit Tests Swing GUIs
Swing is difficult to test. It's big and complicated, and GUIs change frequently during development. To simplify things, you should ensure that you always separate business logic from presentation logic. The most common way to do this is to implement the MVC pattern (Erich Gamma, et al.). MVC allows you to concentrate your testing on the model and the controller, which are significantly easier to test.

However, the GUI still needs to be tested. That's where the JUnit extension called jfcUnit comes in—it's designed to test Swing GUIs. Testing a Swing GUI is traditionally hard for two reasons:

  1. Tests can fail if a visual component is moved to a different part of the screen or onto a different panel in the same window.
  2. Tests can be extremely difficult to write because of the difficulty in locating visual components to test.

jfcUnit does a good job of resolving both these issues, but testing GUIs is still time consuming and laborious (see the Sidebar: GUIs: To Test or Not to Test?). A common suggestion is test the GUI only if it contains complicated presentation logic. This means that if you're writing a basic input form it's probably not worth testing—it's too trivial and is unlikely to be wrong.

For this example, I'll examine a basic Logon form. Figure 1 shows a JFrame, which contains two JTextArea components, a JLabel to identify each of them to the user, and two JButton components.

Figure 1: A JFrame with two JTextArea components

This is a fairly trivial form, but the techniques I'll examine to test it are applicable to most Swing GUI forms. Before I go any further into the creation of the tests for this, however, I need to discuss fixtures.

Fixtures Ensure a Clean Test Environment
A fixture is a set of conditions that are true for every test in a single suite (a suite is all the tests in a single test class). JUnit uses setUp() and tearDown() methods to create and reset a fixture.

In this article's example, you need to have a few private attributes for your tests to access:

private JFCTestHelper helper = null;
private Main main = null;
private JButton ok = null;
private JButton cancel = null;
private JTextField username = null;
private JTextField password = null;
The helper object is used throughout jfcUnit tests to access elements of a form and to simulate mouse and keyboard events. These attributes are populated in the fixture's setup() method:

protected void setUp() throws Exception {
  helper = new JFCTestHelper();
  main = new Main();
  ok = (JButton) helper.findNamedComponent("Ok", main, 0);
  cancel = (JButton) helper.findNamedComponent("Cancel", main, 0);
  username = (JTextField) helper.findNamedComponent("username", 
main, 0);
  password = (JTextField) helper.findNamedComponent("password", 
main, 0);
The setUp() method here finds the active components on the form and assigns their references to the instance attributes: ok, cancel, username, and password.

The way jfcUnit finds components is quite clever. It calls the findNamedComponent() method, like in the following line:

  cancel = (JButton) helper.findNamedComponent("Cancel", main, 0);
This method then checks jfcUnit's internal collection of component references. jfcUnit builds this collection by adding itself as an event listener to all component and hierarchy events for the GUI. Whenever a component is added to a form jfcUnit responds to the event by storing a reference to that component. So, when you call the findNamedComponent() method, it doesn't need to perform the difficult task of navigating a form's containers, it just returns the component from its internal store.

The tearDown() method resets the test's attributes back to null and destroys the form:

protected void tearDown() throws Exception {
  ok = null;
  cancel = null;
  username = null;
  password = null;
  main = null;
This is necessary because you want the form to be as new for each test in your test class. The setUp() method is run before every test method, and the tearDown() method is run after every test method. In this example, it means that a new form is available for each test method.

Using these two methods carefully will help ensure that no dependencies exist between tests. Dependencies make tests more difficult to write. If you use fixtures to ensure a clean environment for each test, they have to be written independently. You want to create tests that are easy to change. If you have dependencies between the tests, when you change one you'd have to change all the dependant ones too—which is a bad thing.

Also, using fixtures tends to make the tests more readable and improves their design. Don't forget, tests are code too—they need to be well designed and well refactored in order to maximize benefits and increase quality and productivity.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date