Java製WebアプリケーションフレームワークのSpringに業務で初挑戦することになりまして、Controllerの単体テスト実装で引っかかったのでメモを残しておきます。
SpringにはControllerの単体テスト方法として次の二種類が用意されています。
- Spring Bootの組み込みAPサーバーを使う方法
- 組み込みAPサーバーを使わず、代わりにSpring MVCのテストフレームワークであるMockMvcを使う方法
MockMvcを使う方法はさらに次の二種類に分かれます。
- SpringのWebアプリケーションコンテキストを自動構成で有効にする方法
- テスト対象ControllerクラスにAutoConfigureMockMvcアノテーションを指定します
- SpringのWebアプリケーションコンテキストを有効にせず、Webレイヤーの特定Controllerのみを対象とする方法
- テスト対象ControllerクラスにWebMvcTestアノテーションを指定します
テスト対象は組み込みAPサーバーを使う方法がもっとも広く、Webアプリケーションコンテキストを有効にしない方法がもっとも狭くなります。テストコードでどのスコープを対象とするかは議論のあるところですが(私はいわゆる古典学派の立場が好み)、スコープが選択できるのは悪いことではありません。
とはいえ選択肢があるからといって常にすべてが選択可能とはかぎりません。セッション(Springではセッションスコープやセッションコンテキストといった表現を用いています)と関連付けるControllerの単体テストは選択肢が限られる単体テストにあたり、セッションのスコープ管理にはWebアプリケーションコンテキストが必要なため、Webアプリケーションコンテキストを有効にしない単体テストは利用できません。
これはSpringおよびMockMvcの仕組みを考えれば理解できます。
- SpringはアノテーションでBean指定されたクラスのインスタンスを自動的に生成する。またコンストラクタやメソッド呼び出しの際に引数がBean指定したクラスであれば依存性注入(DI)の一環で自動生成したインスタンスを利用する
- 逆に言えば、自動生成したインスタンスがなければDIを要求するコンストラクタやメソッドは呼び出せない
- SessionScopeアノテーションを指定してセッション用としたクラスは自動生成のタイミングがセッション開始時になる
- セッションはWebアプリケーションコンテキストと関連づくため、Webアプリケーションコンテキストが有効でなければ単体テストを実行できない
このように機序をたどれば理由は明白なのですが、Springに慣れない人間の試行錯誤にはまったく親切ではありません。仕組みからわかるように、Webアプリケーションコンテキストを有効にしない単体テストは利用できないという事実は Webアプリケーションコンテキストを有効にしない単体テストを実行しないとわからない からです。しかもその事実は例外の送出=単体テストの失敗報告という形で報告されます。単体テストの失敗なのかそもそも単体テストができなかったのかは失敗の内容を精査しなければなりません。Springに慣れていない開発者にとっては荷の重い作業と言えます。私は理解するまでずいぶん時間がかかりました。
アノテーションで動きを制御するSpringのやりかたはたしかに便利ではありますが、構造が隠蔽されてしまうという点ではかならずしも優れているとばかりは言えないように思います。今後は送出例外を工夫するなどのより原因の掴みやすい報告を期待したいところです。
以上、お役に立てば幸いです。