JavaScriptを使用するアプレットの単体テスト
JavaScript を呼び出す Java アプレットを JUnit などで単体テストする場合、当然ながら正式なアプレットの実行環境ではないため JSObject#getWindow() の呼び出しで例外が発生してしまうことになる。テストのために JSObject を自作のスタブで置き換えられると便利であろう。今回はこの JSObject の置き換えについて調べてみる。なお調査に使用する JDK は以下の通りである。
java version "1.5.0_10" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_10-b03) Java HotSpot(TM) Client VM (build 1.5.0_10-b03, mixed mode, sharing)
まず netscape.javascript.JSObject のクラスファイルを紐解いてみよう。格納されている JAR ファイルは以下の通りである。
{$java.home}\jre\lib\plugin.jar
この JAR ファイルから JSObject のクラスファイルを取得し、javap のアセンブラコードを手作業で復元して試行錯誤した結果、肝心な部分のコードは以下のように記述されるようだ。
public abstract class JSObject{
// ...
public static JSObject getWindow(Applet applet) throws JSException{
JSObject jsobject;
if(applet != null){
try{
String mayscript = applet.getParameter("MAYSCRIPT");
AppletContext context = applet.getAppletContext();
jsobject = null;
if(context instanceof JSContext){
JSContext jscontext = (JSContext)context;
jsobject = jscontext.getJSObject();
}
if(jsobject != null){
return jsobject;
}
} catch(Throwable ex){
throw new JSException(
JSException.EXCEPTION_TYPE_ERROR, ex); // at JSObject.java:159
}
}
throw new JSException(); // at JSObject.java:162
}
}
どうやらアプレットが保持している AppletContext を sun.plugin.javascript.JSContext のサブクラスとして実装すればスタブへ差し替えが可能なようだ。
この JSContext は JSObject getJSObject() というメソッドをひとつだけ持つインターフェースなので、以下のような手順で JSObject のスタブ化が可能である。
-
netscape.javascript.JSObjectのサブクラスを作成する。これが今回の目的となるスタブである。 -
sun.plugin.javascript.JSContextとjava.applet.AppletContextの両方をimplementしたクラスを作成し、そのgetJSObject()メソッドで 1. で作成したクラスのインスタンスを返させる。 -
java.applet.AppletStubのサブクラスを作成し、そのgetContext()メソッドで上記 2. で作成したクラスのインスタンスを返させる (必要なアプレットパラメータがあればそれも返すように実装すると良い)。 - テスト対象のアプレットのインスタンスを生成した後、
init()の呼び出し前にsetStub()で上記 3. で作成したアプレットスタブを設定する。
具体的には以下のようなコーディングになる。
なお、アプレットパラメータの MAYSCRIPT 値は参照しているだけで評価には使用されていない様子。実際、何も設定されていなくても JSObject#getWindow() は成功する。
public class MyAppletTest extends TestCase{
public void testSomeFunctionThatCallsJSObject(){
Applet applet = new MyApplet();
applet.setStub(new MyAppletStub());
applet.init();
// ... アプレットの機能テスト
applet.destroy();
return;
}
}
class MyAppletStub implements AppletStub{
private final AppletContext context = new MyAppletContext();
// ... AppletStub のインターフェースを実装
public AppletContext getAppletContext(){
return context;
}
}
class MyAppletContext implements AppletContext, JSContext{
private final JSObject jsobject = new MyJSObject();
// ... AppletContext のインターフェースを実装
public JSObject getJSObject(){
return jsobject;
}
}
class MyJSObject extends JSObject{
// ... JSObject のメソッドを実装
}
参考:
