Is there a way to run GWT JUnit tests in -noserver mode? I'm asking
because my dev environment relies on -noserver and I'm running into
conflicts when I try to run a GWT test case that uses RPC.
Specifically, I get this error message in the log:
ERROR: The module path requested, /com.example.Tests/, is not in the
same web application as this servlet, /gwt_tests. Your module may not
be properly configured or your client and server code maybe out of
date.
WARNING: Failed to get the SerializationPolicy
'C384F35B503938C7EC9B9EB6B150D06F' for module
'http://localhost:49730/com.example.Tests/'; a legacy, 1.3.3
compatible, serialization policy will be used. You may experience
SerializationExceptions as a result.
com.example.Tests is the name of the GWT module I created for testing
purposes. (I'm not sure if it was necessary to do that--it just
inherits my usual module, plus adds a <source> tag for some test-only
code, and a couple of <servlet> tags.) /gwt_tests is where a
test-specific RemoteServiceServlet is mounted on the "real" JBoss
server that I usually access with -noserver. The test module loads a
ProxyPassServlet instance (found here:
http://groups.google.com/group/Google-Web-Toolkit/msg/337cdc4ec7d6019d)
into the embedded Tomcat and it forwards all the requests to the real
JBoss server.
The problem appears to be that the client-side code sends the module
name and the serialization policy's strong name as part of the RPC
request and, because I'm forwarding the request to a different server,
the module name doesn't match the context path of the real servlet.
Also, the serialization policy isn't written until the GWT compiler is
triggered by the test case, so even if I modified the ProxyPassServlet
to massage the body of the RPC request, the serialization policy isn't
available to preload into the real JBoss server.
So, it seems to me I _might_ be able to fix this problem by
pre-compiling the client-side code that runs in the test environment
and also modifying the ProxyPassServlet to replace references to
"/com.example.Tests/" with "" in the body of the request, but I can't
see an obvious way to do either of these things, so it feels like I'd
be thrashing about in the dark. It also feels like I must be missing
something that would greatly simplify my life. I tried passing
-noserver to the compiler via the gwt.args system property, thinking
it might "just work", but the JUnitShell class doesn't provide an
ArgHandler for the -noserver argument, so it fails with an "invalid
command line argument" exception, or some such. (I also realized that
there's no obvious way to get the server-side of the JUnit RPC hack
into my real server so -nosever might be impossible anyway.)
Has anyone solved this problem before?
Ian
--
Tired of pop-ups, security holes, and spyware?
Try Firefox: http://www.getfirefox.com
Hmm.... I should have STFW'd first. It seems there were some posts
in October with roughly the same problem and no solution. Has there
been any change since then?
For the curious, I'm using a generator to pre-compile all the
information that real JUnit figures out at runtime. For now, you mark
test cases with @gwt.Test in a comment, rather than @Test as an
annotation, but I figure that should be easy to port to real
annotations once GWT 1.5 is released. The generator also supports
@gwt.Before, @gwt.After, @gwt.BeforeClass, and @gwt.AfterClass. I
intend to support expected exceptions. At the moment I only support
JUnit 4-style tests, but it should be easy to add support for JUnit
3.8-style tests, too.
Still on my TODO list is:
- port the Assert class and some other JUnit-related code that is
necessary for a proper test case
- get the generator out of proof-of-concept mode so it can generate
code based on annotations rather than a hard-code list of classes and
methods
- add support for JUnit 3.8-style tests
- switch the test harness implementation from internal iteration to
external iteration so that test cases can do asynchronous things, and
so that the UI can be updated after each test, rather than once at the
end
- figure out how to get test results off the client
Once I'm a little further along, I'll petition to add this code to the
GWT incubator.
Ian
PS For those that are seeing this on the -Contributors list and
haven't seen the post that I'm replying to, the reason for the port is
that the existing JUnit support in GWT doesn't seem to support the
-noserver use-case. Porting JUnit to GWT allows me to write a GWT app
that can run my tests in a -noserver environment and test out my RPC
end-points. It also means I can use JUnit 4-style test cases, but
that's really just a bonus.
I was also thinking of eclipse JUnit integration over that for running
complete sets of GWT RPC test cases or even "wizards" for producing the test
case code skeletons.
I was also thinking of eclipse JUnit integration over that for running
complete sets of GWT RPC test cases or even "wizards" for producing the test
case code skeletons.
-----Original Message-----
From: Google-We...@googlegroups.com
[mailto:Google-We...@googlegroups.com] On Behalf Of Ian Petersen
Sent: Friday, December 07, 2007 10:08 PM
To: Google Web Toolkit; Google Web Toolkit Contributors
Subject: Re: JUnit integration
In case anyone out there is interested, I've managed to do everything
on my TODO list, except get the results off the client.
Modifying the code to run asynchronously took a few iterations before
I came up with an abstraction that works, so I still need to go
through the code and clean it up a bit, but everything basically
works. I'll be taking a break from coding for the Christmas holidays,
so I won't get much further until the new year, but I'm finding this
work to be very fruitful, so I'll make sure to post the results once
it's more polished.
Ian
Basically, junit4gwt is a port of JUnit 4.4 that runs as a GWT client
application. My motivation for writing it in the first place was to
allow me to test my GWT RPC endpoints in -noserver mode, which seems
impossible with the bundled JUnit integration. Since I was starting
"from scratch", I decided to port version 4 rather than version 3,
which makes it a little easier to create test cases. I've included a
sample test class below that shows off the features. To see it run,
you can create a new project, inherit the ca.petersens.gwt.JUnit4
module and set your entry point to
ca.petersens.gwt.client.org.junit.runner.JUnitCore. The result will
be a TextArea that shows output similar to a command line run of JUnit
4.4.
Still missing from the code that I've uploaded is an easy way to get
the test results out of the browser. As the code exists right now,
you have to manually run the tests and observe the results. To
facilitate automated testing, it would be nice if there was a way to
post the test results to the server and have a server-side process
that aggregates the results with the results from the rest of your
test suite and does something smart on failure. I think you could
create a custom solution for your testing environment by implementing
a RunListener that posts the results to an aggregation URL, but I
haven't spent any time on it myself because of deadlines elsewhere in
my project.
Please let me know if you find it useful, or if you run into any
trouble. My thanks to Jacob Rank for being my beta tester and
discovering a bug while running in hosted mode.
Ian
Sample code:
package com.example.gwt.client;
import ca.petersens.gwt.client.org.junit.Assert;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
// Extend Assert to get easy access to the static methods therein.
// Once GWT 1.5 is out you can quit extending Assert and just use
// import static.
public final class BasicTest extends Assert {
/**
* @BeforeClass
*/
public static void setUpClass() {
Window.alert("In BasicTest.setUpClass()");
}
/**
* @AfterClass
*/
public static void tearDownClass() {
Window.alert("In BasicTest.tearDownClass()");
}
/**
* @Before
*/
public void setUp() {
Window.alert("In BasicTest.setUp()");
}
/**
* @After
*/
public void tearDown() {
Window.alert("In BasicTest.tearDown()");
}
/**
* @Test
* @Async
*/
public void testAsyncFailure() {
new Timer() {
public void run() {
fail("This should appear in the summary as a failure");
}
}.schedule(5000);
}
/**
* @Test
* @Async
*/
public void testAsyncSuccess() {
new Timer() {
public void run() {
succeed(); // signal successful termination of asynchronous method
}
}.schedule(5000);
}
/**
* @Test
*/
public void testFailure() {
fail("This should appear in the summary as a failure");
}
/**
* @Test
*/
public void testSuccess() {
assertEquals(0, 0);
}
/**
* @Test
* @Expects IllegalArgumentException
*/
public void testExpectedException() {
throw new IllegalArgumentException();