クライアントサイドのjcoverageに関しては何とか動いたようです。いよいよサーバサイドのカバレッジ計測をやってみようと思います。確認は以下のような環境で行いました。
プロダクト | バージョン | 役割 |
WebSphere | WebSphere 5.0.2テスト環境*1 | WEBコンテナ |
Cactus | 1.7.1 | サーバサイドテスティングフレームワーク |
Junit | 3.8.1 | テスティングフレームワーク |
jcoverage | 1.0.5 | カバレッジツール |
Cactusとはサーバサイドのテスティングフレームワークです。JUnitのクラスを拡張して、WEBコンテナ上でテストを行うためのクラス群を提供します。
具体的には、requestパラメータにいろいろな値をセットしてブラウザのリクエストをシミュレーションすることや、ブラウザへ返却される画面に正しく値がセットされてるかをチェックするクラスなどを提供してくれます。
さて、前回にJUnitとの連携まではやりましたが、今回がいままでと違うところはWEBコンテナ上で稼働する箇所とクライアントで稼働する箇所があるというところです。つまりリクエスト情報を構築してリクエストを放るまではクライアント側のJavaVM,リクエストを受けて処理をしてクライアントに返すまではWEBコンテナ上のJavaVM、という方式になります。クライアント側のカバレッジ計測についてはある意味いままでと変わらないのですが、WEBコンテナで稼働する部分のカバレッジ計測をどうするかというところがポイントになります。いろいろ調べた結果、以下のようなやり方でやればよい(ような気がする)事がわかりました。
難しいのは、デプロイするearにアーカイブされるクラスにどのようにデバッグを埋め込むか、ですね。通常開発環境でのコーディング・テストフェーズでは、自分でアーカイブを作ることはせず、開発環境が勝手にデプロイしてくれることが多いからです*2。これに関してはclassesにデバッグ埋め込み済みのコードを配置するようビルドする、ことで解決できそうです。
さて例でやってみます。
WebSphereのテスト環境でjcoverageを使うときにサーバ側でエラーが発生してしまうようで、
WebSphereテスト環境 >> 環境 >> WebSphere特定クラスパス
でjcoverage.jarを追加する必要があるみたいです。Tomcatなどでやるときもcommon/libとかに入れておく必要があるのかな?
手順は、
となります。ポイントはデフォルトではclassesがクラスパスに設定されてしまうので、instrumentしたファイルがclassesに配置されるようビルドするって事です。あと、Eclipseは自動的にビルドする機能がありますが、そうするとせっかくInstrumentしたファイルを上書きしてしまうので、自動ビルドをOFFにしておきました。
プロジェクト名はJCoverageTestWeb?としました。CVSはこちら
今回はAntではなくEclipse上のビルドを用いました。Antでやる場合はこんな感じ。
<!-- 環境依存ファイルの読み込み--> <!-- 実際にファイルを参照してみてください --> <property file="build.win.properties" /> <property file="build.properties" /> <!-- クラスパス --> <path id="project.class.path"> <pathelement location="${j2ee.jar}" /> <pathelement path="${document.root}/${class.dir}" /> <fileset dir="${document.root}/${jar.dir}"> <include name="**/*.jar" /> </fileset> </path> <!-- クラスパス名の定義 以上 --> <taskdef classpathref="project.class.path" resource="tasks.properties" /> <target name="init"> <tstamp> <format property="TODAY" pattern="yyyy/MM/dd" /> </tstamp> <filter token="project.name" value="${ant.project.name}" /> <echo>${ant.version}</echo> <echo>Java ver. ${ant.java.version}</echo> <echo>${ant.project.name}</echo> <echo>BaseDir: ${basedir}</echo> <echo>日付:${TODAY}</echo> </target> <!-- コンパイル --> <target name="javac.src" depends="init" description="ソースをビルドします"> <mkdir dir="${document.root}/${class.dir}" /> <copy todir="${document.root}/${class.dir}" preservelastmodified="yes"> <fileset dir="${src.dir}"> <include name="**" /> <exclude name="**/**.java" /> </fileset> </copy> <javac srcdir="${src.dir}" destdir="${document.root}/${class.dir}" encoding="${compile.encoding}" includeantruntime="no" memoryInitialSize="256M" fork="yes" memoryMaximumSize="256M" includes="**" debug="true"> <classpath refid="project.class.path" /> </javac> </target> <!-- テストクラスのコンパイル --> <target name="test.javac" depends="javac.src" description="テスト用ソースをビルドします"> <mkdir dir="${document.root}/${class.dir}" /> <copy todir="${document.root}/${class.dir}" preservelastmodified="yes"> <fileset dir="${test.src.dir}"> <include name="**" /> <exclude name="**/**.java" /> </fileset> </copy> <javac srcdir="${test.src.dir}" destdir="${document.root}/${class.dir}" encoding="${compile.encoding}" memoryInitialSize="256M" fork="yes" memoryMaximumSize="256M" debug="true"> <classpath refid="project.class.path" /> </javac> </target>
Instrumentはクライアント環境だけの場合もほとんど同じ。
<target name="instrument" description="Add jcoverage instrumentation"> <instrument todir="${document.root}/${class.instrumented.dir}"> <ignore regex="org.apache.log4j.*" /> <fileset dir="${document.root}/${class.dir}"> <include name="**/*.class" /> </fileset> <classpath refid="project.class.path" /> </instrument> <copy todir="${document.root}/${class.instrumented.dir}" preservelastmodified="yes"> <fileset dir="${document.root}/${class.dir}"> <include name="**" /> <exclude name="**/*.class" /> </fileset> </copy> <delete dir="${document.root}/${class.dir}" /> <mkdir dir="${document.root}/${class.dir}" /> <copy todir="${document.root}/${class.dir}" preservelastmodified="yes"> <fileset dir="${document.root}/${class.instrumented.dir}"> <include name="**" /> </fileset> </copy> <delete> <fileset dir="${ser.output.dir}"> <include name="jcoverage.ser" /> </fileset> </delete> <copy file="jcoverage.ser" todir="${ser.output.dir}" /> </target>
ちとダサいんですが、Eclipseがコンパイルしたクラスがあるディレクトリ(classes)でInstrumentして(Insturumented-classesにコピーされる)、classesディレクトリを削除、んでInsturumented-classesディレクトリをclassesディレクトリにコピー、なんてことをやってます。これでWASのテスト環境がパスを通すclassesディレクトリにデバッグ埋め込み済みのコードが配置されたことになります。
あと、一点だけ異なるのがコンテナ上でもカバレッジを計測するので、ここで作成されるjcoverage.serをコンテナ上のVMが出力するディレクトリにもコピーをしなくてはいけないということです。そのディレクトリってのはWEBコンテナの起動ディレクトリっぽいですね。build.xmlの記述でいうと、コンテナ上のVMが出力するディレクトリというのが上の記述の
${ser.output.dir}
です。実体はbuild.propertiesに記述してあって、うちの環境では、
ser.output.dir=D:/IBM/Rational/SDP/6.0
となっています。ちなみにbuild.propertiesはこんな感じになっています。 build.properties
そもそもクライアントVMもコンテナのVMも同じディレクトリに出力してくれればよいのですが、なんかうまくいきませんでした。
ここでWEBコンテナを起動します。ここでビルドがかかっちゃうと、またEclipseがclassesディレクトリを上書きしちゃうので、ビルドがかからないようにそおっと(??)コンテナを起動します。
いつもEclipseでCactusを起動するのと変わりません。これで、クライアントで処理されるbeginXXXのメソッドの網羅率はプロジェクト内のディレクトリに出力されるし、コンテナで処理されるtestXXXのメソッドの網羅率は上に指定したディレクトリに出力されます*3。
WEBコンテナを落とした時点で、
が存在することになります。最後にそれぞれのファイルをマージして一つのファイルにして、レポーティングすれば完成です。Antで以下のようにしました。
<target name="coverage" > <report srcdir="${src.dir}" destdir="${build.coverage.dir}"> <classpath refid="project.class.path" /> </report> <report srcdir="${src.dir}" destdir="${build.coverage.dir}" format="xml"> <classpath refid="project.class.path" /> </report> </target> <target name="merge" > <delete file="jcoverage_server.ser" /> <copy file="${ser.output.dir}/jcoverage.ser" tofile="jcoverage_server.ser" /> <merge> <fileset file="jcoverage*.ser"> </fileset> </merge> </target>
これを実行*4すると、プロジェクトルートのserファイルとWEBコンテナ上のserファイルがマージされ、レポートhtmlが作成されます。。
この記事は
現在のアクセス:17381