昨日, メッセージの表示頻度を簡単に調整できるライブラリdenbunをリリースしました.
初めてのライブラリリリースなので色々と学びがありました.
本稿ではVisibleForTesting
とRestrictTo
アノテーションについて書き留めます.
VisibleForTesting
フィールドやメソッドのスコープはできるだけ狭くすることが大切ですが, テスタビリティを確保するためにやむなくスコープを広くとる場合があります.
VisibleForTesting
は, スコープをテスタビリティのために広く定義していることを明示します.
例えば, Denbunライブラリでは情報の永続化先であるSharedPreferenceとのI/Oをフックできるようにしてテスタビリティを確保しています.
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public DenbunConfig daoProvider(@NonNull Dao.Provider provider) { ... }
このメソッドはプロダクションコードではPackage Private
スコープで扱われることを想定し, テストコードではPublic
スコープで扱われることを想定しています.
そのため本来あるべきスコープはPackage Private
なのですが, テスタビリティのためにPublic
としています.
メソッドが本来あるべきスコープはVisibleForTesting
アノテーションのotherwise
パラメータに指定します.
こうすることで, プロダクションコードにおいてPackage Private
スコープ外からアクセスしてきた場合にインスペクションによる警告が表示されるようになります.
ただし, このアノテーションはクラスファイルに影響を及ぼすものではないので, インスペクションの警告を無視して無理やり要素にアクセスすることは可能です.
VisibleForTesting
の真価は, このアノテーションで指定された要素をプロダクションコードで呼び出すとインスペクションの警告によって使い方が間違っていることを教えてくれるところにあります.
これは, javadocにコメントを残す対応よりもはるかに効果的で簡単です. また, 利用側に実装者の意図をインスペクションを通して伝えることができるので利用側にとっても嬉しい機能です. 実際のライブラリ開発では手軽に導入できてアクセス制御で悩むことも減るのでとても便利に使えます.
ただ, 実際にはアクセス制御できていないので, APIを公開することが致命的であるケースにおいてはイミュータブルインタフェースをかませるなどの対応が必要です. (そのようなケースはあまり思い浮かびませんが, セキュリティが必要なSDKなどでは該当しそうです)
RestrictTo
次にRestrictTo
アノテーションです. これはテストのために用意されたメソッドであることを明示するものです.
VisibleForTesting
はテスタビリティのための”スコープ”に着目しているので, そのメソッド自体は想定されるスコープ内であればプロダクションコードで呼ばれることが許されています.
例えば, VisibleForTesting(otherwise = private)
なメソッドであればプロダクションコードでもクラス内(privateスコープ内)からの呼び出しが想定されているということです.
一方で, RestrictTo
はメソッド自体の存在に着目しています.
RestrictTo(TEST)
であれば, テストコードからの呼び出しのみを想定しており, プロダクションコードでの呼び出しは想定されません. RestrictTo(LIBRARY)
であれば, ライブラリ内での用途に限った要素であることを明示しています.
これはライブラリを作る側としてはとても強力です. これもVisibleForTesting
と同じく, 呼び出し側が想定外の呼び出しを行なった場合にインスペクションの警告を表示します.
例えば, Denbunライブラリでは, DenbunBox
の初期化は一度しか行えず, 2回目以降はno-opになるよう実装されています.
しかし, UnitTestをする際にテストケースごとにDenbunBox
を再初期化したくなる場合も想定して, DenbunBox
の状態をリセットするreset()
メソッドを用意しています.
@RestrictTo(TESTS) public static void reset() { ... }
このメソッドは, ライブラリ内部および, プロダクションコードからの呼び出しも想定していません. テストに限定した利用を想定したものです.
ライブラリを作る際には, こういったアノテーションも活用して, 利用する側に作り手の意図を明示するのも大切だなと感じました.
以上です.