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 のメソッドを実装 }参考: