Closed24
メモ:フロントエンドのテストについて、たくさん記事読んで勉強するスクラップ
このスクラップの目的
フロントエンドのテストについて頭を整理するためのスクラップ。
関連記事を貼り付けながら、メモを残し、整理していこうと思う。
自分が持っている疑問
- どんなテストが、なんて名前で呼ばれているのかあまりわかっていない
- 例:Button クリックした後の挙動をテストする => UI テスト?ユニットテストの範囲?
- テスト文化染み付いているチームでは、どのようにテスト文化を作っていったか
- テストを日常的に書いているチームが書いているテストの対象(と対象外)
- インテグレーションテストの始め方
- 書き方ではなく、どのようにインテグレーションテストを追加した経緯的なこと
- e2e テストの始め方
- コンポーネントテストのテスト対象
- 全てのコンポーネントをテスト対象とすべきか、限定するか(何をもって)
- スナップショットテストは煩わしい?
- 意図した変更でもいちいちフェールする?
- どんなテストが、なんて名前で呼ばれているのかあまりわかっていない
例:Button クリックした後の挙動をテストする => UI テスト?ユニットテストの範囲?
メモ
- テストの分類(Testing trophy より抜粋)
-
ユニットテスト
- 個々の独立した部品が期待通りに動作することをテスト
- 関数に対して行うテスト
- コンポーネントに対して行うテスト
- スナップショットテスト
-
インテグレーションテスト
- 複数のユニットが調和して機能することをテスト
- 複数の関数が調和して機能することをテスト
- コンポーネントテストでは、ユニットテストとインテグレーションテストの区別が曖昧
-
E2Eテスト
- Functional testing, e2e と呼ばれることも
- ユーザーのように振る舞うロボットがアプリをクリックして機能をテストすること
- cypress
-
ユニットテスト
- テストの手法
- スナップショットテスト
- UI が予期せぬ変更がされていないか確かめる
- ユニットテストかもしくはインテグレーションテスト
- ビジュアルリグレッションテスト
- コンポーネントに対して行うテスト
- 視覚的なインターフェースが壊れないかチェック
- ビジュアルスナップショットテストとも呼ばれる
- ユニットテストかもしくはインテグレーションテスト
- クロスブラウザテスト
- 関数に対しても、コンポーネントに対しても行うテスト
- 異なるブラウザ・OSで正しく動作するかをテスト
- e2e
- スナップショットテスト
メモ
- テスティングトロフィー
- テストの種類
- トロフィーのそれぞれの種類の大きさは、テストする際に焦点を当てるべき量と比例
- 4つのテスト
- End to End
- ユーザーのように行動してアプリ内をクリックするヘルパーロボット
- cypress
- インテグレーション
- 複数のユニットが協調して動作するを保証
- モックは最低限に
- ネットワークリクエスト(MSW)
- アニメーションを担当するコンポーネント
- ユニット
- 個々に分離された部品
- スタティック
- コーディング中にタイポや型の誤りをチェック
- End to End
- テストを書く最大かつ最も重要な理由は 自信を得る こと
- 書いたコードが将来もアプリを壊さないこと
- トレードオフ
- 上位のテストになる程、コストが高くなる
- 上位のテストになる程、スピードが遅くなる
- テストがソフトウェアの使用方法に似ていればいるほど、より高い信頼性を得ることができる
- メジャーケースは上位のテストでカバーし、マイナーケースは下位のテストでカバーする
- テストの区別は気にしすぎない
- 目標を達成するために、さまざまなテスト戦略を組み合わせて使用する
メモ
- 種類
- ユニットテスト
- 統合テスト
- アクセシビリティテスト
- ビジュアルリグレッションテスト
- パフォーマンステスト
- テストの定義:開発の可能な限り早い段階でエラーを発見するための自動化されたツール
- 複数の異なる種類のテストを実施するのが良い
- テストを区別することができ、それによって特定の状況でどのテストを使用すべきかを理解すること
- ユニットテスト
- API のテストにおいて特に有効(モック)
- エッジケースを発見できる
- 統合テスト
- インタラクションテスト
-
data-testid
attribute を使って、必要な要素の取得を容易にする
- E2E
- ユーザーがどのようにアプリケーションとインタラクトするかという点のみ扱う。コードや実装とは一切関係なし
- 実際の DOM を有する実際のブラウザで実行される。(テスト用にエミュレートした DOM ではなく)
- ビジュアルリグレッションテスト
- インターフェースの見た目が崩れていないかを確認する
メモ
- 単体テストの設計はプロダクションコードの設計と同じくらい重要
- 単体テストの目的
- 持続可能な開発、Regression を検出
- 単体テストは、プロダクションコードと同様、コード自体が負債
- 良い単体テストとは
- Regression に対する保護
- リファクタリングへの耐性
- 迅速なフィードバック
- 保守のしやすさ
- Storybook: コンポーネントの代表的な利用シナリオと見た目の検証
- Jest: 重要な振る舞いや a11y 構造の検証
- AAA パターン
- Arrange(準備)
- Act(実行)
- Assert(確認)
- AAA パターンい基づいて記載することで、観点を自然と 1 つに絞れる
- Assert は何を検証しているかわかりやすくするため、共通化は避ける
メモ
- テスト戦略を明文化し、指針に沿ってテストを書いていく
- テストを書く理由
- 安全で高速にリリースできるようにする
- 事業成長のためにテストを書かない選択が、逆に事業成長の足枷になる可能性がある
- テスト自体も負債となるので、正しい箇所に正しいテストを書く
- テスティングトロフィー
- 上のレイヤーに行くほど、フィードバックの速度は遅い
- 上のレイヤーに行くほど、実装コストは高い
- 上のレイヤーに行くほど、信頼性は向上する
- それぞれのテストにメリット・デメリットが存在するので、それぞれを比較・検討した上で、どのテストを、どこの機能に、どの粒度でテストを書くか、テスト戦略を立てる必要がある
- テスト戦略
- トレードオフの観点でバランスの良い結合テストを厚めに書く
- 内部実装に依存しないユースケースに沿ったテストを書くことで、リファクタリングに対しても動作保証を行う
- E2E テストは、課金動線やタイムラインなどの、不具合が発生するとビジネス上のネガティブインパクトの大きい箇所だけ書く
- 単体テストは、明らかにテストしなくても自明なロジックに対しては書かない。複雑性が高いビジネスロジックの関数に関しては書く
- 静的テストはベースラインとして必ず引く。導入が後になればなるほど導入コストが跳ねあがるので、プロジェクトの最初に必ず入れる
- トレードオフの観点でバランスの良い結合テストを厚めに書く
メモ
- QAを可視化
- QAの進捗状況をリアルタイムで把握することができる
- テストケース、テスト計画、テストの実行の管理
- テスト計画を文書化し、リアルタイムの進捗を追跡
- イシュートラッカーやテスト自動化との連携
- テストを Atlassian Jira、GitHub Issues、GitLab などの要件や不具合にリンク
- CI/CDを介して実行された自動テストのレポートを作成し、自動テストと手動テストのレポートを一元化して可視化
メモ
- Datadog Synthetic Test から Playwright への移行
メモ
- Cypress でマニュアルテストを自動化し、Testrail でテストケース管理を仕組み化
- リリース前 QA の最終フェーズでリグレッションテスト
- フロントエンド、バックエンド、インフラを総合してテスト
- 役割分担
- エンジニアが単体テストやインテグレーションテストを書く
- リリース前のリグレッションテストは QA エンジニアが担当
- ルール
- ベストプラクティスに則る(Playwright の場合)
- プロダクトコードと同じ技術スタックを用いる
- コードレビューする
- リグレッションテストはテスト環境(本番環境とほぼ同等)に対して実施される
- テストデータ
- あらかじめテストケースを用意するケース
- 表示系のテスト(名前や登録日などが都度変わると困るため)
- 実行時にテストデータを作成するケース
- 更新系、削除系
- あらかじめテストケースを用意するケース
- Cypress とテスト環境の間にテストデータ作成を中継するBFF
-
Automation Status
というカスタムフィールドで、そのテストケースを自動化しているのかを定義- Manual: 手動でテストする (自動化できないケース)
- NotAutomated: 自動化できるが、まだ自動化していない
- Automated: 自動化済み
- Testrail
- Testcases: テストケースのマスタ
- Test Run: テスト実施結果を記録
- Cypress と Testrail の連携
- Cypress から TestRun の作成と、テスト実行結果の登録
- Cypress 実行時に、TestRun を作成
- Cypress のテスト実行ライフサイクルフックで ID を読み取り、実行結果を判定
- テスト結果を TestRun に登録
- Cypress から TestRun の作成と、テスト実行結果の登録
- ワークフロー
- TestRail 上でテストケースの管理
- テストコード更新、コードレビュー
- コードが Push されると CircleCI 上で Cypress が実行される
- テスト実行・レポーティング
- レポートオプションを指定して実行
- マニュアルテストの実施
- テスト結果の可視化
メモ
- テストの種類
- ユニットテスト
- インテグレーションテスト(APIモック)
- シナリオテスト
- リグレッションテスト
- ユニットテスト
-
Vue コンポーネントと Vuex に依存するロジックのテストは書かない
- ライブラリ自体のロジックのテストになりがち
- 粒度が細かくなりすぎ、メンテがしずらくなる可能性
- => インテグレーションテストでカバー
-
コンポーネントに依存しない関数のテストを書く
- コンポーネントファイルから、
.ts
ファイルに切り出すのを推奨
- コンポーネントファイルから、
- 共通ライブラリの場合(アプリケーションではなく)、比較的細かめのテストを書く
- モックデータ
- モデルの型に対応した、テストデータを作成するファクトリ関数
- インテグレーションテスト
- 主に web ページ単位のテスト
- 仕様書をベースにアプリケーションの振る舞いが期待通りかテストする、実装の詳細は考慮しない
- テストの観点
- レンダリング
- 画面の初期表示
- 権限による表示の出しわけ
- API レスポンス形式に応じた表示の出しわけ
- API エラー時の画面表示
- アクション
- ユーザー操作の結果に応じた画面の変化
- フォームサブミット時の API の呼び出し結果
- ページナビゲーション
- Validation
- フォームのバリデーション
- レンダリング
- Cypress 使って、ブラウザ上でテストしている
- メリット
- リファクタリング・パッケージ更新が用意
- オンボーディングに役立つ
- バグ防止
- デメリット
- テストにかける工数の考慮
- 仕様書とテストコードの対応付け
- シナリオテスト
- 予期している挙動全てを確認するテスト
- QAチームがマニュアルで実施
- リグレッションテスト
- リリース前に必ず実行
- シナリオテストから主要なテストケースをピックアップして自動化
- アプリケーションリポジトリとは別リポジトリ
- コードオーナーはQAチーム
-
Vue コンポーネントと Vuex に依存するロジックのテストは書かない
メモ
- テストは意図しない breaking change を防ぐために書く
- Testing Library でブラックボックステストを簡潔に記述
- 「画面をこうしたら、画面がこうなるべき」
- アプリケーションコンポーネントを、ユーザーが実際に使うやり方でテストするために有用
- DOM スナップショットテストは費用対効果が高い
- 既存のプロダクトにテストを追加する場合、仕様をまずは調査しなければならず、敷居が高い
メモ
- テスト哲学
- ユーザーの目に見える動作のテスト
- CSS のクラス名や、関数名などに頼ったテストにしない
- テストはできるだけ隔離する
- デバッグを楽にするため
- 可読性のためにコードがリピートしてしまうのは悪いことではない
- テストは少なく、テストは長く書く
- 1 テストにつき 1 アサーションのように分離するとテストが遅くなる
- どこでエラーが起こったかがわかりにくくなることはないので、メリットのが大きい(テストのアイソレーションは必要)
- データベースを使ったテスト
- ユーザーの目に見える動作のテスト
- ベストプラクティス
-
locators
を使う- チェーニングやフィルターを使う
- XPathやCSSのセレクタよりも、ユーザー向けの属性を優先する
-
locators
をジェネレートする-
codegen
を使う - VS Codeの拡張機能を使用して
locators
をジェネレートする
-
- Web ファーストなアサーションを使う
-
isVisible()
=>toBeVisible()
-
- デバッグの設定
- Playwight のツールを使う
- VS Code extenstion
- Test generator
- Trace viewer
- TypeScript
- ブラウザ横断テスト
- Playwright dependencyを常に最新の状態に保つ
- Run tests on CI
-
メモ
- 優先順位:ユーザーがどのようにアプリを使うかに近い方法でテストを作成する方針に基づく
- みんながアクセスできる Queries
-
getByRole
: 最優先 query -
getByLabelText
: フォームフィールド getByPlaceholderText
getByText
-
getByDisplayValue
: フォームの現在値
- セマンティック Queries
getByAltText
getByTitle
- Test IDs
getByTestId
- Using Queries
-
document.body
を query したいだけなら、screen
を使用することができる - ほとんどのquery API は TextMatch を引数にとる
- 文字列
- Regex
-
(content?: string, element?: Element | null) => boolean
の関数
-
メモ
- 重要度をもとにアプリケーションごとに書くべきテストを決める
- 重要度はテストの難易度とは切り分けて考える
- テストの方針をドキュメントに起こす
- Presentation/Login を分離して、テストしやすさをあらかじめ担保する
- Custom hook に Logic を切り出す
- 純粋な View component を作って、Storybook 駆動開発
- 重要な機能のみテストを厚めに書く
- チームのエンジニアのテスト習熟度に応じてテストツールを利用する
- 「最低限やっておきたいライン」、「チャレンジングだけどやりたいライン」、「後回しでもいいライン」
メモ
- play 関数:testing-library を使ってストーリー単位でインタラクション登録
- 目視でインタラクションを確認しながらテストを書くことができる
- composeStories 関数:セットアップされたストーリーを Jest でそのまま再利用可能にする合成関数
- Storybook 駆動開発
- ストーリーをフロントエンドのテスト、VRT、E2E で使い回す
メモ
- メンテナンス性の低いテスト
- 何をテストできているのか、いないのかがわからない
- => AAA パターンを採用
- プロダクションコードを追加時、どこにどのようなテストを書けば良いのかわからない
- リファクタリングへの耐性がない
- => テスト単位を実装の目線ではなく、ユーザー目線にする
- プロダクションコードをの変更の足を引っ張るような存在になっている
- 何をテストできているのか、いないのかがわからない
- MSW での API モック
- 書かなければいけない定型分が増えるので、scaffdog で自動生成できるものはする
メモ
- 動作するきれいなコード、が TDD のゴール
- Red => Green => Refactor
- 問題を小さく分割する
メモ
- Testing Trophy
- コストの高さ(実行速度、開発・保守工数)と効果(どれだけ大きな問題を解決できるか)
- 上に行くほど解決できる問題が大きく、横に広ければ広いほどカバーできる範囲が広い
- ユニットテストを書きすぎない
- より多くの Integration Test を書く
- ロジックを持った単一モジュールにはユニットテストを書き、それ以外の機能仕様についてはインテグレーションテストでカバーする
- react-testing-library で TDD を実践する
メモ
-
userEvent()
: ユーザーの動作をシミュレーションする -
fireEvent()
: DOM のイベントを発火させる
メモ
- ビジネスロジックはバックエンドに寄せ、フロントエンドは可能な限りプレゼンテーションに集中させることが定石という背景
- テスティングピラミッドから、テスティングトロフィーへ
- VRT とは、テスト実行時にUIのスクリーンショットを撮り、次のテスト実行時に以前のスクリーンショットとの差分をチェックする、回帰テストの一種
- 維持コストが低い
- ユーザーの視点でのテスト
- Chromatic を使った VRT の導入
- DOMの差分を検出するスナップショットテスト
- クラス名やDOM構造など、Componentの内部実装にまで踏み込んだテスト
- UI TestsはVRTの実行結果を表示
- 検出された差分のチェックページ
- デグレ防止のための機能
- UI Review はレビュアーの設定とディスカッションの作成が可能
- ステークホルダーにデザイン面の確認を依頼する機能
- スクリーンショットを撮るstoryを選択できる
- Template層だけに絞ってスクリーンショット数の削減
- Template層のテストは結合テスト
-
play()
を使ったインタラクションテストが可能 - 工数に対し得られるメリットが多く、ROIの高いテスト
- 費用の問題さえなければ多くのチームに適性のあるツール
メモ
- ユーザの気持ちを反映したテストを実装するためには、マシーンリーダビリティを改善させる
- testing-library の方針の「テストをソフトウェアの使用方法に似せる」に従う
- クエリの優先順位
- 視覚・マウス操作・支援技術を使ったユーザ体験を反映したクエリ群
- HTML5 と ARIA に準拠したクエリ群
- TestIDs を利用するクエリ
- プレースホルダーをラベルとして使うのは非推奨
-
getByTestId
はテストを作り始めるときに役にたつ
キリがないのでクローズ
このスクラップは2023/05/04にクローズされました