The postings on this site are the contributor's and don’t necessarily represent IBM’s positions, strategies or opinions.
May 14th, 2008 Alex Moffat Posted in GWT | 14 Comments »
GWTTestCase is great for testing the command and model parts of a GWT UI. However, it has one big drawback, there’s no way to write a test case that makes an RPC call to an external server, something you can do with GWT hosted mode using the -noserver flag. There are two things that contribute to this situation.
First, GWTTestCase is actually in two pieces. One executes in the hosted mode browser and calls the actual code under test, either as JavaScript or bytecode. The other executes in an embedded tomcat server which both serves the JavaScript/bytecode to test and communicates with the JUnit test framework to log results etc.
Second, the same-origin policy prevents JavaScript loaded from site A making XmlHttpRequest calls to site B. This means you can’t just load the JavaScript for the test case from the embedded server and then make the RPC call to a different server.
Our solution to this problem turns out to be pretty simple. The code and configuration for the embedded server is in the gwt-dev-<platform>.jar file where <platform> is windows, mac or linux. This includes a web.xml file com/google/gwt/dev/etc/tomcat/webapps/ROOT/WEB-INF/web.xml that maps GWTShellServlet to the url-pattern /* so all requests to the embedded server are directed to this servlet. The solution then is to somehow replace GWTShellServlet so that we can handle the requests the way we’d like to.
To do this we override web.xml by placing a modified version earlier in the classpath used for executing the test cases than the gwt-dev-<platform>.jar file. This web.xml has a different mapping for the /* url-pattern so that all requests to the embedded server are directed to a new servlet.
The new servlet is a subclass of GWTShellServlet and does one of two things with each request. If it’s a request for code related to a test case then it handles it in the same way as GWTShellServlet, otherwise it proxies the request to a different server. In this way all requests from the client are made to the embedded server but when they get there some are actually served by the external server.
We override two methods, service(ServletRequest, ServletResponse) and init(ServletConfig) in the GWTShellServlet subclass. In the service method the requested uri is examined and if it is for a test case then it is passed to super.service so that the GWTShellServlet code can handle it. If it’s not for a test case then it is proxied to the external server. We need configuration to define what is considered a test case and where the external server is. This is read from web.xml in the init method. <init-param> values define the host and port for the external server and the fully qualified class names for all the test classes. If a request uri starts with a test class FQN then it’s served locally, otherwise it’s proxied. This does mean that we have to add a new entry to web.xml for each new test class. An alternative would be to use a particular package for all test classes and examine the uri for that prefix instead.
Overall this has proved to work very well. We use maven for our build and use the cargo-maven2-plugin to start and stop the external server around the execution of the test cases.
Here is the source for the
Proxy Servlet for GWTTestCases and the web.xml file. The web.xml must be placed in a directory corresponding to com.google.gwt.dev.etc.tomcat.webapps.ROOT.WEB-INF. When you run the test case it will be copied into tomcat/webapps/ROOT/WEB-INF/web.xml.
May 26th, 2008 at 5:37 am
Hi Alex,
thanks for your post about -noflag solution in GWTTestCase. You described exactly what I’m looking for. Can you please provide me with source code of your solution ? (It will save me lot of time).
Thanks in advance.
Peter
May 26th, 2008 at 7:08 pm
Thanks. I’ll see what I can do when I get into the office tomorrow.
May 28th, 2008 at 1:13 pm
Thanks for your great work. It helps me a lot.
Peter
October 12th, 2008 at 11:49 am
[...] you can test your GWT front-end UI code using JUnit very easily, and as explained by Alex in this post you can also test the full interaction (client and server) through JUnit tests. This is huge for us [...]
June 14th, 2009 at 12:03 pm
Hi there,
I read your post with interest as I had the same problem; I am now trying to use your solution to implement a GWTTestCase that tries to make a GWT RPC call to an external server.
I am making progress, however I hit a snag because in hosted mode (which the test case seems to use), the url to the external service seems to be:
‘http://serverhostname//myservice’
whereas the external server is expecting:
‘http://serverhostname/myservice’
I then tried to manually set the service entry point url in my test code, instead of using the usual ‘getModuleBaseURL()’, but then the test fails. So instead, I altered the source code of ProxyServlet to change url’s matching ‘http://serverhostname//myservice’ to ‘http://serverhostname/myservice’ when proxying.
However I then hit another problem because the GWT RPC server side code on the external server uses the ‘module base url’ encoded in the RPC request to try and get the serialization policy file (the *.gwt.rpc files) from the following location:
‘http://serverhostname//xxxxxxxxxxxxxxx.gwt.rpc’
Whereas on the external server all the serialization policy files are as follows:
‘http://serverhostname/xxxxxxxxxxxxxxx.gwt.rpc’
Although my modified code changes the entry point url, it doesn’t change the ‘module base url’ in the RPC request, and as such the server can’t find the serialization policy file and the serialization of the RPC response fails.
Any sugggestions?
Thanks
Alex.
June 15th, 2009 at 9:54 am
That’s an odd problem. It’s not something I’ve run into unfortunately, I’ve never had the url appear different between hosted and non-hosted mode. We use the proxy technique here at Lombardi and it works fine. I’ll take a closer look this evening though.
July 6th, 2009 at 1:01 pm
I have the same problem with the serialization policy as Alex.
July 6th, 2009 at 2:56 pm
But the problem is that the path to the gwt.rpc files includes the junit module name, not my actual module name.
I overcame this by copying the *.gwt.rpc files into webroot/junitmodulename/ on my server
February 9th, 2010 at 9:20 am
He Alex,
Long time ago….:)
Anyway: I came across your post as I am looking how to do the above in gwt 2.0…
That is: how to run noserver gwt tests that extends GWTTestCase against an external server…
I tried some things, like using the war/WEB-INF/web.xml, but without any success.
Is the above still the way to go in gwt 2.0? or does gwt 2.0 offer better/easier ways to do this?
Regards,
Ed
February 10th, 2010 at 10:05 am
We’re still using the proxy servlet technique in GWT 2.0. I don’t think there is an easier way to get it working.
February 10th, 2010 at 11:13 am
Hmmm too bad, I was afraid you would say that
February 24th, 2010 at 4:08 am
Hi Alex,
I would like to hear how you deal with the policy files in noserver mode?
I am trying to improve the policy file usage and started a thread for it:
http://groups.google.com/group/google-web-toolkit-contributors/browse_thread/thread/600aa2f851f33606
Please some feedback?
Ed
February 24th, 2010 at 5:51 pm
Thanks for your quick response.
Ed
June 20th, 2010 at 10:50 am
[...] Just a link to a post I wrote at work on how to implement a -noserver flag for GWTTestCase. [...]