MSW を使って様々な場面でモックデータを再利用する
フロントエンドアプリケーションの開発にはどうしても値の取得や更新のためのデータ通信がつきまとう。
フロントエンドとバックエンドで分業している場合、開発中にはパフォーマンスとかは度外視したモックサーバーを立てて、とりあえずレスポンスを返せるようにして見た目の作成や機能の作成をすることもあるだろう。
テストをする必要も出てくる。unit testing、integration testing, e2e testing、storybook での見た目の確認、storyshot を使った snapshot チェックといったことをすることもあるだろう。
こういった場合にMSWは非常に役に立つ。
通信処理をモック化することに疲れる
例えば axios の get や post、apollo client の query や mutate といった機能を内包した処理のテストをしたい場合、axios や apollo client をモック化したりスパイ化して、決まった値を返す、みたいな処理を書くことがあると思う。
全然 OK なのだが、もっと簡略化したい。あっちこっちにモック関数書くのもめんどくさい。MSW ではデータ通信のジャックができるため、モックを書くことはしなくてよくなる。
Kent 氏の記事は fetch のモック化を行うときの疲弊なんかがまとめられているので読むといい。 Stop mocking fetch
MSW でリクエストを傍受(Intercepts)する
MSW はネットワークレベルでリクエストをインターセプトするライブラリである。
公式ドキュメントには分かりやすく仕組みの解説が書いてあるので一読おすすめ。
サービスワーカーを利用して、リクエストをキャッチすると MSW にはたらきかけて、定義したモックデータを返すようにすることができる。Node.js 環境でも使えるように API も用意されている。
Node.js 環境での制限事項はいくつかある。 >react-testing-library では MSW を使うことを推奨している
そのため、ブラウザと Node.js 環境両方でインターセプトができる。これにより、いろいろな場面でモックデータを使い回すことができる。
test や見た目の確認のためのモックデータをいろいろなところに書かず、シンプルな記述で済ませられるのが MSW の魅力。
どんな場面で使えるのか
The same mock definition can be reused for unit, integration, E2E testing, and debugging.
と書いてあるぐらいなので、いろいろな場面で使える。
実際にどう使うのかを検証した repository を用意した。
ここでは、
- 開発サーバー(React + TS + webpack)
- Unit testing(Jest + Testing Library[react hooks])
- Integration testing(Jest + Testing Library[react])
- UI development(Storybook)
- UI snapshot testing(Storybook + storyshot)
- Visual Regression Testing(Chromatic)
- E2E testing(Cypress)
といったものを、MSW を使って定義したモックデータを利用できるように設定してある。
使ってみてどうだったか
難しいことをせずに簡単にデータ通信をインターセプトでき、モックデータをいろいろなところで使い回すことができるようになったため、修正箇所を減らすことができるようになった。
また、いろいろなところでモックを書かなくて良くなるので統一感のあるテストの記述ができるようになった。
工夫が必要だったところ
storybook の CSF 別々で違う response 返したい
エラーを返す、レスポンス返す時間が遅いなど、いろいろなケースを考慮したコンポーネント開発をしたい場合、各々の CSF でバラバラの handler を使いたい。
そういう場合は MSW 公式で管理しているmsw-storybook-addonを使うといい。
CSF それぞれの parameter に msw プロパティを設置して handlers を流す事ができる。これによって CSF によって異なる handler を使うことができるようになる。
storyshot で MSW 使いたい
データ通信という非同期処理が絡む影響で、普通に storyshot を使うと、fetch が始まる状態の snapshot が取れてしまう。
そこで、initStoryshots 関数の引数のtest
関数を、これを参考に自作すると動かせる。
なお、multiSnapshotWithOptions を使ったときのようにファイルごとの snapshot を作らせることもできる