- PR -

MockObjectを使ったサーブレット&JSPのテストケースについて

投稿者投稿内容
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-03 12:59
サーブレットからJSPを呼び出す部分を
--------------------------------------------------------------------------------
ServletContext sc = getServletContext();
sc.getRequestDispatcher("http://localhost:8080/TestCase/Error.jsp").forward(req,res);
--------------------------------------------------------------------------------

と定義してますがNullPointExceptionになってしまいます。
おそらくServletContextがテストケースで設定していないために、getServletContext()でエラーが出てると思い、MockServletContextを設定しようと次のようにしました。

--------------------------------------------------------------------------------
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mockobjects.dynamic.Mock;
import com.mockobjects.dynamic.OrderedMock;
import com.mockobjects.helpers.ServletTestHelper;
import com.mockobjects.servlet.MockServletContext;
import junit.framework.TestCase;


public class ContributionCheckTest extends TestCase {
Mock mockHttpServletRequest = new Mock(HttpServletRequest.class);
Mock mockHttpServletResponse = new OrderedMock(HttpServletResponse.class, "Response with non-default name");
ContributionCheck aServlet = new ContributionCheck();
final StringWriter output = new StringWriter();
final PrintWriter contentWriter = new PrintWriter(output);

final ServletTestHelper helper = new ServletTestHelper(); //ここでエラーが出る

final MockServletContext sc = helper.getServletContext();

public ContributionCheckTest(String arg0) {
super(arg0);
}

public static void main(String[] args) {
junit.swingui.TestRunner.run(ContributionCheckTest.class);
}
public void testSimpleAddition() throws ServletException, IOException {

mockHttpServletResponse.expect( "setContentType", "text/html");
mockHttpServletResponse.expectAndReturn( "getWriter", contentWriter );

aServlet.doGet((HttpServletRequest) mockHttpServletRequest.proxy(),
(HttpServletResponse) mockHttpServletResponse.proxy());

mockHttpServletRequest.verify();
mockHttpServletResponse.verify();
}
}
--------------------------------------------------------------------------------

new ServletTestHelper()にはHttpServletを引数にしないといけないようなのですが、
この部分がよくわかりません。
そもそも
final ServletTestHelper helper = new ServletTestHelper();
final MockServletContext sc = helper.getServletContext();
このようにServletContextを設定する考え方が間違っているのでしょうか?

MockServletContextクラスのgetContext(java.lang.String arg0) メソッドを使ったほうが良いのでしょうか?
この場合に引数でどんな値を送ればいいのかがわかりません。
どなたかご教授お願いいたします。
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-03 15:48
最初の質問のサーブレットからJSPを呼び出す部分を
--------------------------------------------------------------------------------
ServletContext sc = getServletContext();
sc.getRequestDispatcher("http://localhost:8080/TestCase/Error.jsp").forward(req,res);
--------------------------------------------------------------------------------
から
--------------------------------------------------------------------------------
RequestDispatcher rd = req.getRequestDispatcher("/Bulletin.jsp"); //reqはHttpServletRequest
rd.forward(req,res);
--------------------------------------------------------------------------------
に変更して
ServletTestHelper
MockServletContext
を使用しないで実行したところ
--------------------------------------------------------------------------------
junit.framework.AssertionFailedError: mockHttpServletRequest: Unexpected call: getRequestDispatcher(</Bulletin.jsp>)
Expected one of:
getParameter(< = a>) [called]
getParameter(< = b>) [called]

  at com.mockobjects.dynamic.Mock.invoke(Mock.java:91)
  at $Proxy0.getRequestDispatcher(Unknown Source)
  at ContributionCheck.doGet(ContributionCheck.java:64)
  at ContributionCheckTest.testSimpleAddition(ContributionCheckTest.java:58)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:324)
  at junit.framework.TestCase.runTest(TestCase.java:154)
  at junit.framework.TestCase.runBare(TestCase.java:127)
  at junit.framework.TestResult$1.protect(TestResult.java:106)
  at junit.framework.TestResult.runProtected(TestResult.java:124)
  at junit.framework.TestResult.run(TestResult.java:109)
  at junit.framework.TestCase.run(TestCase.java:118)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:392)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:276)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:167)

--------------------------------------------------------------------------------
とエラーが出ました。
このやり方の場合は相対パスしか指定できないらしく、やはりテストケースのMockObjectで
そこまでのパスを設定しなくてはならないのでしょうか?
HttpServletRequestに設定するのでしょうか?(MockObject)

その場合どのようにすればよいのかわかりません。
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-04 14:45
RequestDispatcher rd = req.getRequestDispatcher("/Bulletin.jsp"); //reqはHttpServletRequest
のこれはどのような値をrdに入れているのでしょうか?
指定されたパスに位置するリソースのラッパとして動作する RequestDispatcher オブジェクトを返しているとAPIリファイレンスには書いてありますが、
「指定されたパスに位置するリソースのラッパとして動作する」とはどのようなものかがわかりません。
これをMockObjectで作成できるのでしょうか?

