IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  Java technology  >

Coberturaでテスト対象範囲を調べる

バグが潜む未テストのコードを見つける

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません

原文はこちら

原文はこちら


レベル: 中級

Elliotte Rusty Harold (elharo@metalab.unc.edu), Adjunct Professor, Polytechnic University

2005年 5月 03日

Coberturaは、テスト対象範囲を調べるオープンソースのツールであり、コードベースを実装することによって、またテスト・スイートが実行する際に、どのコード行が実行され、どのコード行が実行されないかを監視して、対象範囲を調べます。また、Coberturaは未テストのコードを特定し、バグを見つけることに加えて、到達不能な、死んでいるコードにフラグを立てることによって、コードを最適化します。その結果、APIが現実的にどのように動作するかを洞察することもできます。この記事ではElliotte Rusty Haroldが、コード・カバレッジ(テスト対象範囲)に関するベスト・プラクティスを使ってCoberturaを活用する方法を解説します。

テスト駆動開発は、プログラミングに関する過去10年間の改革の中で、最も重要なものですが、「まずテスト」というプログラミング(test-first programming)やユニット・テストは、格別新しいものではありません。質の良いプログラマーであれば、こうした技法を半世紀も前から使っています。しかし、堅牢なバグ無しのソフトウェアを、スケジュール通り、予算通りに開発する上での致命的要素として認識されるようになったのは、ここ数年のことです。しかしテスト駆動開発は、テスト以上に良くはなりません。確かにテストによってコード品質は改善されますが、それは実際にテストされたコードベース部分について言えるだけです。プログラムのどの部分が未テストであるかを伝え、もっとバグが見つかるように、未テスト部分に対するテストが書けるようにするためのツールが必要なのです。

Mark DolinerによるCoberturaは(coberturaはスペイン語で、coverage(対象範囲)を意味します)、この問題に対応するための、フリーのGPLツールです。Coberturaはログのための追加ステートメントを持ったバイトコードを実装してテストを監視し、テスト・スイートが実行する際に、どの行が実際にテストされ、どの行がテストされていないかを調べるのです。そして、具体的にどのパッケージ、クラス、メソッド、個々のコード行がテストされていないかを示すHTMLまたはXMLのレポートを作成します。ですから皆さんは残ったバグを見つけるために、こうした特定の領域に対してテストを書けばよいのです。

Coberturaの出力を読む

まず、生成されたCoberturaの出力から始めることにしましょう。図1は、Jaxenテスト・スイート(参考文献)でCoberturaを実行して得られたレポートです。カバレッジが、ほとんど全て(org.jaxen.dom.htmlパッケージは、ほぼ100%)から、ほとんど無し(org.jaxen.expr.iterは全くカバーされていない)まで、大きな幅があることが分かります。


図1. Jaxenに対する、パッケージ・レベルでのカバレッジの統計
Jaxenに対する、パッケージ・レベルでのカバレッジの統計

Coberturaは、テストされた行数と、テストされた分岐数の両方によってカバレッジを計算します。最初のパスでは、この2つの違いは、あまり重要ではありません。Coberturaはまた、クラスに対する平均的なMcCabe's cyclomatic complexity(繰り返し処理の複雑性、参考文献を参照)も計算します。

HTMLレポートをさらにたどると、特定なパッケージ、またはクラスのカバレッジを見ることができます。図2は、org.jaxen.functionパッケージに対するカバレッジ統計を示しています。このパッケージでのカバレッジは、SumFunctionクラスに対する100%から、IdFunctionクラスに対する5%までの幅があります。


図2. org.jaxen.functionパッケージでのコード・カバレッジ
org.jaxen.functionパッケージでのコード・カバレッジ

さらに、各クラスにまでたどると、具体的にどの行がテストされていないかを見ることができます。図3は、NameFunctionクラスでのカバレッジの一部を示しています。左側のカラムは行番号を示します。次のカラムは、テスト実行中に、その行が何度実行されたかを示します。見て分かる通り、112行は100回実行され、114行は28回実行されています。赤でハイライトされた行は、全くテストされていません。このレポートによって、メソッド全体としてはテストされているものの、多くの分岐がテストされていないことが分かります。


図3.NameFunctionクラスでのコード・カバレッジ
NameFunctionクラスでのコード・カバレッジ

Coberturaはjcoverageから分かれたものです(参考文献)。jcoverageのGPL版は1年以上更新されておらず、その長年来のバグの幾つかを、Coberturaが修復しています。オリジナルのjcoverage開発者達は、オープンソースで続ける代わりに、商用版のjcoverageと、同じコードベースを元にしたクローズドソース製品であるjcoverage+とに注力しています。オリジナルの開発者が対価を要求することに決めたからといって、その成果まで死んでしまう必要はないというのは、正にオープンソースの驚異でしょう。

