Showing posts with label GUI application. Show all posts
Showing posts with label GUI application. Show all posts

Saturday, February 14, 2009

Doing a GUI application using TDD

Testing user interfaces is always a pain point when doing TDD GUI apps. Web development has been able to get around this a bit with automated UAT testing using tools like Selenium or Watir. But with GUI apps, there's no really good, cheap (as in free) UAT tools (if anyone knows of one, please let me know!!!).

So when I create a GUI app I use a MVP pattern where the View is really, really thin because it's just impossible to right good tests around it. In the past with Swing and SWT/RCP application, I've been able to have the view be fairly simple with an exposed getter/setter-ish API on it so that the Presenter can be tested on how it manipulates the View and the Model can have it's own tests for the business rules and that leaves not a whole bunch to test in the View. You don't really need tests for the implementat of view.getUsername() when all it does is read the text from a text field.

Here's a sample login MVP of what I'm talking about...
(I didn't compile this so don't complain if it doesn't actually work)

public class Presenter {
public MyPresenter(final IView view, final IModel model) {
view.addLoginListener(new IListener() {
public void fireEvent() {
try {
model.authenticate(view.getUsername(),
view.getPassword());
} catch (AuthenticationException e) {
view.showErrorMessage(e.getMessage());
}
}
});
}
}


The API exposed on the view is real simple:

public interface IView {
void addLoginListener(IListener listener);
String getUsername();
String getPassword();
void showErrorMessage(String errorMessage);
}


And the API on the model is real simple too:

public interface IModel {
String authenticate(String username, String password)
throws AuthenticationException;
}


So this example makes the presenter very testable. And it separates out the need to know how the stuff is displayed to the user verses and how the actual authentication needs to happen. And the model is is left with a single responsibility and a very simple API. So testing the model is now real easy also. And the view now has no complicated functionality.

Even if you wanted to have some fields in the view that enable or disable based on options that the user has selected in other parts of the view, you can create another MVP triad for that and you could have another interface for that the view implements that might have some methods that look like this...

void enableSubmitButton();
void disableSubmitButton();

So the View doesn't even know what the state of the submit button is. It just knows how to enable or disable the field. Again, all the logic can go off to a Model so that it is testable and the Presenter just acts as a mediator between the View and Model interfaces which keeps the Presenter really testable.

As a general rule of thumb, you write a test for anything that could possibly break. And at the point of where this view is at, what would you be testing? That Swing is working? You shouldn't be testing their code, just yours.

Now in my game, I'm using Java3D for my "View" so that's going to be a fairly complicated piece that needs to be split as thin as possible to provide testability. Because really that should be your goal: how can I make this code testable? If I abstract something out and hide it behind an interface and inject it in, I can test my classes better.