はじめに
継続的インテグレーションは開発プロセスに欠かせないものであり、最近ではコードカバレッジチェックをビルドオートメーションに組み込もうとする傾向が強まっていますが、開発チームは一体どれくらいのカバレッジ率(ソースコードに対し、どれだけ詳細にテストが行われたかを示す割合)を目標にすればよいのでしょうか。コードカバレッジ重視派の人々の中には、75%という人もあれば、85%という人もあり、100%という人もいます。
私たちがあるプロジェクトでカバレッジ率のベースラインを測定したところ、この開発チームの目標値はそれよりずっと低くせざるを得ないことがわかりました。私は、ジョーゼフ・ヘラーの小説『キャッチ=22』に出てくるキャスカート大佐のように、部隊の兵士の目標出撃回数が達成されそうになるたびに目標回数を引き上げていくようなことはしたくありませんでした。
ヨッサリアンはがっくりとうなだれた。「じゃあ、僕はどうしても50回出撃しなきゃいけないのか」
「55回だ」ダニーカ軍医は言った。
「55回?」
「55回出撃せよ、というのが大佐の今度のご希望だよ」
――ジョーゼフ・ヘラー著『キャッチ=22』より
そこで、あやふやな目標値を設定するのではなく、漸進的(ぜんしんてき・少しずつ段階を踏むこと)に向上させていく手法を採用することにしました。この手法を成功させるには、各ビルドは前回成功したビルドと同じかそれ以上のカバレッジに到達する必要があります。小さな前進をいくつも積み重ねることで、最終的に大きな品質向上を達成しようと考えました。
この記事では、CoberturaとApache Antを使って、コードカバレッジの漸進的向上を実践する方法について説明します。
ユニットテスト、コードカバレッジ、継続的インテグレーション
ユニットテスト、コードカバレッジ、継続的インテグレーションは、いずれも推奨技法として広く認められています。実際、ほとんどの開発者は、ユニットテストを行うことを信奉しています。念のため、まだ改心していない人のために、Googleの研究ディレクターであるPeter Norvigの言葉を引用しておきましょう。
コードのユニットテストを作成する必要がないと思うなら、その理由をすべて紙に書いてみるといい。書き終わったらその紙を入念に見直そう。それが終わったらその紙は捨てて、いずれにしてもユニットテストを作成すること。
けれども、そのテストコードは誰がテストするのでしょうか。つまり、作成したテストコードが十分であるかどうかはどうやって確かめるのでしょうか。これはとても大切な情報です。というのも、作成したテストで実行されないコードこそ、注目すべき部分だからです。1つの解決策は、コードカバレッジツールを使用してテストで実行されるコードの割合を調べ、それから通常の統合プロセスにカバレッジチェックを組み込むことです。カバレッジチェックが失敗すれば、ビルドも失敗するようにします。
漸進的向上の手法を実践するために、私はコードカバレッジツールとしてCoberturaを採用しました。このツールには、シンプルでよく出来ている、4つのAntタスク用インターフェイスが備わっているからです。これらのタスクの1つはcobertura-checkで、これを使用すると、要求するカバレッジ率にコードが到達しない場合にビルドを失敗させることができます。たとえば、次のAntターゲットでは、カバレッジ率が80%を下回るとビルドが失敗します。
<target name="coverage_check">
<cobertura-check totallinerate="80"/>
</target name="coverage_check">
ただし、行数の割合をこのようにハードコーディングする代わりに、現在のチェックの目標カバレッジ率として前回のビルドの結果を使用することにします。これを行うには、2つのコアAntタスクを使用していくつかのCoberturaタスクを変更します。
ここで行数の割合、分岐数の割合、その他のカバレッジ測定値を測定するかどうかは気にしません。目的は達成済みのレベルを達成することであり、目標を絶対値で設定したり、どのカバレッジ率測定基準を使うかの詳細を検討することではありません(Coberturaの使用方法については、ここを参照してください。cobertura-checkタスクに該当するタスクのないツールを使用する場合は、Antのfailタスクを検討してください。ただし、その場合はカスタムのconditionを作成することが必要になります)。
XML形式のカバレッジレポートを作成する
コードのインストルメント化とテストの実行を行うターゲットを作成したら、ビルドスクリプトに漸進的カバレッジ率チェックを追加します。まず、cobertura-reportタスクを使用して、XML形式のカバレッジレポートを作成します。
<cobertura-report format="xml"/>
以下は、cobertura-reportタスクで生成されるレポート(coverage.xml)の例です。
<?xml version="1.0"?>
<!DOCTYPE coverage SYSTEM
"http://cobertura.sourceforge.net/xml/coverage-02.dtd">
<coverage line-rate="0.43612334801762115"
branch-rate="0.48344370860927155" version="1.8"
timestamp="1181043899853">
<sources>
<source>./src/java</source>
</sources>
<packages>
...
</packages>
</coverage>
このファイルは後で必要になるので、安全な場所に保管しておきます(バージョン管理システムにチェックインしておくとよいでしょう)。
レポートからカバレッジ率を抽出する
初めは、AntのXmlPropertyタスクを使用して行数の割合をAntプロパティに直接抽出すればよいと思うかもしれません。しかし、その方法には次の理由から問題があります。
- Coberturaでは、coverage.xmlには行数の割合が小数値として書き込まれるのに対し、cobertura-checkタスクには整数値を指定する必要がある。
- 現実の大規模なプロジェクトでは、coverage.xmlのサイズも大きくなると考えられるので、XmlPropertyタスクを使用しようとすると、JavaのOutOfMemoryErrorが発生する可能性がある。
代わりに、AntのXSLTタスクを使用して必要な値のみを抽出します。
<xslt in="coverage.xml" out="build/coverage.properties"
style="src/xsl/coverage.xsl" />
次の単純なXSLテンプレートでは、必要な値のみが含まれるプロパティファイルが生成されます。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="coverage">
total.line-rate=
<xsl:value-of select="floor(@line-rate*100)"/>
</xsl:template>
</xsl:stylesheet>
floor(@line-rate*100)というメソッド呼び出しに注目してください。これは行数の割合を%値に変換し、端数を切り捨てて整数にします。その結果、coverage.propertiesファイルには次の1行のみが含まれることになります。
各ビルドの間に保存するcoverage.xmlとは異なり、coverage.propertiesは中間ファイルであり、ビルドの後にクリーンアップできます。
次に、Antのpropertyタスクを使用して、coverage.propertiesからtotal.line-rateというAntプロパティへ値を読み込みます。
<property file="build/coverage.properties" />
最後に、最初に書いたcobertura-checkタスクの"80"を、新しく設定したAntプロパティに置き換えます。
<cobertura-check totallinerate="${''total.line-rate''}"/>
手順全体をまとめる
最終的なbuild.xmlは次のようになります(見やすくするために、cobertura-instrumentタスク、テストターゲット等は省略しています)。
<target name="coverage_check" depends="check_against_previous_rate">
<antcall target="coverage_report"/>
</target>
<target name="coverage_report">
<cobertura-report format="xml" destdir="." />
</target>
<target name="check_against_previous_rate"
depends="coverage_xml_to_properties">
<property file="build/coverage.properties" />
<cobertura-check totallinerate="${coverage.line-rate}" />
</target>
<target name="coverage_xml_to_properties">
<xslt in="coverage.xml" out="build/coverage.properties"
style="src/xsl/coverage.xsl" />
</target>
新しいカバレッジレポートが生成されるのは、カバレッジチェックが成功したとき(前回成功したビルドと同等かそれ以上のカバレッジ率である場合)のみであることに注意してください。
これを開始するには、最初にcoverage_checkを実行するより前にcoverage_reportを実行することを忘れないでください。実際には、cobertura-reportタスクをもう1つ追加してHTMLレポートも生成する必要があります。
向上度を追跡する
この漸進的向上手法では、カバレッジ率をファイルに記録して向上度の追跡も追加するとよいでしょう。これを行うには、Antのechoタスクを使用します。
<target name="time">
<tstamp>
<format property="date.time" pattern="yyyy-MM-dd HH:mm"/>
</tstamp>
</target>
<target name="log" depends="time">
<echo file="${history.txt}" append="true">
${date.time};total.line-rate;${total.line-rate}
</echo>
</target>
測定可能な結果、目に見える向上
最初に述べたプロジェクトでは、この手法を採用して1週間後にカバレッジ率が30%向上しました(開始時のレベルが非常に低かったということもありますが)。特に喜ばしかったのは、それまではテストを作成したがらなかった開発者たちも、カバレッジ率の向上を誇るようになったことです。
アジャイル開発の真の民主主義精神に基づき、チームメンバー全員がそれぞれより多くのテストを作成することよって、プロジェクト全体の目標を引き上げることができます。目標値をころころ変えるキャスカート大佐はいなくてよいのです。
また、ここで終わりにする必要はありません。漸進的向上手法は他のコード測定基準にも応用できます。ほとんどのコードチェックツールでXML形式の出力が可能なので、XSLテンプレートを使って関連する測定値を抽出し、次のビルドのチェック用の入力値として使用できます。
スウェーデンのコンサルティング会社Omegapoint AB勤務。20年以上に及ぶシステム開発経験を持ち、ここ数年はスウェーデンにあるインターネット銀行のJavaアーキテクトも務める。