誰かご教授お願いいたします。
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-08 12:12
サーブレットのテストの部分は自己解決ができました。
MockObjectでreq.getRequestDispatcherとされた時に
RequestDispatcherの擬似オブジェクトをリターンするという形を取りました。

サーブレットはこれで良いのですが、JSPはどうするんでしょうか・・・
JSPに関してはMockオブジェクトの使い方がいまいちわかりません。
MockObjectでコンテナを作りテストをする、という形でできるらしいのですが、
みなさんはどのような形でテストをしていますか?
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-03-08 14:21
JSP は実行環境でコンパイルされてサーブレットに変換されますので、実行環境を用意しないMockアプローチでのテストは難しそうですね。
そもそもモデルとコントローラさえしっかりユニットテストができていればプレゼンテーション層それほど問題は発生しにくいものです。
どうしても必要なら実際にお使いのコンテナのJSPコンパイラでコンパイルしたものをサーブレットと同じ方法でテストしてみてはいかがでしょうか。実行環境なしにそのまま動くかどうかはわかりませんが・・・。


[ メッセージ編集済み 編集者: インギ 編集日時 2004-03-08 14:24 ]
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-08 17:31
インギさん返信ありがとうございます。

>どうしても必要なら実際にお使いのコンテナのJSPコンパイラでコンパイルしたものをサーブレットと同じ方法でテストしてみてはいかがでしょうか。

この方法ですと、コンテナはtomcatを使っていて、Eclipsで作っていますので、
JSPをコンパイルした後に、JAVAファイルのインポートをわざわざしないといけなくなってしまうので、できれば違う方法でやりたいと考えています。

パラメータを渡して正常に動くかをテストしたいので、
Tomcatを動かしている状態でJUnitからJSPを呼び出してテストするという形を取ろうと考えたのですが、req.getRequestDispatcher()が上手くいかないとできません。
サーブレットではRequestDispatcherが実際の動きをしなくてもforwardが実行されたかを確認すればよかったので、MockRequestDispatcherを使えば良かったのですが、今回は実際にJSPが呼ばれないといけないので、この部分を何とかしようと思っていますが、
ServletContextにgetRequestDispatcher()がどの値を設定すれば動くかわからなくてできません。
koe
大ベテラン
会議室デビュー日: 2003/07/13
投稿数: 198
投稿日時: 2004-03-08 23:56
引用:

ダメ猫さんの書き込み (2004-03-08 17:31) より:
この方法ですと、コンテナはtomcatを使っていて、Eclipsで作っていますので、
JSPをコンパイルした後に、JAVAファイルのインポートをわざわざしないといけなくなってしまうので、できれば違う方法でやりたいと考えています。


Eclipseのどのプラグインを使っているのか分かりませんが、Sysdeo Tomcat Launcher pluginを
使った場合、JSPから変換されたサーブレットのソースがプロジェクト内のworkフォルダに
作成されます。そうでなくても、server.xmlのContextタグの属性workdirにプロジェクト内の
フォルダを指定することで同等のことを実現できます。

ところでJSPのテストケースを作成したいとのことですが、
テストしやすいようにJSPを書かないとかなり複雑になるので注意した方がいいです。
実際にやってみた経験から言えば、JSPを、テストケースが不要でも安心できる程度に
シンプルに作り、必要となる複雑な処理はJSPの呼び出し側(サーブレットなど)や
タグライブラリに持って行った方がいいと思います。その方がJSPの保守性も上がりますから。
ダメ猫
常連さん
会議室デビュー日: 2004/02/20
投稿数: 45
投稿日時: 2004-03-09 11:22
koeさん返信ありがとうございます。

JAVAファイルはプロジェクト内のworkフォルダに作成されますが、インポートしないとEclipsから参照ができません。
workフォルダの中身はパッケージがorg.apache.jspとなっていて、参照ができません。
なぜかその中にはファイルが無い扱いになっています。
それで、そこにファイルをインポートしようとしたら、ファイルが存在しますと言われてしまいインポートできません。
一旦別の場所にインポートしてからそこに移動させればできますが・・・
この作業を一回やると、次回からはJSPを更新するたびにJAVAファイルが更新されるようです。
ですが、新しいJSPを作成するたびにこれをやるのはどうかと思います・・・
プラグインはcom.sysdeo.eclipse.tomcat_2.2.1を使っています。
プラグインをSysdeo Tomcat Launcher pluginに変えればできるのでしょうか?

JSPでテストしたい部分は引数を受け取って、正常な値ならまともに動作して、異常ならエラーにするというテストだけなので、テスト自体は簡単なものです。

JUnitからreq.getRequestDispatcher("/hogehoge.jsp").forward(req,res)を正常に動作させる方法は無いのでしょうか?

スキルアップ/キャリアアップ(JOB@IT)