テストケースを自動生成するには?
準備ができたら、早速テストケースを生成してみましょう。Eclipse上のJavaプロジェクトでビルドエラーを解決した状態にします。テストケースを生成したいコードを選択し、右クリックで[Agitar]→[Generate Tests]を選択します。
すると、選択したソースコードがJUnit Factoryのサーバに送信されテストケースが生成されます。テストケースの生成が完了すると、[JUnit Factory]ビューが表示され、生成されたテストケース名とカバレッジが表示されます(図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)。
[JUnit Factory]ビュー内の「Warning」をクリックすると、テストケース生成のためのヒントが表示されます(図10)。
モックを利用したテストケースはテスト内容が非常に分かりづらくなることがあります。そのような場合は、ヘルパクラスを利用してインスタンスを取得するようにすると、分かりやすいコードにできます。
ヘルパクラスの利用については、Eclipseのヘルプファイルに利用方法が書いてあるので、そちらをご覧ください。
JUnit Factoryでカバレッジの確認
各コードをチェックすると、カバレッジ計測時に実行されたコード(緑の部分)と未実行のコード(ピンクの部分)を確認できます(図11)。
左のカバレッジ表示領域にマウスのカーソルを合わせると、詳細な情報がポップアップで表示されます。
JUnit Factoryはすべてのテストケースを生成してくれるわけではありません。カバレッジが満たされない部分は開発者がテストコードを追加する必要があります。
続いて次ページでは、ダッシュボードでテスト状況を把握する方法やJUnit Factoryを実際に使う際の注意点、保守性が高いコードかどうか計測するCrap4jについても解説します。あなたのソースコードが保守しやすいかどうか、試してみましょう。
Copyright © ITmedia, Inc. All Rights Reserved.