未テスト部分を識別する

Coberturaのレポートを使うと、コード中の未テスト部分を特定できるため、その部分に対するテストを書くことができます。例えば図3は、Jaxenがテキスト・ノードやコメント・ノード、命令処理ノード、属性ノード、名前空間ノードに対して、name()ファンクションを適用するテストを必要としていることを示しています。

カバーされていないコードが、Coberturaが特定するように大量にある場合には、足りないテスト全てを追加するのは非常に時間がかかります。しかしこれは、するだけの価値があるのです。全てを一度に行う必要はありません。まず、全くカバーされていないパッケージのような、一番テストされていないコードから始めるのです。全てのパッケージを少しテストしたら、カバーされていない各クラスに対するテストを書きます。全てのクラスを部分的にテストしたら、カバーされていないメソッドを対象にしたテストを書きます。全てのメソッドをテストしたら、未テストのステートメントをアクティブにするために、何が必要かを探し始めればよいのです。

未テストのコードを(ほとんど)無くす

テストはできるが、テストすべきではない、というものがあるでしょうか。誰に質問するかによって、答えが異なるでしょう。J. B. RainsbergerはJUnit FAQの中で、次のように書いています。「一般的な考え方は次の通りです。それ自体では壊れないのであれば、(簡単過ぎるので)壊れることはありません。第一の例はgetX()メソッドです。getX()メソッドが、インスタンス変数の値のみを答える、と考えてください。その場合getX()は、コンパイラー、あるいはインタープリターが一緒に壊れない限り、壊れることはありません。ですから、getX()をテストすべきではありません。何の利点も無いのです。setX()についても同じことが言えますが、そのsetX()メソッドが、パラメーターに関して何らかの妥当性検証を行う場合や、何らかの副作用がある場合には、恐らくテストする必要があるでしょう。」

理論的には、カバーされていないコードに対するテストを書いても、バグが見つかる保証はありません。しかし私は現実問題として、そうしたテストでバグが見つからない、というケースを見たことがありません。未テストのコードにはバグが満ちています。テストが少なければ少ないほど、未発見のバグがコードの中に潜んでいる可能性が高いのです。

私は、彼の意見に同意できません。私が、「(簡単過ぎるので)壊れることはない」コードの中で見つけたバグの数は数え切れません。確かに、一部のセッターやゲッターは、あまりに些細なため、フェールしようがありません。しかし私は、どのメソッドが(簡単過ぎるので)壊れることはなく、どのメソッドがそう見えるだけなのか、判別できた試しがありません。セッターやゲッターのような単純なメソッドをカバーするテストを書くことは、決して難しくありません。そのために費やす時間は、予期せぬバグが見つかるメソッドの数だけで、十分に元が取れるはずです。

一般的に言って、いったん測定を始めれば、90%のテスト・カバレッジに達するのは、ごく簡単です。ところが95%以上にまでカバレッジを増加させるには、ちょっとした細工が必要です。例えば、全バージョンのライブラリーには現れないバグに対する回避策をテストするために、別バージョンのサポート・ライブラリーをロードする方法があります。あるいはコードを再構成して、通常は到達できないコード部分に、テストが到達できるようにします。保護されたメソッドを単にパブリックにし、テストされるように、クラスを拡張する方法もあります。こうしたトリックは過剰に思えるかも知れませんが、私はこうした方法によって、未発見のバグを半分の時間で見つけることができています。

完璧な、100%のコード・カバレッジは、必ずしも達成できるものではありません。いかにコードをいじっても、何行か、あるいは幾つかのメソッド、あるいはクラス全体が、とにかくテストで到達できないことが、時にはあるのです。皆さんが突き当たりそうな壁の例としては、次のようなものでしょう。

  • 特定なプラットフォームでのみ実行するコード。例えば、よく設計されたGUIアプリケーションで「Exit」メニュー・アイテムを追加するコードは、Windows PCでは実行しますが、Macでは実行しません。
  • 起こり得ない例外を捉えるcatch文。例えばByteArrayInputStreamから読み取る時に投げられるIOExceptionなど。
  • 実際には呼び出されることはないものの、インターフェース契約を満足するために実装する必要のある、非パブリック・クラス内のメソッド。
  • 例えばUTF-8エンコーディングの認識失敗など、仮想マシンのバグを処理するコード・ブロック。

ユニット・テスト・パッケージや、クラス自体も調べることを忘れないでください。私はこれまでに、テスト・メソッドやテスト・クラスが、テスト・スイートで実際には実行されないことに、一度ならず気がついたことがあります。通常はこれによって、名前付けでのバグ(例えば、あるメソッドに対してtestSomeReallyComplexConditionの代わりにtesSomeReallyComplexConditionと名前を付ける)や、プライマリーのsuite()メソッドに追加し忘れたクラスなどのバグで影響が出ます。またある場合には、予期せぬ条件が、テスト・メソッド内でコードがスキップされているためだったこともあります。いずれにせよ、私はテストを書いたのですが、実際には実行されていませんでした。JUnitは、(皆さんは実行していると思っている)全テストは実行してはいない、と言ってくれませんが、Coberturaは言ってくれるのです。いったん見つかりさえすれば、修正は通常、簡単なものです。

