日々常々

ふつうのプログラマがあたりまえにしたいこと。

JUnit4.12に追いつこう(周回遅れで)

JUnit4.12がでました!……2014/12/4に。なんと6ヶ月経ってる。まぁいいや。

ググっても4.12の情報があまり引っかからなかったので書いてみますね。

リリースノート斜め読み

とりま、Summary of changes in version 4.12 を斜め読み致して、気になるところ(★)は後でもう少し詳しく書く事にします。なお、バグフィックス、メッセージ変更、挙動の統合などの特にテストコーディングに影響を与えないものはスルー。

  • Assersions
    • floatのassertNotEquals → float 使わない
    • booleanのassertArrayEquals → boolean あまり使わない
  • Command-line options
  • Test Runners
    • ParentRunnerの拡張ポイント追加 → 少し楽になったのかも
    • AnnotationBuilderのインナークラス対応 → Runner作るなら
    • AnnotationValidator → 新機能ー ★
  • Exception Testing
    • ExpectedExceptionのメッセージが変えられる → ちょっと嬉しいかも
    • ErrorCollector#checkSucceedsのジェネリック化 → checkSucceeds使ったことない
  • Timeout for Tests ★
    • TestFailedOnTimeoutException追加 → 具体的な型になるのはいいこと
    • Timeoutのスタックトレースが出せる → よさげ
    • Timeoutの指定方法が増えた → よさげ
  • Parameterized Tests ★
    • Iterable<Object[]>じゃなくてよくなった → よさげ
    • UseParametersRunnerFactory → 新機能ー
  • Rules
    • Stopwatch追加 → よく書く ★
    • @Ruleがstaticにも書ける → へ? ★
    • DisableOnDebug追加 → 使えなくはないんだろうけども
  • Theories
    • 失敗時のメッセージ改善 → よく使ってる人は影響ある?
    • DataPointsからとるのに名前指定ができるように → よさげ ★
    • enumやbooleanのDataPointを自動生成 → 使えそう
    • DataPointsにIterableが使えるように → よさげ ★
  • Categories
    • @Category@Inheritedに → 継承しないけどする時は嬉しいかも
    • @Categories.IncludeCategoryとかにanyMatchが指定できるように → Categoriesランナー使わない
  • Maven → スルー

結構なボリュームありますね。

気になるところを個別確認

リリースノート読んだり、プルリク読んだり、コード読んだり書いてみたりしつつ、思ったことを適当に書いていきます。書いてみたのはこの辺に置いてますけど、特に参考にはならなさそうです。

AnnotationValidator

一応新機能なのでとりあげ。 アノテーションにつけるバリデーターフレームワークです。アノテーションを作らないなら関係ありません。アノテーションを作るということは、それを対象にしたRunnerやRuleを作るということなので、この機能の恩恵を受ける人は限定的でしょうか。

JUnit内では「@Category@Beforeとかが付与されているメソッドにつけてはならない」などの制約をつけるために使用されています。あと、別経路のPublicClassValidatorと言うのが作られていて、テストクラスがpublicであることをこいつで検証するようになってます。AnnotationValidatorの拡張ではなく、ちょっと強引な作りになっていますが、今後はJUnit内部もこの系統のバリデーションに変更されていくのでしょう。internalでもexperimentalでもなく、JUnit的にはトップレベルのorg.junit.validatorパッケージにいきなり来ているあたりからも意気込みを感じますね(適当)。

JUnitが提供するアノテーションに片っ端からこの手のバリデーションが入るようになると、JUnitを拡張した系統のテスティングフレームワークで困ったことがでてくるかもしれないなーとぼんやり思いました。

関連で、JUnit4.11で非publicクラスを実行した場合に落ちるタイミングはインスタンス化しようとした際の「publicコンストラクタがねーよ」だったんですけど、その前に「publicクラスじゃねーよ」と言って落ちるようになってます。条件/タイミングが変わっているのでちょっと注意かも。

AnnotationValidatorのインスタンス複数のRunnerで共有されるので、イミュータブルかつスレッドセーフにせよとのこと。 また、インスタンスはAnnotationValidatorFactoryがnewInstanceで作るのでpublicのデフォルトコンストラクタが必要です。 これらのことから、AnnotationValidatorはオーバーライドできる3メソッド(validateAnnotatedClass, validateAnnotatedMethod, validateAnnotatedField)の引数に対してしか操作できません。で、どのアノテーションのバリデーターにするかで呼ばれるメソッドがかわります……なんかダサ……いやなんでもない。

Parameterized

  • パラメータ定義が少し楽になった
  • Runnerが拡張しやすくなった

