Angular*1のHTTP通信部分(Serviceクラス)のテスト*2を書く際に、派手につまずいたので対応方法と経緯をメモ。
テスト対象の実装
HTTP通信には、AngularのHttpClientを用いている。
実装側のコードにおいては、公式ドキュメントにあるように、通信を使用するModuleクラス*3でHttpClientModule
をインポートしたうえで、ServiceクラスにHttpClient
をインジェクトする。これでHTTP通信が利用できるようになる。
これのテストコードを書きたい。公式ドキュメントには、モックを用いたテストの方法が書いてあるが、今回は実際のAPI(サーバーサイドの実装)をテストしたいという意味合いもあったので、モックを利用しないテストを書く。
対応方法
結論から先に書くと、テスト側のコードにおいては、下記のように、HttpClientModule
をインポートすればよい。
beforeEach(() => TestBed.configureTestingModule({ imports: [HttpClientModule] }));
以上のように記載することで、テスト実行時に正常にHTTP通信を実行することができる。
対応までの経緯
まず、何も考えずにテストケース(it
の部分)を記載する。すると、下記のようなエラーになる。
Error: StaticInjectorError(DynamicTestModule)[ConnpassService -> HttpClient]: StaticInjectorError(Platform: core)[ConnpassService -> HttpClient]: NullInjectorError: No provider for HttpClient!
HttpClient
のインジェクトに失敗したと言われる。確かに、実装側のServiceクラスは、HttpClient
をインジェクトされている。そこで、下記のように書きたくなった。
beforeEach(() => TestBed.configureTestingModule({ providers: [HttpClient] }));
実行すると、下記のようなエラーとなる。
Error: StaticInjectorError(DynamicTestModule)[HttpClient -> HttpHandler]: StaticInjectorError(Platform: core)[HttpClient -> HttpHandler]: NullInjectorError: No provider for HttpHandler!
インジェクションに失敗しているらしい。「HttpHandler
がないと怒られているのかー」と考え、providers
に追加してみる。
beforeEach(() => TestBed.configureTestingModule({ providers: [HttpClient, HttpHandler] }));
テストを再実行すると、下記のようなエラーになった。
Expected TypeError: _this.handler.handle is not a function to be null.
このエラーメッセージは苦しい。Googleで検索してもなかなかズバリ答えが見つけられなかった。
そもそも、HttpClient
は明示的に実装側でインジェクトしているものなのに対して、HttpHandler
はそうではない。エラーメッセージを見て、何も考えずにproviders
に追加してしまったが、モックを利用しているわけでもないのに実装側で意識していないことをテスト側で意識しなければならないのはどこかがおかしいと思うべきだった。
試行錯誤を繰り返した結果、先述のようにimports
にHttpClientModule
を記載したことでエラーが解消した。実装側で、
HttpClientModule
をインポートHttpClient
をインジェクト
という2段階を踏んでいるのだから、真っ先にHttpClientModule
への依存を疑うべき事案だった。
感想
依存しているモジュールをimports
に追加するというのは、Angularの仕組み、TestBed
クラスの仕組みを理解していればすぐにわかることなのだろう。有識者に聞けばすぐに答えが返ってくると思われるので、Angular Japan User GroupのSlackも活用していきたい。
今回、テストについての情報をGoogleを駆使して検索してみて、うまく答えにたどり着けなかった。エラーメッセージで検索をするのが常套手段だが、英語の記事を含めてあまり的確な情報を獲得することができなかったので、記事として書いておくことにした。
※記事の内容の誤り、もっといい対応方法等があれば、ぜひご指摘ください。