ファイヤープロジェクト
Cactusによるサーブレットのテスト自動化
2004-07-06T00:10+09:00   matsu
Cactusによるサーブレットのテストの自動化について調査してみた.
サーブレットのテストで困るのは,テストを実行する層をサーブレットが動いている環境とサーブレットの間に作成するのが難しい点だろうか.Cactusのテスティングフレームワークに乗っかると,そのようなテスト層を作成する必要がなく,そのテスト層で動くものとしてのテストプログラムの作成に専念できる.Cactusのテスティングフレームワークでは,以下を行う.
  1. テストプログラムの作成
  2. サーブレットの実装
  3. テストクライアントの設定
  4. テストの実行
以下,順を追ってこれを行う.
基本的にCacatusではサーブレットのテストのテスト対象メソッドとして,serviceとかdoGetとかdoPostは含まれていない.サーブレットのテスティングフレームワークとしてこれはかなり辛いような気がする.だが,考えてみるとあまり人間(あるいはブラウザ)寄りの出力を検証するのは冗長で繁雑で,かつしばしば変更がかかることが予想されるので,応答ストリームをトラップして云々ということをやるのはあまり面白くない.つまりservice,doGet,doPostがどうというよりも,対人間の直接のデータ(すなわちrequest.getOutputStream....で現われる実行結果)ではなく,それへ至る途中のメソッドの実行結果を検証して,あとはまた別途考えましょうということなんだと思う.前置きが長くなったが,次節で作成するサンプルのサーブレットのテストプログラムを以下に示す.
Cactusによるサーブレットのテストプログラムではorg.apache.cactus.ServletTestCaseを拡張するのが基本である.
public class TestCactusSampleServlet extends ServletTestCase {
次にJUnitのテストプログラムと同様,テスト対象のxxxxというメソッドに対してtestXxxというメソッドを作成する(※).今回は2パターン作成することにしたので,変則的にtestXxxxPattern1などというメソッド名にした.
    public void testAddMethodPattern1() {
    public void testAddMethodPattern2() {
サンプルではメソッド名がtestで始まるメソッドにtestYyy対してbeginYyyがある.Cactusでは,Yyy部分の各パターンに対して以下の順にメソッドが呼ばれる.
  1. beginYyy
  2. testYyy
  3. endYyy
beginYyyではrequestやcookieやsessionに値を設定してtestYyyを呼び出す準備をする.testYyyではメソッドyyyを呼び出し,beginYyyでの設定を踏まえて各assertメソッドにより結果を検証する.endYyyではresponseの状態のチェックなど,さらに追加的な検証を行う.beginAddMethodPattern1内での
	webRequest.addParameter("param1", "1.0", WebRequest.POST_METHOD);
は,ポストメソッドのrequestにリクエストパラメータparam1=1.0を設定している.webRequestはインタフェースorg.apache.cactus.WebRequestをもつオブジェクトである.
※ 厳密にはtestで始まっていれば何でもよいが(引数はなし),どのメソッドのテストなのかが分かりやすいようにtestXxxという命名パターンがよく使用されるようだ.
上のテストプログラムのテスト対象のサーブレットを作成したみた.
今回はdoPostでリクエストを受けるということにした.この場合だとdoPostではクライアントへの出力を行い,その出力対象のデータをaddMethodで計算している.これは単純な例だが,このようにメソッドの作成のポイントとしてテストのしやすさというのは重要だと思う.
Cactusのテストプログラムを動かすためにいくつかの準備がある.まず,web.xmlは大体の場合,以下のファイルをそのまま使用すればよいと思う.
これは,クライアントからの要求を受けて,テスト対象にリダイレクトするサーブレットの設定である.先のサンプルのようにテストプログラム内でサーブレットインスタンスをnewする場合にはこのようにほぼ固定のweb.xmlを使用できる.ただし,テスト対象のサーブレットがweb.xmlで初期化パラメータを取得したりするような場合には,上のweb.xmlと本番用のweb.xmlをマージしたweb.xmlが必要である.また,テストプログラムからテスト対象のサーブレットのメソッドを呼び出すのにも工夫が必要であるが,別の機会に検証しようかと思う.次にクライアントプログラムがどこへ接続しに行ったらよいかの設定ファイルcactus.propertiesを(クライアントの)クラスパスに置く必要がある.
cactus.contextURL = http://apserver/fireproject
cactus.servletRedirectorName = ServletRedirector
cactus.jspRedirectorName = JspRedirector
cactus.enableLogging = false
cactus.contextURLはテスト用サーブレットのコンテキストパスのデプロイによって適切に書き換える.この設定から想像できるように,クライアントはAPサーバとは別のマシンからでも動作させることが可能である.あとはテストを実行するだけである.テスト実行には二つの方法がある.一つは
java junit.textui.TestRunner TestCactusSampleServlet
などとする.私はjakarta-cactus-13-1.6.1.zipに入っているjarファイルにクラスパスを通したら動いた.もう一つのテスト実行方法は,WEBブラウザでServletTestRunnerに直接アクセスする方法である.
http://apserver/fireproject/ServletTestRunner?suite=matsu.cactus.sample.TestCactusSampleServlet
suite=テストプログラムのFQDNとしてテストプログラムを指定する.出力はxmlなので,さらにxslを指定すると見やすい.
上の続き&xsl=cactus-report.xsl
また,私の環境(Debian GNU/Linux Woody)ではTomcat4のポリシー設定が必要であった.
grant codeBase "file:/tomcat/webapps/webtest/WEB-INF/lib/-" {
  permission java.util.PropertyPermission "cactus.*", "read,write";     
  permission java.util.PropertyPermission "user.*", "read,write";       
  permission java.io.FilePermission "/usr/share/tomcat4/junit.properties", "read";
  permission java.net.SocketPermission "localhost", "resolve";
  permission java.net.SocketPermission "127.0.0.1:80", "connect,resolve";
};
これらを/etc/tomcat4/policy.d/04webapps.policyに追記した.最後の二つは実行設定次第で必要な記述が変わるかもしれないが例外を見れば必要な設定が大体わかる.
今回作成したサンプルを置いておく.これ.以下ビルド手順.
  1. 例によってjarファイルなどが足りないので,置く.jakarta-cactus-13-1.6.1.zipを展開してlibのjarを全部置けば動くはず.具体的には以下9つ.
    aspectjrt-1.1.1.jar   commons-httpclient-2.0.jar  junit-3.8.1.jar
    cactus-1.6.1.jar      commons-logging-1.0.3.jar   nekohtml-0.7.4.jar
    cactus-ant-1.6.1.jar  httpunit-1.5.4.jar          servletapi-2.3.jar
    
  2. 自分の環境に合わせてcactus.propertiesのcactus.contextURLを編集
  3. ant buildwebtestでwebtest.warができる.これをデプロイする.
  4. ant webtestでテスト実行
  5. doc/testreport/html/index.htmlに結果が出力されるので,WEBブラウザなどで見る.
以下にサンプルのディレクトリ構成を示す.WEB-INFがテスト用と本番用の二つあるので注意する.先にあげたのはsrc/webtest/WEB-INF/web.xmlの方である.
.
|- - build
|   |- - main
|   |   `- - matsu
|   |       `- - cactus
|   |           `- - sample
|   |               `- - CactusSampleServlet.class
|   |- - test
|   `- - webtest
|       |- - cactus.properties
|       `- - matsu
|           `- - cactus
|               `- - sample
|                   `- - TestCactusSampleServlet.class
|- - build.eucjp
|- - build.properties
|- - build.properties.eucjp
|- - build.xml
|- - lib
`- - src
    |- - WEB-INF ←本番用
    |   `- - web.xml
    |- - html ←本番用
    |- - jsp ←本番用
    |- - main ←本体(テスト対象)
    |   `- - matsu
    |       `- - cactus
    |           `- - sample
    |               `- - CactusSampleServlet.java
    |- - test ←テスト用
    `- - webtest ←WEBテスト用
        |- - WEB-INF ←テスト用
        |   `- - web.xml
        |- - cactus.properties.eucjp
        |- - html ←テスト用
        |- - jsp ←テスト用
        |- - matsu
        |   `- - cactus
        |       `- - sample
        |           `- - TestCactusSampleServlet.java
        `- - xsl
すこし複雑だが,srcの下にプログラム本体ソースディレクトリmain,通常テストソースディレクトリtest,Cactusテストソースディレクトリwebtestがあり,jarやwarやtestやwebtestなどで,適当なものをかき集めるようにbuild.xmlを作成した.まぁ,build.xmlは一人でやってる内は自分の好きなようにつくればよい気がするので,大した話ではない.そんなことよりも,Cactusのクライアントを起動するAntタスクwebtestを今回は以下のように記述した.
<target name="webtest" depends="buildwebtest"
        description="Test by executing JUnit and Cactus.">
  <junit fork="yes" haltonfailure="no">
    <classpath>
      <fileset dir="lib">
        <include name="**/*.jar" />
      </fileset>
      <pathelement path="${build.webtest}" />
    </classpath>

    <formatter type="xml"/>

    <batchtest fork="yes" todir="${doc.testreport.xml.dir}">
      <fileset dir="${src.webtest}">
        <include name="**/*Test*.java" />
        <exclude name="**/AllTests.java" />
      </fileset>
    </batchtest>
  </junit>

  <junitreport todir="${doc.testreport.html.dir}">
    <fileset dir="${doc.testreport.xml.dir}">
      <include name="**/TEST-*.xml" />
    </fileset>
    <report format="frames" todir="${doc.testreport.html.dir}" />
  </junitreport>
</target>
設定すればCactus用のAntタスクも使用できるようだが,今回のように簡単な例ではJUnit用のAntタスクで十分対応できた.junitreportはJUnitによるテスト結果(この設定ではCactusのテストもAntからはJUnitによるテストと同じに見える)を見やすくレポートするものである.
matsu(C)
Since 2002
Mail to matsu