Webのユニットテスト⑩ Java

前回の続きです。
いろいろなブラウザでのユニットテストを実行する、Hudsonの分散プロジェクトを作ってみます。
の続編です。

build.xmlなどを置く

以前に作ったbuild.xml等を「【SampleJob】WEB+DBユニットテストJava)①デプロイ」のプロジェクト直下に置きます。


 build.properties
 build.xml
 codeAnalysis.properties
 codeAnalysis.xml
 coverage.properties
 coverage.xml


以下のファイルを変更しています。


■build.properties

###################################################
# ディレクトリ定義
###################################################
# ソースファイル格納ディレクトリ
src.dir=src
# テストソースファイル格納ディレクトリ
test.dir=testsrc
# スタブソース格納ディレクトリ
stub.dir=stubsrc
# クラスファイル格納ディレクトリ
class.dir=bin
# ライブラリ格納ディレクトリ
lib.dir=WebContent/WEB-INF/lib
# テスト用ライブラリ格納ディレクトリ
test.lib.dir=testlib
# WARファイル格納ディレクトリ
war.dir=war
# 設定ファイル格納ディレクトリ
conf.dir=conf
# 画像ファイル格納ディレクトリ
img.dir=img
# スクリーンショット格納ディレクトリ
screenshot.dir=screenshot
# テスト結果ファイル格納ディレクトリ
report.dir=report

###################################################
# コンパイル定義
###################################################
# ソースファイルのエンコーディング
src.encoding=UTF8

###################################################
# WARファイル作成定義
###################################################
# WARファイル名
war.name=WebAndDbUnitSample.war
# WARファイルに含めるファイル
create.war.src.dir.includes=**/*.xml, **/*.properties
# WARファイルに含めないファイル
create.war.class.dir.excludes=**/*Test*, **/Xls*, **/Stub*, **/*Assert*

###################################################
# ユニットテスト実行定義
###################################################
# ユニットテスト対象外のソースファイル
unitTest.test.dir.exclude=**/*Tester.java, **/*TestUtils.java, **/*Assert*.java
# SeleniumServerのJARファイル
seleniumServer.jar=selenium-server.jar
# SeleniumServer停止URL
stop.seleniumServer.url=http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer


build.xml
SeleniumServerの起動/停止とWebTester.propertiesのコピー処理を追加しています。
WebTester.propertiesのコピー処理で使用するBrowserTypeは、Hudsonで設定した構成軸の値が入ってきます。