一つ目が地味ながら嬉しそうな、@ParametersIterable<Object[]>を作らずによくなったこと。Iterable<? extends Object>になったので、適当にイテレートできる物体を押し付ければパラメタライズドに使えます。今までIterable<Object[]>だったのはリフレクションで実行する時の引数がObject[]だからと言う実装都合の話なので、この辺を吸収する程度ではありますがいい変更だと思います。その分JUnit内部がごちゃっとしてるのは……まぁ仕方ないことなのかなと。

もう一つは@UseParametersRunnerFactoryの追加で、これはParameterizedランナーの中の個々で実行されるテストのRunnerを変更するための拡張点です。JUnit4.11まではParameterizedクラスの中にTestClassRunnerForParametersと言うインナークラスがあり、これがテストを実行するランナー本体でした。これがBlockJUnit4ClassRunnerWithParametersに切り出されて公開クラスとなったので、Runnerの拡張が可能になったわけです。 しかし、従来の拡張したRunnerがそのまま使用できるわけではありません。アノテーションのパラメータ型名はParametersRunnerFactoryであり、Parameterを想定したRunnerが必要なことを主張しています。RunnerFactoryではないのです。うまく繋ぎこむラッパーを書けないことはないとは思うのですが、変更点を切り離しきったらRuleになる気もしてしまうのが微妙な感じ。

Theories

こちらもパラメータ定義まわりの微修正ですね。

@DataPointsが配列だけだったのが、Iterableも良くなりました。これで無駄にListを作ってtoArrayしていた過去とおさらばできます。

@FromDataPointsは大きな改善に思えます。JUnit4.11まではテストメソッドに同じ型の引数が複数あった場合、型の一致するすべての@DataPointsから取得されたものがねじ込まれてしまうため、テスト用の別の型を定義して回避するなど涙ぐましい努力が必要でしたが、@FromDataPointsを引数に付与すれば名前で紐付けられます。何年か前にこれが欲しかった……。

Stopwatch

時間を計るためのルール。よく作るのが標準で用意されるので少し手軽になった。おそらくfinishedメソッドだけをオーバーライドする実装ばかりするんだろうなーと。1クラスでやるならLambdaで書きたいところだけど、継承した1クラスを作って全体で使ったりするとかになるだろうから、いいか。

しかしJUnitは「protectedメソッドいっぱい用意してるから、必要なやつオーバーライドしてね!」というノリだなーと今更ながら思いました。

staticな @Rule

変な書き方ができるようになった。

    @Rule
    @ClassRule
    public static TestRule rule = ...;

パッと見た感じ、テストクラスと各テストメソッドに対して同じルールがかかるのが期待できて、その通り動く。気にしなくてはいけないのは、通常のRuleはテストメソッドごとにインスタンス化されるのでステートを持っていても問題ないけれど、ご覧の通りstaticフィールドなので同じインスタンスを共有することになるのは注意が必要。つまりStopwatchとかは(今の実装だと)使えません。

しかしこれできて何が嬉しいんだろう……。@ClassRuleに絡む時点でExternalResource系の重めのRuleなんだろうけど、正直ユースケースがわからないです。

Timeout周りの強化

コンストラクタやファクトリメソッドが充実して、スタックトレースとかその辺も強化された。ドキュメントを読めばわかる感じなので特に触ってません。予測できるAPIというのは良いですね。

タイムアウトの指定はシンプルにいくなら@Test(timeout = 99)みたく書けるんだけれど、全テストメソッドに指定するのも面倒なのでTimeoutルールを使うわけです。で、ミリ秒だけだったんだけどTimeUnitを使える今風(と言うほど新しくもないけど)なコンストラクタと、何かできそうな感じのTimeout#builder()が増えました。でもTimeout.Builderのできる事は少ないので、なんだろこれという気分にはなりました。まぁbooleanを受け取らせるファクトリメソッドオーバーロードするよりはマシ……かな。

あとRuleに追加されたDisableOnDebugTimeoutでの使用がメインぽい。DisableOnDebugがジェネリック化されてないのはなぜだろうとは思ったけど、用途を考えると要らない気もするので気にしない事にします。

どうでもいいけど、なんかTimeout.javaがすごく大きくなっててちょっとびっくりした。

まとめ

常用するのにインパクトのある変更なかったー!! Parameterized/Theory使いは影響あるけど、「ちょっと楽になるけど知らなくても問題ない」程度。魔改造クラスタに足突っ込んでないのであれば、JUnit実践入門の内容でまだまだ第一線余裕でしょう。

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

もう発売されてから2年以上経ってるんだなぁ。

あわせてよみたい?(疑問系)

irof.hateblo.jp