これらの問題や、類似の問題があることを考えると、一部の極端なプログラマーが未テストのコード全てを自動的に削除してしまうのは、非現実的であり、滑稽でもあると思います。絶対的に完璧なコード・カバレッジが常に実現できないからといって、カバレッジを改善しなくて良いことにはなりません。

とはいえ、到達できないステートメントやメソッドは多くの場合、何ら目的を持たない、痕跡のようなコードと言うことができ、コードベースから削除しても何ら影響がありません。また、場合によっては、リフレクションを使ってプライベート・メンバーにアクセスする、非常に臭い手段を使って未テストのコードをテストすることも可能です。あるいは、テスト・クラスを、テストを行っているクラスと同じパッケージの中に置くことによって、未テストの、パッケージ保護されたコードに対するテストを書くこともできます。しかし、そんなことをすべきではありません。公開された(パブリックであり、保護された)インターフェースで到達できないコードは、そんなことをせずに削除すべきです。到達できないコードを、コードベースの一部にすべきではありません。コードベースは小さければ小さいほど、それを理解し、維持することは容易なのです。

Coberturaを実行する

さて、コード・カバレッジを測定するメリットが分かったので、Coberturaを使って実際にコード・カバレッジを測定するための具体的な詳細を説明しましょう。CoberturaはAntから実行するように作られています。まだIDEプラグインは何もありませんが、1年か2年のうちに、何かしら開発されるでしょう。

まず、build.xmlファイルにタスク定義を追加する必要があります。この、最上位レベルのtaskdef要素は、カレントの作業ディレクトリーにcobertura.jarファイルがあることを規定します。


<taskdef classpath="cobertura.jar" resource="tasks.properties" />

次に、コンパイル済みのクラス・ファイルにログ用のコードを追加するcobertura-instrumentタスクが必要です。todir属性は、実装されたクラスをどこに置くべきかを指示します。fileset子要素は、どの.classファイルを実装すべきかを規定します。

<target name="instrument">
  <cobertura-instrument todir="target/instrumented-classes">
    <fileset dir="target/classes">
      <include name="**/*.class"/>
    </fileset>
  </cobertura-instrument>
</target>

通常、テスト・スイートを実行するのと同じタイプのAntタスクとして、テストを実行します。違うのは、実装されたクラスが、オリジナル・クラスよりも前にクラスパスに現れる必要があること、Cobertura JARファイルをクラスパスに追加する必要があることだけです。

<target name="cover-test" depends="instrument">
  <mkdir dir="${testreportdir}" />
  <junit dir="./" failureproperty="test.failure" printSummary="yes" 
         fork="true" haltonerror="true">
    <!-- Normally you can create this task by copying your existing JUnit
         target, changing its name, and adding these next two lines.
         You may need to change the locations to point to wherever 
         you've put the cobertura.jar file and the instrumented classes. -->
    <classpath location="cobertura.jar"/>
    <classpath location="target/instrumented-classes"/>
    <classpath>
      <fileset dir="${libdir}">
        <include name="*.jar" />
      </fileset>
      <pathelement path="${testclassesdir}" />
      <pathelement path="${classesdir}" />
    </classpath>
    <batchtest todir="${testreportdir}">
      <fileset dir="src/java/test">
        <include name="**/*Test.java" />
        <include name="org/jaxen/javabean/*Test.java" />
      </fileset>
    </batchtest>
  </junit>
</target>>

Jaxenプロジェクトは、JUnitをテスト・フレームワークとして使用しますが、Coberturaはフレームワークを気にせず、TestNGでもArtima SuiteRunnerでもHTTPUnitでも、あるいは皆さん自作のシステムでも、同じようにうまく動作します。

最後に、cobertura-reportタスクが、この記事の最初の方で示したようなHTMLファイルを生成します。

<target name="coverage-report" depends="cover-test">
 <cobertura-report srcdir="src/java/main" destdir="cobertura"/>
</target>

srcdir属性は、オリジナルの.javaソースコード・ファイルがどこにあるかを規定します。destdir属性は、Coberturaが出力HTMLを置くべきディレクトリー名を規定します。

同じようなタスクを皆さん独自のAntビルド・ファイルに追加すれば、下記のようにタイプするだけで、カバレッジ・レポートが生成されます。

% ant instrument
% ant cover-test
% ant coverage-report

もちろん、皆さんの好みに合わせて、ターゲットの名前を変更したり、これら3つのタスクを1つのターゲットにまとめたりすることもできます。




