tags:

views:

343

answers:

2

I'm trying to use jMockit to stub out a call to netscape.javascript.JSObject which is unfortunately an abstract class with a few native methods in it, both of which don't play well with jMockit. Is there any way for me to stub this out, either with jMockit, or using plain java (I'd prefer not having to introduce a new library if possible)? The code I'm trying to inject the mock in looks something like the following (don't ask):

JSObject win = JSObject.getWindow(this);
String someVal = (String) win.call("foo", new String[] {""});

Unfortunately I can't modify the code in question (once again, don't ask), so the only way I can do any sort of unit testing on it is if I can figure out how to stub out or otherwise eliminate the calls to JSObject. My current mock implementation returns a new instance of itself for the call to getWindow, and returns a constant string for the call to call but of course it fails at runtime complaining about

class redefinition failed: attempted to change method modifiers

which from my research I've discovered means it's attempting to mock a native method.

Edit: Due to the target platform I need to use JDK 1.5 to build/test so any solution must be viable on 1.5.

+1  A: 

Which version of JMockit are you using ? According to this, you should be able to mock native methods with no problems.

@Test
public void mockNativeMethod() {
    new MockUp<Runtime>() {
        @Mock
        @SuppressWarnings("unused")
        int availableProcessors() {
            return 999;
        }
    };
    assertEquals(999, Runtime.getRuntime().availableProcessors());
}

EDIT: As you've noted, this doesn't work with Java 1.5. I fear the only solution is to wrap your native objects with a decorator that simply operates as a pass-through. That's a bit of a headache but it will permit mocking/interception etc.

Brian Agnew
I'm using the latest version, but the problem is I'm constrained to use JDK 1.5 which doesn't provide the interface to allow instrumentation of native methods required to mock them.
Orclev
A: 

As Brian pointed out, JMockit supports the mocking of native methods. (The Expectations API, shown in the test below, also supports it.)

@Test // this only works under JDK 1.6+; JDK 1.5 does not support redefining natives
public void mockNativeMethod()
{
   new Expectations()
   {
      final System system = null;

      {
         System.nanoTime(); returns(0L);
      }
   };

   assertEquals(0, System.nanoTime());
}

The problem, though, is that the JVMTI implementation in JDK 1.5 is not very mature, not supporting redefinition of native methods.

I understand you need to deploy the production code in a JDK 1.5 environment, but that should be no impediment to using JDK 1.6 in your development environment. Normally, it should be perfectly safe and easy to run the tests on JDK 1.6, and even to compile all source code with the JDK 1.6 compiler. You simply need to specify the javac "target" as "1.5". In Eclipse, there is a "JDK Compliance" configuration under "Project Properties -> Java Compiler".

Rogerio