Shiraji's Blog

Testing Against Annotation Processing

以下のPRを投げて、マージされました。

Add integration test cases for checking the behavior of generated code https://github.com/hotchemi/PermissionsDispatcher/pull/339

このPRについて語ってみます。

想定読者

  • Annotation Processingに興味がある人
  • Annotation Processingを利用したライブラリを作っている人
  • PermissionsDispatcherに興味がある人
  • 細かい単語を気にしない人

compile-testingによるテスト

Annotation Processingのテストで検索するとだいたいcompile-testingを使えと書かれています。

compile-testingとは

A library for testing javac compilation with or without annotation processors

です。annotation processingのライブラリでよく使われる手法として、コードを読み込ませ、compile-testingを利用し想定通りのコードを出力しているかを確認しています。

こんな感じで書いています。

https://github.com/shiraji/kenkenpa/blob/master/kenkenpa-compiler/src/test/java/com/github/shiraji/kenkenpa/compiler/HopTests.java#L17-L75

  • ユーザが書くであろうコード(JavaFileObject source)からcompile-testingが生成するコードが想定している生成コード(JavaFileObject expectedSource)と等しいかどうかを確認します。
  • コンパイルエラーなどが起こさないか?の確認もします。

このテストを実行し、JUnitが全てパスした場合、思った通りのコードが生成され、コンパイルも問題なしです。私はこれだけで安心していました。

挙動のテスト

compile-testingが通りました!OKです!・・・でも本当にそうなんでしょうか?

テストしたのはcompilerが思った通りのコードを生成したことを確認しただけです。

本当に必要なテストは、生成したコードが実際に思った通り動くかどうかの挙動の確認であるはずです。

ライブラリ利用者からすれば、極論、生成されるコードなんてどんな形でも良いのです。挙動さえ正しければ良いはずです。(メソッド数や容量とか気になるけど。) が、その挙動の確認はcompile-testingでは行っていません。

ではどうするか?

実際に生成したコードに対してテストを書けば良いはずです。

冒頭のPermissionsDispatcherのPRでやったこととして

PermissionsDispatcherはDelegate to generated classにある通り、2つのpublicメソッドを生成します。そこで、この2つのメソッドに対し、PowerMockito、Robolectricなどフル活用してテストを書きました。

このテストのメリットとして、今後PermissionsDispatcherはKotlinはKotlinらしいコードを生成しようぜ!という話が上がっており、Kotlinらしいコードを生成したとしても、このテストが通りさえすれば、あくまで理論上ですが、どんなコードを生成しても問題ないと言えるはずです。

compile-testing捨てるべきなの?

捨てちゃダメです。冒頭のPRでは生成コードが必須になるため、正常系のテストしか行うことが出来ません。コンパイルエラーが発生した場合のエラーハンドリングが適切に行われているか?の確認としては非常に有効に活用できます。

compile-testingが何をテストしているのかをしっかり把握し、適材適所でのライブラリの選定が一番だと思います。