現在はテスト自動化シリーズと題しましてJUnit関連の紹介を連載中です。
さて、JUnitの開発では、「渡す引数が違うだけで同じメソッドを何度もテストする」というパラメータテストを実施することがあります。
例えば、「文字列の半角英数字チェック」のメソッドをテストしようとすれば、引数として渡すテスト対象文字列は以下のように色々なパターンが存在します。
- 半角英字
- 半角数字
- 記号
- 全角英字
- 全角数字
- ひらがな
- 空白スペース
- 以下続く……
これらのパターンに対してそれぞれテストを実行し、全て正常に期待した結果になるかどうかをチェックするJUnitソースを作成するというシチュエーションです。
では、まずはダメなパターンから行ってみましょう。
@Test public void 半角英数字チェックのテスト() { assertThat(TheoriesSample.isHankaku("a"),is(true)); assertThat(TheoriesSample.isHankaku("1"),is(true)); assertThat(TheoriesSample.isHankaku(","),is(false)); assertThat(TheoriesSample.isHankaku("A"),is(false)); assertThat(TheoriesSample.isHankaku("あ"),is(false)); assertThat(TheoriesSample.isHankaku("*"),is(false)); assertThat(TheoriesSample.isHankaku(""),is(true)); assertThat(TheoriesSample.isHankaku(null),is(true)); assertThat(TheoriesSample.isHankaku(" "),is(true)); }
こういう書き方はJUnitの作法としてNGです。
この場合、一つのテストメソッドで複数パターンのテストを実施していることになってしまいます。
「一つのメソッドで一つのテストパターン」
これがJUnitの作法です。
みんなが正しく作法を守ることで可読性の高い良いソースが生まれるのです。
面倒でもテストパターン毎に別々のメソッドにして下さい。
その作法を守ったパターンが以下になります。
@RunWith(Enclosed.class) public class TheoriesSampleTest { public static class 半角英数字チェックのテスト { @Test public void 半角英数字チェック_半角英字はtrue() { assertThat(TheoriesSample.isHankaku("a"),is(true)); } @Test public void 半角英数字チェック_半角数字はtrue() { assertThat(TheoriesSample.isHankaku("1"),is(true)); } @Test public void 半角英数字チェック_記号はfalse() { assertThat(TheoriesSample.isHankaku(","),is(true)); } //以下続く } }これなら「一つのメソッドで一つのテストパターン」のルールを守れます。
前回の記事で紹介した「Enclosedアサーション」を使えば「半角英数字チェックのテスト」でグループ化出来るので、メソッドが増えてもソースの見通しは比較的良い状態を保てます。
とはいえ、いくら正しい書き方と言っても、これは面倒でしょう。
「引数が1コ違うだけで他は全部同じなんだから、引数以外は共通化したい」と思うのがプログラマー精神です。
そこでご紹介するのが、今回記事のテーマ「Theoriesアサーション」です。
この機能は、上記のように「テストを実行するメソッド部分は同じだが、渡すパラメータだけは変えたい」という要望に対応するものです。
まずは以下にサンプルを記載します。
@RunWith(Enclosed.class) public class TheoriesSampleTest { @RunWith(Theories.class) public static class 半角英数字チェックのテスト_true系 { @DataPoint public static String 半角英字 = "a"; @DataPoint public static String 半角数字 = "1"; @DataPoint public static String 空白 = ""; //省略 @Theory public void 半角英数字チェックパラメータテスト(String str) { assertThat(TheoriesSample.isHankaku(str),is(true)); } } @RunWith(Theories.class) public static class 半角英数字チェックのテスト_false系 { @DataPoint public static String 記号 = ","; @DataPoint public static String 全角英字 = "A"; @DataPoint public static String ひらがな = "あ"; //省略 @Theory public void 半角英数字チェックパラメータテスト(String str) { assertThat(TheoriesSample.isHankaku(str),is(false)); } } }
ここで新登場のアサーションは三つです。
- @RunWith(Theories.class):このテストクラスがTheoryによる繰り返しであることを示す
- @DataPoint:繰り返し時に渡すパラメータ
- @Theory:テストメソッド本体
今まで引数として渡していたパラメータは「@DataPointアサーション」をつけたフィールド変数として定義します。
そして、そのクラスに「@RunWith(Theories.class)」を、テストメソッドに「@Theory」を付与することで、定義した全ての@DataPointがテストメソッドに繰り返し渡されるという構造です。
つまり、「@DataPoint」の定義だけをペタペタと増やしていけば、大量にあるテストパターンのパラメータも全て網羅出来るわけです。
この結果がエラーになった場合は、以下のように表示されます。
なるほど。
普通のテストケースだと「assertThat」の箇所が表示されますが、こちらの場合はエラーになったパラメータが表示されるわけです。
つまり、テストメソッドの中にassertThatが複数あった場合、その中のどのassertThatがエラーになったかは一目では分からないということになります。
このため、テストメソッド本体の中に記述するassertThatは一行一発で済ませるのが良いわけです。
このような場合には、過去に紹介した「カスタムMatcher」を作ることで解決して下さい。今まで紹介したJUnitの機能を組み合わせることで、スタイリッシュなJUnitソースを開発出来るのです。
次回予告
今回の記事ではTheoriesアサーションの簡単な例を紹介しました。しかし、
- 「@DataPointでテストメソッドに渡している引数がString型1コしか無いけど、同時に複数個渡したい場合はどうすればいいの?」
- 「期待する結果がtrue,falseの2パターンだけならこれでいいけど、引数毎に違う場合はどうするんだ?」
という疑問が沸いた方もいらっしゃるかと思います。
そう、上記の例では、テストメソッドに渡す引数は1コ限定、「assertThat」の期待値部分もtrue/falseの固定値と、柔軟性が無いのです。
プロジェクトの都合に合わせて柔軟に対応するにはもう一息、JUnitの機能と言うより、書き方のテクニックが必要になります。
次回はその書き方のテクニック「Fixture」についてご説明します。
0 件のコメント:
コメントを投稿