You have to run the TC in hosted mode.
Workaround: call the native method in the test method.
I use the same JSNI initialization pattern in real applications and it
works fine.
Code example:
public class StaticJSNIInitializer extends GWTTestCase
{
static
{
jsInit();
}
private static native void jsInit() /*-{
// define a native function to call into GWT
}-*/;
public String getModuleName()
{
return "asquare.gwt.bugs.UnitTests";
}
public void testSimple()
{
// workaround: do JSNI initialization here
assertEquals(true, true);
}
}
Stack trace:
java.lang.UnsatisfiedLinkError: jsInit
at asquare.gwt.bugs.test.StaticJSNIInitializer.jsInit(Native Method)
at asquare.gwt.bugs.test.StaticJSNIInitializer.<clinit>(StaticJSNIInitializer.java:24)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
at junit.framework.TestSuite.createTest(TestSuite.java:131)
at junit.framework.TestSuite.addTestMethod(TestSuite.java:114)
at junit.framework.TestSuite.<init>(TestSuite.java:75)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.getTest(RemoteTestRunner.java:399)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:445)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Exception in thread "main"
--
Mat Gessel
http://www.asquare.net
You're quite right about this one, but I'm not sure there's any really
good alternatives. The problem is that your test case actually has to
serve two distinct roles. First, the compiled class file must be a
real JUnit TestCase so that it will fit into the JUnit framework
properly; the launch sequence for your test case turns around and
launches the JUnitShell which creates a hosted mode or web mode
environment to actually run the real test. Meanwhile, the source code
for you test case then serves in the second role, to be translated and
run inside the browser. The problem you point out lies in the conflict
between these two competing goals. A workaround could be to compile a
stripped-down class file that does not use natives, but couple it with
a source file that does. But that would force you to keep things
out-of-sync. If anyone has ideas on how to reformulate this to loosen
the restrictions, I'd be interested.
Scott
1. As I can see from stack trace above and from generated
Test-hosted.cmd, you just run junit.textui.TestRunner. So, most
probably it really just loads classes from disk.
2. Why not run something more complex, like mentioned JUnitShell (I
don't know what it does, probably something like GWTShell) that:
- initializes Tomcat;
- creates special class loader for JUnit and application classes;
- loads JUnit runner using this class loader and runs it.
Because you run JUnit from your class loader, it will ask for classes
using it, so you will able to replace "native" methods with something
executable. And you probably already have such kind of class loader for
GWTShell.
The problem is that GWT has no chance to gain control of the system
before the error occurs. Look at the stack trace carefully and you'll
see that every method on the stack is a JUnit framework method right up
to the point where the user's class is being initialized. There's
simply no opportunity that I'm aware of to step in and hijack the
process until *after* clinit and such, because we're implemented as a
superclass of the user's class.
Scott
This is known problem during writing of profilers. Before Java5 only
was was using native library. In Java5 you can write agents on Java.
See following option:
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see
java.lang.instrument
You can generate required .cmd files to run JUnit tests with agent
and users of IDE's can add this parameter manually. At least Eclipse
allows this. Later, developers of GWT supporting plugins can add
special launch configuration type that will add such parameters
automatically.