Cobertura、Ant、Jenkinsでコードカバレッジを取得する方法

JenkinsでAntを使用してビルドする際に、Coberturaを使用してコードカバレッジを取得しようというお話。
GradleやらMavenが主流みたいで、日本語ドキュメントがあまりみつからなかったので書いてみます。*1

前提

  • Jenkinsは既に動作している
  • Jenkins上でAntを使用してビルドが行われている
  • AntでJunitを実行している

coberturaの概要

  1. アプリケーションのコードをコンパイル
  2. コンパイルされたclassファイルにログ出力コードを埋め込む
  3. Junit実行
  4. レポートを出力

coberturaのモジュール取得

公式ページからモジュールを取得。
私は「cobertura-1.9.4.1-bin.zip (binary)」を使用しました。

必要モジュールの取得

取得したモジュールを解凍し以下のモジュールをプロジェクトに設定。

  • asm-3.0.jar
  • asm-tree-3.0.jar
  • cobertura.jar
  • jakarta-oro-2.0.8.jar
  • log4j-1.2.9.jar

今回は「/build/cobertura」内に設定しました。*2
尚、「cobertura.jar」はカレントから、他のモジュールはlibフォルダにあります。

/
├build
│└cobertura
│  ├asm-3.0.jar
│  ├asm-tree-3.0.jar
│  ├cobertura.jar
│  ├jakarta-oro-2.0.8.jar
│  └log4j-1.2.9.jar
└build.xml

build.xmlの編集

coberturaの依存jarを定義指定
<path id="cobertura.classpath">
	<fileset dir="build/cobertura">
		<include name="*.jar" />
	</fileset>
</path>
タスクの追加
<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
カバレッジデータファイルの削除

私が行った際には弊害はなかったのですが、ビルド毎に削除した方がよいそうです。初期処理などで実行しておきましょう。

<delete file="${project_home}/cobertura.ser" />
コンパイルされたclassファイルにログ出力用のコードを追加
<target name="cobertura_instrument" description="classファイルにログ出力用コード追加">
	<cobertura-instrument todir="build/cobertura/classes">
		<fileset dir="build/classe">
			<include name="**/*.class" />
		</fileset>
	</cobertura-instrument>
</target>

上記の設定では「build/classe」にあるコンパイルされたclassを指定し、
「build/cobertura/classes」にログ出力用のコードを埋め込んだclassファイルを出力します。

Junitタスクの編集
<junit fork="true">
	<sysproperty key="net.sourceforge.cobertura.datafile" file="${basedir}/cobertura.ser" />
	<formatter type="xml" />
	<classpath>
		<!-- coverage測定用 -->
		<path refid="cobertura.classpath" />
		<path path="${tmp_dir}/coverage-classes" />

		<!-- junit用 -->
		<path refid="test.classpath" />
	</classpath>
	<batchtest todir="${reports_junit_dir}">
		<fileset dir="${testsrc_dir}">
			<include name="**/**Test.java" />
		</fileset>
	</batchtest>
</junit>

JUnitのタスクなので色々ごちゃごちゃしていますが編集点はこんな感じです。

  • Junitタスクに「fork="true"」を付与
  • syspropertyを追加
  • classpathにcoberturaの実行用jarを追加
  • classpathに測定用に作成したCLASSを追加

JUnit実行時には測定用に作成したCLASSを使用して欲しいので、classpathの先頭にしなければいけません。

<sysproperty key="net.sourceforge.cobertura.datafile" file="${basedir}/cobertura.ser" />
<classpath>
	<path refid="cobertura.classpath" />
	<path path="build/cobertura/classes" />
</classpath>
coverageレポートを出力
<target name="coverage_report" description="coverageレポート出力">
	<cobertura-report srcdir="${src_dir}" destdir="build/reports/coverage" format="xml" encoding="shift_jis" />
</target>

カバレッジの測定結果のレポートを出力します。
その際にソースコードと紐付けるのでソースコードディレクトリを指定します。
jenkinsでカバレッジ測定結果を表示するためにformatはxmlと指定していますが、htmlと指定することで結果をその場で確認することが可能です。*3
また、encodingが異なっているとレポート観覧時にソースコードを見た際に正しく表示されません。

開発環境で実行

指定した箇所に「coverage.xml」というレポートが出力されればOKです。
内容の確認をしたい場合にはformatをhtmlにして出力して確認して下さい。

jenkinsの設定

プラグインの導入

特に問題はないと思いますが一応。。。
Jenkinsの管理 → プラグインの管理 → 利用可能 → フィルターに「Cobertura」と入力 → 「Cobertura Plugin」にチェック → 再起動せずにインストール

ジョブの設定
  • ビルド後の処理の追加 → Coberturaカバレッジ・レポートの集計
  • 「Cobertura XMLレポート パターン」にXML形式の出力されるレポートを設定。「build/reports/coverage/coverage.xml
  • 必要ならば他の設定を変更。
  • 設定を保存
ビルド実行とカバレッジ・レポート
  • ビルド実行
  • ビルド正常終了後にカバレッジ・レポート確認
  • カバレッジ・レポートが表示されればOK!(0%でなければ正しく出力されているかと思います。)

ハマった所とか

カバレッジ・レポートが0件かつ0%の場合

junit実行時のclasspathにログ挿入後のclassを含まなかった場合にこの現象が発生しました。*4

JUnitの実行が遅い場合

私の環境ではそれほど遅くなかったので対処しませんでしたが、実行がかなり遅くなる場合があるようです。
kennyjのブログ(仮): coberturaが猛烈に遅い!...けど解決した

まとめ

メリット
  • 簡単に導入が可能。(上記のハマリがなければ数時間で余裕でした。)
  • 数値として現れる事でユニットテスト作成の意欲向上!
  • ユニットテストの薄い部分などがわかるはず。*5
デメリット
感想

担当製品がレガシーコードで、ユニットテストを入れたのが最近だった事もあり、10%程度だと思ってカバレッジを測ってみたら「3%」という結果でした。。。
ただ、逆にユニットテストを作成すればするだけ数値が上がるので楽しんで作成していこうと前向きに考えて頑張る次第です。

参考URL

↑2005年の記事です。かなり古いので参考程度に。。。

*1:英語なら公式に記述があります。。。

*2:標準のディレクトリ構成ってなんですか?w

*3:詳しくは見ていないですが表示形式が異なるので注意。

*4:書き忘れることはないでしょうが、途中で出力フォルダを変更した場合などは注意。

*5:上記の通り全体的に薄いプロジェクトだったので、私は実感できてません。