連載
» 2008年02月06日 00時00分 公開

CoolなEclipseプラグイン(24):これはすごい!?コード品質のカイゼン化プラグイン2種 (2/3)

[岡本隆史,@IT]

テストケースを自動生成するには?

 準備ができたら、早速テストケースを生成してみましょう。Eclipse上のJavaプロジェクトでビルドエラーを解決した状態にします。テストケースを生成したいコードを選択し、右クリックで[Agitar]→[Generate Tests]を選択します。

図7 テストコード生成 図7 テストコード生成

 すると、選択したソースコードがJUnit Factoryのサーバに送信されテストケースが生成されます。テストケースの生成が完了すると、[JUnit Factory]ビューが表示され、生成されたテストケース名とカバレッジが表示されます(図8)。

図8 テストコード生成結果 図8 テストコード生成結果

 [JUnit Factory]ビューの結果に表示されているテストケース名をクリックすると、生成されたテストケースが表示されます。

 図8の例では、筆者が開発する形態素解析器の「Sen」を例にテストケースを生成しましたが、非常に高いカバレッジでテストケースが生成されているのが分かります。JUnit Factoryはソースコードを解析し、メソッド中の条件分岐を基にテストコードを生成します。

 また、「アグレッシブモック」と呼ばれるモックを利用したモックオブジェクトの生成やprivateフィールドへの値の設定を利用し、JUnitのテストケースが困難なケースに対してもテストケースを生成します。

実際のソースコードから見るテストケースの自動生成例

 例えば、Senの例で見ると、クラスViterbiに次のようなメソッドがあります。

public class Viterbi {
……(略)……
    private Node lookup(SentenceIterator iterator, char[] surface
        , Reading constraint) throws IOException {
        Node resultNode = this.tokenizer.lookup(iterator, surface);
        if (constraint == null) {
            return resultNode;
        }
        Node filteredResultNode = null;
        Node lastNode = null;
        for (Node node=resultNode; node!=null; node=node.rnext){
            if ((node.length == constraint.length) &&             (node.morpheme.getReadings().contains(constraint.text))){
                if (filteredResultNode == null) {
                    filteredResultNode = node;
                } else {
                    lastNode.rnext = node;
                }
                lastNode = node;
            }
        }
        if (lastNode != null) {
            lastNode.rnext = null;
            return filteredResultNode;
        }
            // Synthesize Node
        Node unknownNode = this.tokenizer.getUnknownNode(
            surface, iterator.origin(), constraint.length,
            constraint.length + iterator.skippedCharCount());
        unknownNode.morpheme.setReadings(Arrays.asList(
            constraint.text));
        return unknownNode;
    }
……(略)……
} 

 上記のようなメソッドに対して、次のテストコードを生成します。

public void testLookupWithAggressiveMocks1() throws Throwable {
    Viterbi viterbi = (Viterbi) Mockingbird.getProxyObject(
        Viterbi.class, true);
    Tokenizer tokenizer = (Tokenizer) Mockingbird.getProxyObject(
        Tokenizer.class);
    char[] chars = new char[0];
    Reading reading = (Reading) Mockingbird.getProxyObject(
         Reading.class);
    Node node = (Node) Mockingbird.getProxyObject(Node.class);
    Node node2 = (Node) Mockingbird.getProxyObject(Node.class);
    Morpheme morpheme = (Morpheme) Mockingbird.getProxyObject(
         Morpheme.class);
    List list = (List) Mockingbird.getProxyObject(List.class);
    setPrivateField(viterbi, "tokenizer", tokenizer);
    setPrivateField(reading, "length", new Integer(1));
    setPrivateField(reading, "text", "");
    Mockingbird.enterRecordingMode();
    Mockingbird.setReturnValue(tokenizer.lookup(null, chars), node);
    node.length = 0;
    node.rnext = node2;
    node2.length = 1;
    node2.morpheme = morpheme;
    node2.rnext = null;
    Mockingbird.setReturnValue(false, morpheme, "getReadings",
        "()java.util.List", list, 1);
    Mockingbird.setReturnValue(false, list, "contains",
        "(java.lang.Object)boolean", Boolean.TRUE, 1);
    Mockingbird.enterTestMode(Viterbi.class);
    Node result = (Node) callPrivateMethod(
        "net.java.sen.dictionary.Viterbi", "lookup",
        new Class[] {SentenceIterator.class,char[].class
            ,Reading.class}
        ,viterbi, new Object[] {null, chars, reading});
    assertNotNull("result", result);
    assertNotNull("viterbi.tokenizer", getPrivateField(viterbi,
        "tokenizer"));
} 

 生成されたテストケースを見ると、無理やりな印象を受けないでもないですが、モックオブジェクトやプライベートフィールドへのアクセスなどを利用して、オブジェクトの生成と値の設定、確認を行う高度なテストケースが出力されているのが分かります。

 このように、モックオブジェクトとプライベートフィールドへのアクセッサを利用することにより、高カバレッジのテストケースを生成します。

警告やヒントも表示されるので便利!

 テストケースが十分に生成できなかったり、生成されたテストケースの可読性が低い場合は、JUnit Factoryビュー内に警告が表示されます(図9)。

図9 警告が表示されたテストケース(赤線内) 図9 警告が表示されたテストケース(赤線内)

 [JUnit Factory]ビュー内の「Warning」をクリックすると、テストケース生成のためのヒントが表示されます(図10)。

図10 テストケース生成のためのヒント 図10 テストケース生成のためのヒント

 モックを利用したテストケースはテスト内容が非常に分かりづらくなることがあります。そのような場合は、ヘルパクラスを利用してインスタンスを取得するようにすると、分かりやすいコードにできます。

 ヘルパクラスの利用については、Eclipseのヘルプファイルに利用方法が書いてあるので、そちらをご覧ください。

JUnit Factoryでカバレッジの確認

 各コードをチェックすると、カバレッジ計測時に実行されたコード(緑の部分)と未実行のコード(ピンクの部分)を確認できます(図11)。

図11 カバレッジ 図11 カバレッジ

 左のカバレッジ表示領域にマウスのカーソルを合わせると、詳細な情報がポップアップで表示されます。

図12 カバレッジの詳細情報 図12 カバレッジの詳細情報

 JUnit Factoryはすべてのテストケースを生成してくれるわけではありません。カバレッジが満たされない部分は開発者がテストコードを追加する必要があります。

 続いて次ページでは、ダッシュボードでテスト状況を把握する方法やJUnit Factoryを実際に使う際の注意点、保守性が高いコードかどうか計測するCrap4jについても解説します。あなたのソースコードが保守しやすいかどうか、試してみましょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。