<?xml version="1.0" encoding="UTF-8"?>
<project name="UnitTestProject" default="build" basedir=".">

  <!-- ========================================================== -->
  <!-- 定義値 -->
  <!-- ========================================================== -->

  <!-- プロパティファイル名 -->
  <property file="build.properties" />
    
  <!-- ソース参照 -->
  <path id="build.src">
    <pathelement path="${src.dir}" />
    <pathelement path="${test.dir}" />
    <!-- 【プロジェクト参照をする場合はココにソース参照を追加】 -->
  </path>
  
  <!-- ライブラリ参照 -->
  <path id="build.classpath">
    <fileset dir="${lib.dir}" includes="**/*" />
    <fileset dir="${test.lib.dir}" includes="**/*" />
    <!-- 【プロジェクト参照をする場合はココにライブラリ参照を追加】 -->
  </path>

  <!-- ========================================================== -->
  <!-- Antタスク実行順序 -->
  <!-- ========================================================== -->
  <target name="build">
    <echo message="OS:${os.name} Java:${ant.java.version} Ant:${ant.version}"/>
    <antcall target="clean" />
    <antcall target="compile" />
    <antcall target="create.war" />
    <ant antfile="coverage.xml" target="build" inheritAll="true" inheritRefs="true" />
  </target>
  <target name="build-unitTest">
    <antcall target="clean-unitTest" />
    <antcall target="unitTest" />
  </target>
  <target name="build-unitTest-coverage">
    <antcall target="clean-unitTest" />
    <antcall target="update.WebTester.properties" />
    <antcall target="unitTest" />
    <ant antfile="coverage.xml" target="coverageReport" inheritAll="true" inheritRefs="true" />
  </target>

  <!-- ========================================================== -->
  <!-- ディレクトリをクリア -->
  <!-- ========================================================== -->
  <target name="clean">
    <delete dir="${class.dir}" />
    <delete dir="${war.dir}" />
    <mkdir dir="${class.dir}" />
    <mkdir dir="${war.dir}" />
  </target>

  <!-- ========================================================== -->
  <!-- ディレクトリをクリア(ユニットテスト用) -->
  <!-- ========================================================== -->
  <target name="clean-unitTest">
    <delete dir="${report.dir}" />
    <delete dir="${screenshot.dir}" />
    <mkdir dir="${report.dir}" />
    <mkdir dir="${screenshot.dir}" />
  </target>

  <!-- ========================================================== -->
  <!-- コンパイル -->
  <!-- ========================================================== -->
  <target name="compile">
    <javac destdir="${class.dir}" encoding="${src.encoding}" debug="on" includeantruntime="no" failonerror="false">
      <src refid="build.src" />
      <classpath refid="build.classpath" />
    </javac>
    <copy todir="${class.dir}">
      <fileset dir="${src.dir}" includes="${create.war.src.dir.includes}"/>
      <fileset dir="${test.dir}" includes="${create.war.src.dir.includes}"/>
    </copy>
  </target>

  <!-- ========================================================== -->
  <!-- WARファイル作成 -->
  <!-- ========================================================== -->
  <target name="create.war">
    <war destfile="${war.dir}/${war.name}" webxml="WebContent/WEB-INF/web.xml">
      <fileset dir="WebContent"/>
      <webinf dir="WebContent/WEB-INF"/>
      <lib dir="${lib.dir}"/>
      <classes dir="${class.dir}" excludes="${create.war.class.dir.excludes}" />
      <classes dir="${src.dir}" includes="${create.war.src.dir.includes}"/>
    </war>
  </target>

  <!-- ========================================================== -->
  <!-- プロパティファイルの更新 -->
  <!-- ========================================================== -->
  <target name="update.WebTester.properties" if="BrowserType">
    <echo message="BrowserType=${BrowserType}" />
    <copy file="${test.dir}/${BrowserType}-WebTester.properties" tofile="${class.dir}/WebTester.properties" overwrite="true" />
  </target>
  
  <!-- ========================================================== -->
  <!-- ユニットテスト実行 -->
  <!-- ========================================================== -->
  <target name="unitTest">
  
    <!-- SeleniumServer起動 -->
    <java jar="${seleniumServer.jar}" fork="true" spawn="true" />
    
    <!-- ユニットテスト実行 -->
    <junit fork="yes" maxmemory="512m" printsummary="on">
      <classpath location="${class.dir}" />
      <classpath refid="build.classpath" />
      <formatter type="xml" />
      <batchtest fork="yes" todir="${report.dir}">
        <fileset dir="${test.dir}" excludes="${unitTest.test.dir.exclude}" />
      </batchtest>
    </junit>
    
    <!-- SeleniumServer停止 -->
    <get src="${stop.seleniumServer.url}" dest="result.txt" ignoreerrors="true" />
    
  </target>

</project>


■coverageSave.jsp
Cobertura FAQによると、
アプリケーションサーバ上でカバレッジを取得する場合は、明示的にカバレッジデータの保存を実行する必要があります。
アプリケーションサーバのプロセスはずっと動きっぱなしなので・・・カバレッジデータを保存するタイミングがないらしいです)
なので、カバレッジデータの保存を実行するJSPを作成して、Antからキックすることにします。

<%
try {
    String className = "net.sourceforge.cobertura.coveragedata.ProjectData";
    String methodName = "saveGlobalProjectData";
    Class saveClass = Class.forName(className);
    java.lang.reflect.Method saveMethod = saveClass.getDeclaredMethod(methodName, new Class[0]);
    saveMethod.invoke(null,new Object[0]);
} 
catch (Throwable t) {
}
%>

■coverage.properties

###################################################
# カバレッジ作成定義
###################################################
# Coberturaインストールディレクトリ
cobertura.home=C:/Cobertura1.9.4.1
# カバレッジデータファイル
coverage.data=C:/Tomcat6.0/cobertura.ser
# クラスファイル格納ディレクトリ(カバレッジ測定用)
instrument.class.dir=instrument
# カバレッジ測定結果出力ディレクトリ
cobertura.report=coverageReport
# カバレッジ測定対象外のクラス
instrument.class.dir.excludes=**/*Test*, **/*Assert*
# カバレッジ保存URL
coverage.save.url=http://localhost:8080/TEST-WebAndDbUnitSample/coverageSave.jsp


■coverage.xml
テスト用のWARファイルにcoverageSave.jspを含めるように変更しました。
coverageSave.jspをキックして、アプリケーションサーバ上のカバレッジデータを保存する処理を追加しています。

