We are a user experience design and software development firm
Hire us to design your site, build your application, serve billions of users and solve real problems.
Speaking of GroovyTestCase, I experimented a bit with using both it and JMockit as an alternative to GWTTestCase. This may sound strange at first-- after all, the point of GWTTestCase is to exercise your code in the way it will eventually execute as JS at runtime. What I've effectively done is to run my tests from within Groovy against GWT classes compiled-as-Java. First I will go through an example of what I'm talking about, then I will summarize some of the thoughts I had as a result of going through this exercise.
Below I have an implementation of KeyboardListener which modifies the contents of a TextArea based on keypress. I want to test the behavior of this listener. Here's what I did:
1) Write a test. I did this using Groovy, but you could just as easily write it in Java using JUnitTestCase if you prefer. This particular test verifies that my listener will append an equal amount of whitespace to the next line when the <enter> key is pressed, and position the cursor at the end of that new line. Very simple stuff, but worth testing even outside of GWTTestCase. I find that the Groovy style keeps this test case a lot shorter than it would otherwise be in Java.
class SampleTest extends GroovyTestCase {
private static final char KEY_ENTER = (char) KeyboardListener.KEY_ENTER
private static final int NO_MODIFIERS = 0;
protected void setUp() throws Exception {
Mockit.redefineMethods(GWT, MockGWT)
}
public void testEnterKey() {
def keyListener = new MyKeyListener()
def textArea = new TextAreaFixture(text: " abcd", cursorPos: 8 )
keyListener.onKeyPress(textArea, KEY_ENTER, NO_MODIFIERS)
assertEquals "Content is incorrect", " abcd\r ", textArea.text
assertEquals "Cursor position is incorrect", 13, textArea.cursorPos
}
}
class TextAreaFixture extends TextArea {
String text;
int cursorPos;
}
2) Mock out com.google.gwt.core.client.GWT. This ultimately comes down to mocking out the 'create' method. Notice below that we make a special case for 'DOMImpl.class'. The reason for this is that the actual DOMImpl is decided at runtime. It is also referenced in the constructors of the widget we are testing (i.e. 'TextArea()'). Note that this class needs to be a Java class (i.e. non-Groovy) due to the way in which JMockit redefines methods on existing Java classes. As for why I return 'null' for everything else, I do this to fail fast in any cases I'm not testing. As it turns out, this is all I need for the testcase above.
public class MockGWT {
public static Object create(Class classLiteral) {
try {
if (classLiteral.equals(DOMImpl.class)) {
return new MockDOMImpl();
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3) Subclass com.google.gwt.user.client.impl.DOMImpl. Ah-hah! Here's the catch... This step provides us just enough code to satisfy construction of our Widgets. For my purposes, I let my IDE generate this subclass for me, and stuck with the default implementations it generated. I return an instance of it above.
public class MockDOMImpl extends DOMImpl {
public Element createElement(String tag) {
return null;
}
[...]
}
The approach to this style of testing intercepts any calls on the Widgets themselves before anything is allowed to hit the underlying DOM implementation. What I provide above is just an example of how this could be done, specifically when testing behavior of custom classes that happen to inherit or reference GWT classes. Testing things like layout or UI concerns is another matter, but one which I would not consider 'interesting' enough for unit testing, since most of the behavior on the UI can be easily inspected by viewing Widgets as simple beans.
So why would you want to attempt this anyway? First, I happen to like working in Groovy, so I crave more reasons to do exactly that. Second (and more pertinent), much of behavior I find interesting in GWT apps revolves around simple message passing. I trust GWT enough to do its job once I leave the Java space. Beyond this are some philosophical differences I have with GWTTestCase, particularly since GWTTestCase makes it hard to maintain a larger body of unit tests, requires additional configuration in your module file (or as I've done before, constructed a different "test-only" module file that inherited from two module files), did not let me set up tests the way I would like to, etc. I could go on. When unit testing, very little of the logic I write is made less interesting or less relevant outside of a JS runtime environment, so where possible I would like to gain some benefits from treating everything as Java.
There are other benefits. There's the noticeable speedup in execution time when running entirely within the JVM. There's also less restrictions on the test class itself with regards to instance variables, language (i.e. Groovy), dependencies in outside packages, etc. Why wouldn't I want such flexibility in my test code? I really want my test code to be as concise and expressive as possible, particularly as my apps get larger and more complex. I think having alternative ways of approaching this kind of testing brings a lot of these types of benefits.
Topics: Ajax Development, Groovy, GWT, Testing, Tutorials, Unit Tests
Hire us to design your site, build your application, serve billions of users and solve real problems.
Hi Ivan,
This approach is great and mocking GWT class is brilliant idea.
Unfortunately this does not work with GWT 1.5 .
The problem is that in GWT 1.5 some inner mechanism to create Element objects has changed.
For example in GWT 1.4 the method of DOM class to create Button was like this:
public static Element createButton() {
return impl.createElement(”button”);
}
So mocking of DOMImpl class is sufficient in this case.
But in GWT 1.5 we have something like:
public static Element createButton() {
return Document.get().createButtonElement().cast();
}
Here get() method of Document class is a native method and it seems like it is not possible to mock it (at least I was not able to do this):
public static native Document get() /*-{
return $doc;
}-*/;
Does it mean that this approach is not possible to use with GWT 1.5 or may be some workaround exists?
Thanks in advance,
Leonid
Comment by Leonid, Thursday, June 19, 2008 @ 12:32 pm