Building GWT Applications with MVP and Issues with Overlay Types
MVP has recently become a popular strategy for structuring GWT applications. This is largely due to its testability and Ray Ryan's Best Practices For Architecting Your GWT App from this year's Google I/O. GWT, by itself, is simply a widget toolkit and doesn't ship with any sort of MVC (or MVP) framework.
On my current project, we're using GXT, a GWT implementation based on ExtJS. It has its own MVC framework, but it has very little documentation and can be confusing when using it with GWT's History management. At one point, I attempted to make it more understandable by writing a blog entry on GXT's MVC Framework.
One of my initial assignments was to decide if we should use MVP or MVC. Regardless of which one was chosen, I was also tasked with deciding if we should use an existing framework or write our own. After watching Ray Ryan's session on YouTube and recalling my frustration with GXT MVC on my last project, I quickly became convinced MVP was the answer.
To test my "MVP is best for our project" theory, I did a spike to implement it. I used the GWT MVP Example tutorial as a starting point and added the following libraries to my project.
- GWT-Presenter: An implementation of the MVP pattern.
- Google Gin: Dependency Injection based on Google's Guice.
- GWT-Log: A log4j-style logger for GWT.
Implementing the MVP pattern itself was relatively straightforward, but I did encounter a few issues. I'm writing this post to see if anyone has solved these issues.
MVP Implementation Issues
The first issue I ran across was GXT's widgets don't implement standard GWT interfaces. To try and figure out a solution, I posted the following on Twitter:
"Wondering if it's possible to do MVP with GXT since it's buttons don't implement standard GWT interfaces."
The best response I received was from Simon Stewart (founder of the WebDriver project, works for Google):
"Put the GXT buttons in the View. Let that turn DOM events into semantic events."
He also pointed me to his tdd-gwt-gae project which shows many techniques for unit testing (with jMock) and integration testing (with GWTTestCase). Using Simon's examples, I was able to determine an initial strategy for implementing MVP with GXT.
My strategy is instead of getting widgets from the view and adding handlers, you add handlers to to the view and it takes care of adding them to the widgets that should listen for them. This seems to work, but my View interface has a lot of void methods, which is a bit different than standard MVP patterns I've seen.
The 2nd issue I encountered is with unit testing. Unit testing can be be performed on MVP applications by mocking out any dependencies that use JSNI. Classes that use JSNI are not testable with plain ol' JUnit and typically requires you to use GWTTestCase, which can be slow and cumbersome. This isn't to say that GWTTestCase isn't useful, just that it has its place.
When unit testing MVP applications, the recommended practice seems to be you should test presenters and not views. Services fall into a similar "don't unit test" category because they'll need to connect to the server-side, which won't be running in a unit testing environment.
This is where I ran into a major issue that I don't have a solution for.
eventBus.fireEvent(GwtEvent) after the JSON parsing has happened. This means I can't fully test the flow of a presenter if event firing happens in a callback.
In attempt to try different mocking techniques for callbacks, I created a test that uses two recommended EasyMock-ing strategies. The first is a "CallbackSuccessMatcher" and is described in more detail in Testing GWT without GwtTestCase. The second technique uses a "CallbackMockSupport" class to allow EasyMock expectations such as expectLastCallAsync() and expectLastCallAsyncSuccess(T). You can read more about this technique in Test driven development for GWT UI code with asynchronous RPC.
Both of these examples use RPC, which typically has callbacks that have an onSuccess(T type) method. It's easy to use these callbacks in unit tests since T is a POJO and the onSuccess() method contains no JSNI code.
Currently, I see a few possible solutions to this problem:
- Figure out a way to detect when unit tests are running and add if/else logic to callbacks.
- Modify presenters and services so a callback can be set that is unit test-friendly.
- Make JSOModel an interface that can be replaced/mocked in tests.
The last solution seems like best one, but I'm also curious to know what others are doing. My hunch is that most GWT apps use RPC and haven't run into this issue.
I'm generating our JSO DTO objects, so when I hit this, I made the generator generate the JSO, a pure Java DTO, and the interface they both implement. I then use a factory to create the right one depending on the context. This is working well and I can test using GWT Test case with JSO DTO objects, or Java DTO objects in plain JUnit tests.
So far the dual impls seem to work fine since they are generated. I'm not an uber mocker, but that may have also made it work. The only big gotcha I hit was where I needed generics.
Keep the posts coming.
Posted by Robert Zaleski on September 22, 2009 at 06:23 PM MDT #
A lot of the GXT widgets use subclasses of "BaseModel". Do you have those at all or do you convert to them from your overlay types? Or do you use a "JSONReader" for those widgets?
You could use the binding annotations of Guice to completely get rid of this factory.
Posted by Sakuraba on September 23, 2009 at 03:20 AM MDT #
@Sakuraba - I created BaseGXTModel and BaseGXTTreeModel classes that extend my AbstractJSOModel and implement the appropriate GXT interfaces. I copied much of the code from GXT's BaseModel and BaseTreeModel.
I'm using GIN on my project, so I'm interested in hearing more about how you guys envision switching the JSON parsing implementation. Here's what one of my callbacks looks like currently:
How would this be refactored to allow both overlay types (as shown here) and testing (w/o overlay types)?
Posted by Matt Raible on September 23, 2009 at 09:31 AM MDT #
Posted by Etienne on September 29, 2009 at 04:11 AM MDT #
Thx for the Post
I'm new with GWT and GWT EXT, a client wants gxt,
I was googling and saw google guice and there video.
Very nice, I tried to add it in. but all the gwt components are used and no GXT so again googling around, and found your post,
looked at MVP4G, this is I think the solution, or write it yourself of corse.
Is it also possible to use EJB3 and JPA with it on Websphere Application Server?
thx in advance
Posted by toine on September 30, 2009 at 01:32 AM MDT #
> Is it also possible to use EJB3 and JPA with it on Websphere Application Server?
It should be possible to use anything on the server-side of a GWT application. You simply need to expose your services as RPC or HTTP endpoints.
Posted by Matt Raible on September 30, 2009 at 07:03 AM MDT #
Posted by John Ipson on September 30, 2009 at 11:43 AM MDT #
I have a question.. I'm using the MVP4G framework, that works fine, but i need to get for exampe a UserBean back in the Presenter ( to fire an event ).
The bean is EJB3 with JPA ... I recieve the userbean but when i want to parse it back i get a serialize error ..
Do you have a solution for this. or is it only possible with hibernate
Posted by toine on October 09, 2009 at 03:08 AM MDT #
I think you need a JSON-type DTO to pass objects like Java beans across the wire. I have experienced this with both JPA and Hibernate where you need to copy your standard Entity bean into a DTO before pushing it out to the client. Interestingly, I have found that Ibatis will work without the DTO layer. In GXT I can have my bean extend BaseModel and Ibatis will map it directly to the data in the database and I get no serialization errors. Also Ibatis version 3.0 has added alot of Java 5 support (i.e. annotations, etc)
Posted by Rick Smith on October 13, 2009 at 09:31 AM MDT #
Posted by Araminos on October 22, 2009 at 09:00 PM MDT #
"My strategy is instead of getting widgets from the view and adding handlers, you add handlers to to the view and it takes care of adding them to the widgets that should listen for them. This seems to work, but my View interface has a lot of void methods, which is a bit different than standard MVP patterns I've seen."
I just looked at the source code of tdd-gwt-gae, but the pattern of adding a listener to the view doesn't make much sense to me. I don't see how doing that directly is better than registering handlers with an event bus, and to me the whole point of using an interface like e.g. HasClickHandlers is that it is very easy to provide mock view that you can let send out the UI interactions you expect and test that; if you just register an event listener you lose that ability again. Used like that, MVP doesn't buy you much imho.
So... I guess back to extending components again to let them implement the interfaces you are interested in. It's a pity the GXT authors haven't displayed much interest in supporting MVP better for their customers. Any new insights from your side Matt?
Posted by Eelco Hillenius on December 07, 2009 at 11:53 PM MST #
Any new insights from your side Matt?
Unfortunately, there's been no movement from the GXT folks on this. One thing we've started doing is referencing GXT widgets in our presenters. Since they're classes, not interfaces, we've been mocking them with EasyMock's Class Extension.
Posted by Matt Raible on December 08, 2009 at 09:53 AM MST #
Posted by Edoardo "Dado" Marcora on March 24, 2010 at 07:31 PM MDT #
That sounds like a good strategy to start adopting the MVP pattern in an exsiting GXT project, can you please explain with some detail how do youbind the view to the controller/presenter? i think thats exactly what we need for our current project.
Thanks in advance for your help.
Posted by Carlos Andres Neva Vargas on July 16, 2010 at 03:05 PM MDT #
Posted by jca on August 16, 2010 at 07:00 AM MDT #
You might like to take a look at this post -> http://floonit.blogspot.com/2010/10/gwt-ext-gwt-gxt-mvp-and-gwtp.html and the sample project -> http://code.google.com/p/gwt-projecttimer/.
Posted by Rob on November 13, 2010 at 06:14 AM MST #
For last suggestion: "Make JSOModel an interface that can be replaced/mocked in tests."
if a JsoModel must be created in presenter,How to?
1,JsoModel model=new JsoModelImpl();
- JsoModelImpl contains jsni can't be tested with pure junit,GWTTest must be used.
2,JsoModel model=factoryAsField.createModel();//factoryAsField is injected
- Actually,too many factories must be created and injected.
How has you solved this problem?
Posted by Alex Luya on June 25, 2012 at 08:08 AM MDT #