上に戻る


まとめ

Coberturaは、アジャイルなプログラマーのツールボックスに追加すべき、重要なツールです。Coberturaを使うと、コード・カバレッジについて具体的な数字が得られるため、ユニット・テストが職人芸から科学にと変わります。Coberturaはテスト・カバレッジでの隙間を見つけ、それが直接、バグの発見につながります。コード・カバレッジを測定することによって、バグを発見し、修正するための情報が得られるため、より堅牢なソフトウェアを作ることができるのです。



参考文献

  • SourceForgeから、Coberturaをダウンロードしてください。

  • Javaプラットフォームでのユニット・テスト用フレームワークのデファクトである、JUnitの、test infectedを入手してください。

  • Dave ThomasとAndy Huntの共著による、Pragmatic Unit Testing in Java With JUnit(2003年、Pragmatic Bookshelf刊)を読んでください。

  • CenquaによるCloverは、もう少し洗練された、有料のテスト・カバレッジ・ツールであり、基本的にCoberturaと同じことを行いますが、荒削りなところは少なくなっています。

  • IBM Rationalは、コード検査を補助するための、包括的なテスト・ツール・スイートを提供しています。「Get started with automated testing: Road map to success」を読むと、コードのテストを始めるために必要な基礎が分かるでしょう。

  • Mike Kellyが、詳細な記事、「using TestManager to report test coverage」(developerWorks, 2003年12月)を書いています。

  • PureCoverageは、IBM Rational PurifyPlusテスト・ツール・スイートの一部ですが、素晴らしいコード・カバレッジ・ツールです。Rational Application Developorには、PureCoverageの機能が含まれています。詳しくは、記事「Component testing with IBM Rational Application Developer」(developerWorks, 2005年3月)を読んでください。

  • Coberturaはjcoverageから分かれたものです。

  • Carnegie Mellon大学のSoftware Engineering Instituteが、McCabe's Cyclomatic Complexityについての詳細を公開しています。

  • David CarewとSandeep Desaiの共著による「Keeping critters out of your code: How to use WebSphere and JUnit to prevent programming bugs」(developerWorks, 2003年6月)は、テストに対して極限プログラミング手法を応用する方法について説明しています。

  • Dennis M. Sosnoskiが「クラスワーキング・ツールキット: ヘンゼルとグレーテルでコードをカバーする」で、オープンソースのコード・カバレッジ・ツールである、ヘンゼル(Hansel)とグレーテル(Gretel)を利用したクラスワーキング・ツールキットのシリーズ(一部英語)を始めています。

  • Jesterでテストをテストする」を読んで、オープンソースのJUnitテスト・テスターについて学んでください。

  • Malcolm Davisによる「AntとJUnitを用いた漸進的開発」(developerWorks, 2000年11月)は、プロジェクトの中にJUnitを統合する方法を説明しています。

  • Erik Hatcherによる「Automating the build and test process」(developerWorks, 2001年8月)は、一つの自動プロセスで斬新的テストと連続ビルド組み合わせる方法を説明しています。

  • IBM alphaWorksのツール、FoCuSを試してください。FoCuSは機能的カバレッジ方法論を実装しており、テストが不足している領域に対して詳細なカバレッジ情報を提供してくれるため、アプリケーションのテストを改善することができます。

  • この記事でもモルモットとして登場したJaxen projectは、Javaプログラミング用のオープンソースのXPathエンジンであり、様々なオブジェクト・モデルに適用することができます。

  • developerWorksのJava technologyゾーンには他にも、Javaプログラミングに関する記事が豊富に用意されています。

  • Developer Bookstoreでは、Java関連の書籍をはじめ、広範な話題を網羅した技術書が豊富に取り揃えられていますので、ぜひご利用ください。


著者について

Photo of Elliot Rusty Harold

Elliotte Rusty Haroldはニューオーリンズ出身であり、時たま、おいしいgumbo(オクラ入りのスープ)を食べに帰っています。ただし現在はニューヨークのブルックリン近郊のProspect Heightsに、妻のBethと猫のCharm(charmed quarkからとりました)とMarjorie(義理の母の名前からとりました)と一緒に住んでいます。彼はPolytechnic Universityのコンピューター・サイエンスの非常勤教授として、Java技術とオブジェクト指向プログラミングを教えています。彼のCafe au Lait Webサイトは、インターネット上で最も人気のある独立系Javaサイトの一つです。また、そこから派生したCafe con Lecheは、最も人気のあるXMLサイトの一つです。彼の著作には Effective XML Processing XML with Java Java Network Programming 、それに The XML 1.1 Bible があります。現在はXML処理用のXOM APIやJaxen XPathエンジン、Jesterテスト・カバレッジ・ツールなどに取り組んでいます。




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