『フロントエンド開発のためのテスト入門』

先日こちらの本を買いました。

https://www.amazon.co.jp/%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E9%96%8B%E7%99%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E5%85%A5%E9%96%80-%E4%BB%8A%E3%81%8B%E3%82%89%E3%81%A7%E3%82%82%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E8%87%AA%E5%8B%95%E3%83%86%E3%82%B9%E3%83%88%E6%88%A6%E7%95%A5%E3%81%AE%E5%BF%85%E9%A0%88%E7%9F%A5%E8%AD%98-%E5%90%89%E4%BA%95-%E5%81%A5%E6%96%87/dp/4798178187

以前の記事でもフロントエンドのテストについて触れたので改めて勉強してみようと思った次第です。

表紙にあるように、「何から始めるべきか」を示してくれる1冊でした。

テストを書く目的

第1章ではテストを書く目的と障壁について書かれています。

テストを書く目的として挙げられていたのは以下の通りです。

  • 事業の信頼のため
  • 健全なコードを維持するため
  • 実装品質に自信を持つため
  • 円滑なコラボレーションのため
  • リグレッションを防ぐため

普段バックエンドの実装を行うときはクラスごとにユニットテストを書いていますが、「健全なコードを維持するため」と「実装品質に自信を持つため」のところが大きいように思います。

例えばリファクタリングを行う際、既存の動作が壊れていないことが大前提となります。

それをどう担保するかを考える上で必要なのがテストです。

テストがしっかり書かれていればリファクタリングを行った際にも動作を担保することができます。

また、小さい単位で実装と併せてテストを書くことで各クラスの責務を意識しながら実装することにも繋がりますし、動作が担保されたクラスを使うことでテストで確認したい責務も自ずと明確になります。

小さい単位で、というのは例えば値オブジェクトです。

値オブジェクトのテストがしっかり書けていればその動作は担保されているため、それを用いたエンティティの責務や振る舞いも明確になります。

同様にエンティティのテストを書くことでリポジトリ等の動作も...というように小さな単位から積み上げていくことでより強固な実装になります。

そうすると自信を持って実装することができます。

なので先程述べた2つが大事であると考えていました。

それ以外のところで言うと「円滑なコラボレーションのため」もなるほどと思いました。

テストコードは、単純なテキストドキュメントよりも優れた補足情報です(図1-4)。テストには1つずつタイトルが与えられ、どのような機能を提供しているのか、どのような振る舞いを持つのかが記されています。それらのテストをパスしていますから、補足内容と実装内容が異なるということもありません。

吉井健史著『フロントエンド開発のためのテスト入門』 p.8

↑の文章は今所属しているチームで共有している「テストを仕様書にする」という文化とまさに同じです。

テストコードを見ればそのテスト対象の仕様がわかる状態が理想であり、またテキストによる仕様書と異なって仕様変更があった際にはテストが失敗するので腐ることもありません。

またレビューの際にもテストコードを見れば要件を満たしているかどうかのチェックもできるため、要件が不足したままリリース、ということもなくなります。

CIで自動テストを行えばそもそもレビュー前にテストが落ちていたら修正が必要なのでレビュワーが指摘する手間も省けます。

こうした目的を果たすために、テストを書く習慣をチームに根付かせ、常にメンテナンスしていくことが求められます。

テストを書く障壁

一方で、テストを書く文化や習慣が無い状態だとそもそもテストを書くことに対してネガティブだったりします。

  • テストを書く時間があればその分機能実装に時間を使いたい

とか、

  • そもそもテストをどう書けばいいかわからない

とか。

まず前者については長期的に見ればテストを書いた方が工数の削減に繋がります。

なぜかと言うと問題を早期に潰せるからです。

機能の実装だけ行う場合と機能の実装に加えてテストを書いた場合、そこだけを見れば確かに前者の方が工数は短くなるでしょう。

ただ、例えばQAチームによる手動テストの際にバグが発見された場合は手戻りが発生します。

その際にかかる工数はバグの原因の調査、修正、再度QAチームによる検証を勘案するとトータルではテストを書く時間より長くなります。

QAチームが見つけてくれた場合はまだ良い方で、万が一見逃してしまってそのままリリースされた場合、しかもバグを埋め込んだまま要件追加に対応してより複雑さが増していた場合はもっと大変です。

バグを見つけるのは早期であればあるほど原因の特定も修正も容易です。

予めテストを書いておくことはこうした惨事を防ぐことに繋がります。

後者の「そもそもテストをどう書けばいいかわからない」については既存のテストコードを見て、どのような観点でテストしているかを参考にすることが第一歩かと思います。

もしテストコードが全くない場合はまさに本書がその一助となるはずです。

テストの種類

ここまではフロントエンドに限らずバックエンドにも当てはまる話でしたが、第2章からはいよいよフロントエンドの話に入っていきます。

フロントエンドのテストの種類は以前の記事に書いたように以下の4つに大別されます。

これらの分類に加え、テストタイプによる分類もあります。

機能テスト

フロントエンド開発はコンポーネントベースでの開発が基本です。

機能テスト(インタラクションテスト)は開発対象のコンポーネントの機能に不具合が無いかを検証するテストです。

実際のブラウザをヘッドレスモードで起動し、UIオートメーションを用いてテストする以外にも仮想ブラウザ環境でテストする方法もあります。

フロントエンド開発のテストはこの機能テストが中心となります。

非機能テスト

名前の通り、機能に関すること以外のテストを指します。

フロントエンド開発ではアクセシビリティテスト等がこれに当たります。

正直に言いますと、本書を読むまでフロントエンドのテストは上記の機能テストだけだと思っていました。

しかし現在では何らかの制約がある場合でも可能な限り多くの人がWebサイトを使えるよう、WAI-ARIA等のアクセシビリティに関する仕様が定められています。

こうした観点からのテストもフロントエンドにおいては必要です。

リグレッションテスト

差分を検出して想定外の不具合が出ていないかを検証するテストです。

フロントエンドは特にUIに関わるのでビジュアルリグレッションテストが重要視されます。

テスト戦略

2章の最後にテスト戦略についていくつかのケースを挙げて書かれています。

テストがなく、リファクタリングに不安がある場合

リリース済みの機能をリストアップし、リファクタリング前後で欠陥が混入していないことを検証するリグレッションテストを行います。

リファクタリング前にテストを書くことでリファクタリングに安心して取り組めるようになります。

また、APIサーバーへの依存がきれいに分割されていない場合はモックサーバーを使用した結合テストが効果的です。

レスポンシブレイアウトを含むプロジェクトの場合

Storybookを用いたビジュアルリグレッションテストを中心とします。

PC向けに設定したスタイルがスマートフォン向けのスタイルにも適用されてしまうことを未然に防ぐことができます。

データ永続層を含めたE2Eテストを行いたい場合

実際のAPIサーバーを含めたE2Eテストを行いたい場合はステージング環境を用意した上でブラウザ上でUIオートメーションを用いたテストを行います。

それ以外の方法としては関連システムを再現するテストコンテナを用意する手法もあります。

上記それぞれ具体的な内容については本書をご参照ください。

まとめ

以上冒頭2章をまとめてみました。

第3章からは具体的なテストの書き方に入っていくのでそちらについてもまたそのうち書くかもしれません。