Ajax is undoubtedly one of the biggest buzz words to hit the Web development streets in recent memory -- the proliferation of Ajax-related tools, frameworks, books, and Web sites is evidence that this technology is here to stay. Besides, Ajax applications are pretty slick, aren't they? Yet, as anyone who has developed an Ajax application can testify, testing Ajax isn't exactly easy. In fact, the emergence of Ajax has essentially invalidated a host of test frameworks and tools that weren't designed to test asynchronous Web applications!
Interestingly, the developers of one particular Ajax-enabling framework noticed this limitation and did something quite novel: they built in testability. What's more, because this framework facilitates creating Ajax applications with Java™ code rather than JavaScript, they built on the shoulders of giants, so to speak, and took advantage of what's arguably the standard testing framework for the Java platform: JUnit.
The framework I'm talking about is, of course, the immensely popular Google Web Toolkit, also known as the GWT. In this article, I'll show you how the GWT actually leverages its Java compatibility to make Ajax applications every bit as testable as their synchronous counterparts.
Because GWT-related Ajax applications are written using Java code, they're perfectly suited to developer testing with JUnit. In fact, the GWT development team created a helper class dubbed GWTTestCase that extends from JUnit's 3.8.1 TestCase. This base class adds functionality for testing GWT code as well as handling some of the plumbing to get a GWT component up and running.
One thing to keep in mind is that GWTTestCase isn't meant to
test UI-related code -- it's meant to facilitate testing asynchronous aspects that can
be triggered by UI interactions. Mistaking the purpose of GWTTestCase has led many developers new to GWT to become frustrated
because they expect it to mimic user interactions easily, and they find that it doesn't.
There basically are two aspects to an Ajax component: its look and feel and its functionality, which of course is designed to be asynchronous. Figure 1 demonstrates a simple Ajax component that emulates a Web form. Because the component is Ajax enabled, the form submit is executed asynchronously (that is, without the page reload associated with traditional form submits).
Figure 1. A simple Ajax-enabled Web form
If you enter in a valid word and click the Submit button of this component, a message is sent to a server requesting the word's definition. The definition is returned asynchronously through a callback and is accordingly inserted into the Web page, as shown in Figure 2:
Figure 2. After clicking the Submit button, a response is displayed
Functional versus integration testing
Testing the interaction shown in Figure 2 can be broken out into a number of different
scenarios, but two stand out. From a functional standpoint, you'd probably want to write
a test to fill in the form value, click the Submit button, and then verify the presence of the definition under the form. A second option would be an integration test, which would allow you to validate the asynchronous functionality of the client-side code. The GWT's
GWTTestCase was designed for just this sort of test.
The thing to remember is that user interaction testing isn't possible with the GWTTestCase test case environment. When designing and building GWT applications, you must think about testing the code without relying on user interactions. This sort of thinking requires a decoupling of interaction code from business logic, which (as you already know) is a best practice to begin with!
For example, take another look at the Ajax application shown in Figures 1 and 2. This
application is composed of four logical components: a TextBox for entering a desired word, a Button for clicking, and two Labels (one for the TextBox and the other where the definition is displayed). An initial approach to the actual GWT module could look like what you see in Listing 1, but how would you test this code?
Listing 1. A valid GWT application, but how to test it?
public class DefaultModule implements EntryPoint {
public void onModuleLoad() {
Button button = new Button("Submit");
TextBox box = new TextBox();
Label output = new Label();
Label label = new Label("Word: ");
HorizontalPanel inputPanel = new HorizontalPanel();
inputPanel.setStyleName("input-panel");
inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
inputPanel.add(label);
inputPanel.add(box);
button.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
String word = box.getText();
WordServiceAsync instance = WordService.Util.getInstance();
try {
instance.getDefinition(word, new AsyncCallback() {
public void onFailure(Throwable error) {
Window.alert("Error occurred:" + error.toString());
}
public void onSuccess(Object retValue) {
output.setText(retValue.toString());
}
});
}catch(Exception e) {
e.printStackTrace();
}
}
});
inputPanel.add(button);
inputPanel.setCellVerticalAlignment(button,
HasVerticalAlignment.ALIGN_BOTTOM);
RootPanel.get("slot1").add(inputPanel);
RootPanel.get("slot2").add(output);
}
}
|
While in working order, the code in Listing 1 suffers from a major deficit: it can't be
tested a la JUnit and GWT's GWTTestCase. In fact, if I tried to write tests for this code, they would technically run but wouldn't ever logically work. Think about it for a second: how would you verify anything about this code? The only public method available to test returns void, so how will you verify its correct functionality?
If I want to validate this code in a white box manner, I have to separate
UI-specific code from business code, which requires some refactoring. In essence, it
means moving the meat of the code in Listing 1 into isolated methods that can be easily
tested. That isn't as simple as it sounds, however. The component's hook is obviously through the onModuleLoad() method, but if I want to force its behavior, I might have to manipulate some UI components.
Decoupling UI code from business logic
My first step is to create accessor methods for each UI component, shown in Listing 2. That way, I can grab them if I need to.
Listing 2. Adding accessor methods to UI components to make them accessible
public class WordModule implements EntryPoint {
private Label label;
private Button button;
private TextBox textBox;
private Label outputLabel;
protected Button getButton() {
if (this.button == null) {
this.button = new Button("Submit");
}
return this.button;
}
protected Label getLabel() {
if (this.label == null) {
this.label = new Label("Word: ");
}
return this.label;
}
protected Label getOutputLabel() {
if (this.outputLabel == null) {
this.outputLabel = new Label();
}
return this.outputLabel;
}
protected TextBox getTextBox() {
if (this.textBox == null) {
this.textBox = new TextBox();
this.textBox.setVisibleLength(20);
}
return this.textBox;
}
}
|
I've now enabled programmatic access to all of my UI-related components (assuming that all the classes that require access are in the same package). Only time will tell if I actually need to use one of these accessors for verification purposes. I'm hoping to limit my use of the accessors because, as I've already mentioned, the GWT isn't meant to be used for interaction testing. In this case, I'm not really trying to test whether a button instance has been clicked, but rather that the GWT module will call my server-side code for a given word and that the server side will return a valid definition. I do this by pushing (no pun intended!) the
onModuleLoad() method's definition-retrieving logic into a
testable method, as shown in Listing 3:
Listing 3. The refactored onModuleLoad method defers to more testable methods
public void onModuleLoad() {
HorizontalPanel inputPanel = new HorizontalPanel();
inputPanel.setStyleName("disco-input-panel");
inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
Label lbl = this.getLabel();
inputPanel.add(lbl);
TextBox txBox = this.getTextBox();
inputPanel.add(txBox);
Button btn = this.getButton();
btn.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
submitWord();
}
});
inputPanel.add(btn);
inputPanel.setCellVerticalAlignment(btn,
HasVerticalAlignment.ALIGN_BOTTOM);
if(RootPanel.get("input-container") != null) {
RootPanel.get("input-container").add(inputPanel);
}
Label output = this.getOutputLabel();
if(RootPanel.get("output-container") != null) {
RootPanel.get("output-container").add(output);
}
}
|
As you can see in Listing 3, I've essentially deferred onModuleLoad()'s definition-retrieving logic to the submitWord method, which is defined below in Listing 4:
Listing 4. The meat of my Ajax application!
protected void submitWord() {
String word = this.getTextBox().getText().trim();
this.getDefinition(word);
}
protected void getDefinition(String word) {
WordServiceAsync instance = WordService.Util.getInstance();
try {
instance.getDefinition(word, new AsyncCallback() {
public void onFailure(Throwable error) {
Window.alert("Error occurred:" + error.toString());
}
public void onSuccess(Object retValue) {
getOutputLabel().setText(retValue.toString());
}
});
}catch(Exception e) {
e.printStackTrace();
}
}
|
The submitWord() method defers to another method dubbed
getDefinition(), which I can test using JUnit. The getDefinition() method is logically separated from UI-specific code
(for the most part) and can be called without concern for a button click. On the other
hand, state issues associated with asynchronous applications and the semantic rules of the Java language dictate that I can't completely avoid UI-related interactivity in my tests. Looking closely at the code in Listing 4, you can see that the getDefinition() method, which invokes an asynchronous callback, does manipulate some UI components -- namely an alert window for errors and a Label instance.
I can still verify the application's functionality by obtaining a handle to the output
Label instance and asserting that its text is the definition
for a given word. When testing via GWTTestCase, I'm best off
not attempting to manually force a state change on a component, but rather
letting GWT do it. For example, in Listing 4, I want to verify that the correct
definition is returned and placed into an output Label for a
given word. I don't actually have to manipulate a UI component to set the word, though; I can just call the getDefinition method directly and then assert that the Label has the corresponding definition.
Now that I've written my GWT application with testing in mind, I actually have to write the test for it, which means setting up the GWT's GWTTestCase.
GWTTestCase requires you to play by some rules if you want to benefit from its testing magic. Luckily, the rules are simple:
- All implementing test classes must reside in the same package as the GWT module
you're attempting to test.
- When running tests, you must pass in at least one VM argument to help specify which
GWT mode (hosted or Web) to run the test in.
- You must implement the
getModuleName()method, which returns aStringrepresentation of your XML module file.
Last, because an Ajax application that communicates with a server-side entity is asynchronous by nature, the GWT offers an additional Timer class that facilitates delaying JUnit long enough for asynchronous behavior to complete before an associated assertion.
Implementing getModuleName and the Timer class
As I mentioned earlier, the brunt of my testing effort will be focused on the getDefinition() method (shown in Listing 4).
As you can see from the code, the test is logically simple: pass in a word (such as
pugnacious) and then verify that the corresponding Label has the correct definition as its text. Simple, right? Don't
forget, though, that the getDefinition() method has some
asynchronicity associated with it through the AsyncCallback object.
The GWTTestCase class is abstract because its getModuleName() method is declared as such; consequently, when you
extend the class, you'll be required to implement getModuleName() (unless you are creating your own base abstract class for a framework). The module name is essentially the package structure where your GWT XML file resides minus the file extensions. For example, in my case, I have an XML file named WordModule.gwt.xml, which resides in a directory structure like this: com/acme/gwt. Consequently, the logical name of the module is com.acme.gwt.WordModule, which should remind you of the Java platform's normal packaging scheme.
Now that I've got a module name, I can begin defining a test case as in Listing 5:
Listing 5. You must implement the getModuleName method and provide a valid name
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.Timer;
public class WordModuleTest extends GWTTestCase {
public String getModuleName() {
return "com.acme.gwt.WordModule";
}
}
|
So far, so good, but I haven't tested anything yet! Because my Ajax application uses
the AsyncCallback object, I'll have to force JUnit to delay
running to completion when I call the getDefinition() method via my test case; otherwise, the test will fail due to no response. This is where GWT's Timer class comes in handy. Timer allows me to override
getDefinition()'s run method and finish the test case's logic within Timer instead. (The test case will be run in separate thread, effectively blocking JUnit from completing the overall test case.)
For example, with my test, I'll first call the getDefinition() method and then provide an implementation of a Timer's run() method. The run() method obtains the text of the output Label instance and verifies that the proper definition is there. After I've defined a Timer instance, I then need to schedule it to launch while also forcing JUnit to hang around for the Timer instance to complete. If this sounds complicated, don't worry, it's quite easy in practice. In fact, you can see the entire scenario in Listing 6:
Listing 6. Testing with GWT is a cinch
public void testDefinitionValue() throws Exception {
WordModule module = new WordModule();
module.getDefinition("pugnacious");
Timer timer = new Timer() {
public void run() {
String value = module.getOutputLabel().getText();
String control = "inclined to quarrel or fight readily;...";
assertEquals("should be " + control, control, value);
finishTest();
}
};
timer.schedule(200);
delayTestFinish(500);
}
|
As you can see, the Timer's run()
method is where I actually verify the functionality of my Ajax application and its use
of a remote procedure call. Note that the last step in my run method is to call the
finishTest() method, which signals that everything went as
expected and JUnit should run as normal without further blocking. In practice, you may
find you'll need to hone your delay times further depending on how long the asynchronous
behavior requires to complete. But the point of testing GWT applications through JUnit
is that you can do it without having to deploy the fully functional Web application to test it. As a result, you can test your GWT applications earlier and hopefully more often.
As I mentioned earlier, if you'd like to actually run your GWT JUnit tests, you'll have
to do a number of little things to configure a running environment. For example, to run
my tests via Ant's junit task, I have to ensure some files
are in my classpath and provide an argument to the underlying JVM. Specifically, when
invoking the junit task, I need to ensure the directory (or
directories) that hosts my source files (including tests) is in the classpath, and I
need to tell the GWT which mode to run in. I tend to use the hosted mode, which
means I use the www-test flag, shown in Listing 7:
Listing 7. Running GWT tests with Ant
<junit dir="./" failureproperty="test.failure" printSummary="yes"
fork="true" haltonerror="true">
<jvmarg value="-Dgwt.args=-out www-test" />
<sysproperty key="basedir" value="." />
<formatter type="xml" />
<formatter usefile="false" type="plain" />
<classpath>
<path refid="gwt-classpath" />
<pathelement path="build/classes" />
<pathelement path="src" />
<pathelement path="test" />
</classpath>
<batchtest todir="${testreportdir}">
<fileset dir="test">
<include name="**/**Test.java" />
</fileset>
</batchtest>
</junit>
|
Running the GWT tests then becomes a matter of invoking my build. Also note that GWT tests are fairly lightweight, so I can run tests frequently, or even continuously, as I would in a Continuous Integration environment.
In the GWT test case demonstrated in this article, you've seen the first steps to verifying that an Ajax application runs as it should. You could continue testing my example GWT application, for instance by testing a few more bounds cases, but I think my point has been made: Testing Ajax applications is easier than you would think, provided they've been written using a framework that incorporates testing features.
The key to testing GWT applications well (as is true of most applications) is to design the application with testing in mind. Also note that GWTTestCase isn't meant to be used for interaction testing. You cannot use GWTTestCase to simulate users directly. You can, however, use it to verify user interactions in an indirect manner, as I've demonstrated in this article.
- Participate in the discussion forum.
- "Simplify Ajax development using Cypal Studio for GWT" (Noel Rappin, developerWorks, June 2007): Learn how Cypal Studio for GWT helps create new GWT modules, supports the creation of remote procedure calls, and makes it easy to view and deploy your Web applications.
- "Ajax for Java developers: Exploring the Google Web Toolkit" (Philip McCarthy, developerWorks, June 2006): Philip McCarthy shows you what the GWT can do and helps you decide whether it's right for you.
- "Three steps to running GWT JUnit tests in Eclipse" (Andrew Glover, thediscoblog.com, April 2007): Learn how to create and run
GWTTestCases via Eclipse. - "
In pursuit of code quality: Programmatic testing with Selenium and TestNG" (Andrew Glover, developerWorks, April 2007): Learn how to run Selenium tests programmatically, using TestNG as the test driver.
-
In pursuit of code quality series (Andrew Glover, developerWorks): Learn more about writing quality-focused code.
-
developerWorks Java technology zone: Hundreds
of articles about every aspect of Java programming.

Andrew Glover is president of Stelligent Incorporated, which helps companies address software quality with effective developer testing strategies and continuous integration techniques that enable teams to monitor code quality early and often. Check out Andy's blog for a list of his published work.
Comments (Undergoing maintenance)





