Bruce Johnson has posted a new entry on the Google Web Toolkit blog entitled Getting to really know GWT, Part 2: JavaScript Overlay Types. In the article, Bruce shows us how to use the new GWT 1.5 JavaScript overlay types to easily parse JSON into Java objects.
I previously wrote about Testing JSON Parsing with GWTTestCase using the original GWT JSON parsing libraries, so I'm posting a new example of testing JSON parsing using the new JavaScript Overlay Types.
Bruce's example parses some customer data into a Java Customer object. Here's the source from the article, with an additional "parse" factory method that parses a Customer object from a String containing JSON.
class Customer extends JavaScriptObject { // Overlay types always have protected, zero-arg ctors protected Customer() { } // Typically, methods on overlay types are JSNI public final native String getFirstName() /*-{ return this.FirstName; }-*/; public final native String getLastName() /*-{ return this.LastName; }-*/; // Note, though, that methods aren't required to be JSNI public final String getFullName() { return getFirstName() + " " + getLastName(); } // Parse from JSON, based upon code from // http://sites.google.com/site/io/gwt-and-client-server-communication public static final native Customer fromJson(String input) /*-{ return eval('(' + input + ')') }-*/; }Here's a simple test case demonstrating using an existing JavaScript object as well as parsing from JSON:
public class CustomerTest extends GWTTestCase { // Required by all GWTTestCases public String getModuleName() { return "JsniOverlays"; } public void testUsesUnderlyingJavaScriptObject() { Customer customer = testCustomer(); assertEquals("Jimmy", customer.getFirstName()); assertEquals("Webber", customer.getLastName()); assertEquals("Jimmy Webber", customer.getFullName()); } // Sample test fixture private native Customer testCustomer() /*-{ return { "FirstName" : "Jimmy", "LastName" : "Webber" } }-*/; public void testParsesFromJson() { Customer customer = Customer.fromJson("{ FirstName: \"Murphy\", LastName: \"Plankton\" }"); assertEquals("Murphy", customer.getFirstName()); assertEquals("Plankton", customer.getLastName()); } }
Notice that testUsesUnderlyingJavaScriptObject refers to a test fixture creation method written in JavaScript using JSNI. This test is similar to Bruce's example which coerces a JavaScript object into a Java object.
testParsesFromJson passes in a String containing JSON, which is what a remote service might do after receiving a response from a server. Notice how I'm not testing getFullName again in this test; it's already been verified in another test.
Testing Considerations
One challenge with using overlay types is that your objects contain JSNI -- which means that they fail to run in a plain JUnit TestCase. Instead, they must be run in a GWTTestCase, and that means a much slower test.
To solve this problem, I'm tempted to create an interface for the methods I need on my Customer object, then create a Test Double to use in my tests. This would encourage referring to the object by it's interface, which is a good thing.
Still, it seems a little annoying that I need to jump through some hoops so that my core domain object (Customer) doesn't accidentally call JavaScript in my tests. I'm not sure if this would be a real problem or not; experience will tell.
Congratulations to the GWT team on their hard work in making some great improvements to GWT 1.5!