I've recently spoken with a few developers that were writing Swing apps (some for the first time) but were not familiar with the Swing threading model and the event dispatch thread, so I thought I'd review some of the basics. The main rule of Swing programming is that any code that updates the GUI (this applies only after a component has been realized, meaning its paint method has been called) must be invoked on the event dispatch thread (EDT). This can be accomplished using the methods SwingUtilities.invokeLater and SwingUtilities.invokeAndWait to run tasks on the EDT asynchronously and synchronously respectively. There are some exceptions to the rule, and Sun details those in an article http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#exceptions (that whole article is a very good explanation of Swing threading as well).
While this does sound like a straight forward rule, there are some easy ways to get into trouble. One of these ways is when code is updating a GUI's model, which in turn updates the GUI component, that code must be invoked on the EDT. For example, calling model.setDataVector on the DefaultTableModel to update the data in a table must be called from the EDT. Even though you are just updating the model, the model fires an event that updates the GUI (note that some GUI libraries may already handle this for you, but it's not safe to assume this in general).
Violating this rule can cause unpredictable effects. Just like any threading error, reproducing and debugging the problem can be very difficult, so be sure to obey this rule religiously.
Showing posts with label user interface. Show all posts
Showing posts with label user interface. Show all posts
Friday, October 2, 2009
Tuesday, September 8, 2009
Automated UI Testing
I've often been asked how to automate testing for a UI. There isn't an easy solution to this problem, and automated UI testing is very difficult. An application's UI is frequently changing, sometimes just minor layout changes, and sometimes major reworks. Regardless, the change can be enough to throw off a lot of automated UI testing tools. So does that mean we shouldn't test the UI?
Every situation is different, but these are a few tips that will help make automated UI testing easier:
Every situation is different, but these are a few tips that will help make automated UI testing easier:
- Make the UI as thin as possible. Don't put business logic in UI components, even though it can be tempting (patterns such as Model-View-Controller help with this). An example:
Difficult to test:
JButton button = new JButton("Check if even");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int num = Integer.parseInt(numberField.getText());
if ( num % 2 == 0 ) {
System.out.println("num " + num + " is even");
}
}
});
This is difficult to test because now the whole UI needs to be brought up just to test this function, and this is not conducive to automated testing. This can be refactored as follows:
JButton button = new JButton("Check if even");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int num = Integer.parseInt(numberField.getText());
if ( NumberUtils.isEven(num)) {
System.out.println("num " + num + " is even");
}
}
});
public static class NumberUtils {
public static boolean isEven(int num) {
return num %2 == 0;
}
}
While this may not look like a big deal, the change is signficant. The NumberUtils class can have a simple unit test written for it and now that code is being tested outside the UI. It's easy to see that as functions become more complex, it becomes even more important to move them outside the UI logic. - Wait until the last responsible moments to write UI tests. Don't write automated UI tests while the UI is still in a great state of flux. You'll spend more time maintaining them than you will find useful. This will vary from project to project, but I find this doesn't generally happen until later on in the project.
- Maintain automated UI tests like you maintain code. They should be run as part of a regression test (even if it's a nightly test as opposed to a continuous build since they may take longer to run). They generally can't be simple record/playback tests. While this is a good place to start, they really should be refactored by a developer to make sure that when the UI changes and tests break, minimal changes will be needed to the UI tests. For example, if multiple tests are looking for a particular table column, make that a static string somewhere (or even reference it from the code under test if you can and the string itself is not under test). This way if the string changes, you don't need to update the string in multiple places.
Labels:
automated testing,
UI testing,
user interface
Subscribe to:
Posts (Atom)