<?xml version="1.0" encoding="UTF-8"?>
<project name="UnitTestCoverage" default="coverage" basedir=".">

  <!-- ========================================================== -->
  <!-- 定義値 -->
  <!-- ========================================================== -->

  <!-- プロパティファイル名 -->
  <property file="build.properties" />
  <property file="coverage.properties" />

  <!-- カバレッジ測定用ライブラリ参照 -->
  <path id="cobertura.classpath">
    <fileset dir="${cobertura.home}">
      <include name="cobertura.jar" />
      <include name="lib/**/*.jar" />
    </fileset>
  </path>

  <!-- ========================================================== -->
  <!-- Antタスク定義 -->
  <!-- ========================================================== -->
  <taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
  
  <!-- ========================================================== -->
  <!-- Antタスク実行順序 -->
  <!-- ========================================================== -->
  <target name="build">
    <antcall target="clean" />
    <antcall target="instrument" />
    <antcall target="create.TEST-war" />
  </target>
 
  <!-- ========================================================== -->
  <!-- ディレクトリをクリア -->
  <!-- ========================================================== -->
  <target name="clean">
    <delete dir="${instrument.class.dir}" />
    <delete dir="${cobertura.report}" />
    <delete file="${coverage.data}" />
    <mkdir dir="${instrument.class.dir}" />
    <mkdir dir="${cobertura.report}" />
  </target>

  <!-- ========================================================== -->
  <!-- カバレッジ測定用コードの埋め込み -->
  <!-- ========================================================== -->
  <target name="instrument">
    <cobertura-instrument datafile="${coverage.data}" todir="${instrument.class.dir}">
      <fileset dir="${class.dir}" excludes="${instrument.class.dir.excludes}" />
    </cobertura-instrument>
  </target>
  
  <!-- ========================================================== -->
  <!-- WARファイル作成 -->
  <!-- ========================================================== -->
  <target name="create.TEST-war">
    <war destfile="${war.dir}/TEST-${war.name}" webxml="WebContent/WEB-INF/web.xml">
      <fileset dir="WebContent"/>
      <webinf dir="WebContent/WEB-INF"/>
      <lib dir="${lib.dir}"/>
      <lib dir="${cobertura.home}" includes="**/*.jar" />
      <classes dir="${instrument.class.dir}"/>
      <classes dir="${src.dir}" includes="${create.war.src.dir.includes}"/>
      <fileset file="coverageSave.jsp"/>
    </war>
  </target>

  <!-- ========================================================== -->
  <!-- カバレッジ測定結果を出力 -->
  <!-- ========================================================== -->
  <target name="coverageReport">
  
    <!-- アプリケーションサーバ上のカバレッジデータ保存 -->
    <get src="${coverage.save.url}" dest="result.txt" ignoreerrors="true" />
    
    <!-- カバレッジ測定結果を出力 -->
    <cobertura-report format="xml" datafile="${coverage.data}" destdir="${cobertura.report}">
      <fileset dir="${src.dir}" includes="**/*.java" />
    </cobertura-report>
    
  </target>
  
</project>

最終的に、「【SampleJob】WEB+DBユニットテストJava)①デプロイ」のディレクトリ構成はこんな感じになりました。

WebAndDbUnitSample
├─build
├─src
│ ├─dbcp.properties
│  └─sample
│   └─SampleForm.java
├─testdata
│  └─SampleFormTest
│   └─init.xls
├─testlib
│ ├─dbunit-2.4.7.jar
│ ├─junit-4.8.2.jar
│ ├─poi-3.2-FINAL-20081019.jar
│ ├─poi-contrib-3.2-FINAL-20081019.jar
│ ├─poi-scratchpad-3.2-FINAL-20081019.jar
│ ├─selenium-java-client-driver.jar
│ ├─slf4j-api-1.6.0.jar
│ ├─slf4j-ext-1.6.0.jar
│ ├─slf4j-migrator-1.6.0.jar
│ ├─slf4j-simple-1.6.0.jar
│ └─TEST-DbUnitSample.jar
├─testsrc
│ ├─WebTester.properties
│ ├─iexplore-WebTester.properties
│ ├─firefox-WebTester.properties
│  └─sample
│   ├─HtmlTableAssert.java
│   ├─SampleFormTest.java
│    └─WebTester.java
└─WebContent
   ├─META-INF
   ├─WEB-INF
    │ ├─lib
    │ │  ├─click-2.2.0.jar
    │ │  ├─click-extras-2.2.0.jar
    │ │  ├─commons-dbcp-1.4.jar
    │ │  ├─commons-dbutils-1.3.jar
    │ │  ├─commons-lang-2.5.jar
    │ │  ├─commons-pool-1.5.4.jar
    │ │  ├─DbUnitSample.jar
    │ │  └─h2-1.2.135.jar
    │ ├─click.xml
    │ └─web.xml
    └─SampleForm.htm


「【SampleJob】WEB+DBユニットテストJava)①デプロイ」のプロジェクト直下には、以下のファイルを置いています。
 build.properties
 build.xml
 coverageSave.jsp
 codeAnalysis.properties
 codeAnalysis.xml
 coverage.properties
 coverage.xml
 selenium-server.jar
 selenium-server-run.bat