Upgrade to Pro — share decks privately, control downloads, hide ads and more …

webフロントエンドテストと自動化

 webフロントエンドテストと自動化

Cybozu
PRO

July 13, 2023
Tweet

More Decks by Cybozu

Other Decks in Technology

Transcript

  1. webフロントエンドテスト
    ⾃動化
    フロントエンドエキスパートチーム
    左治⽊隆成

    View Slide

  2. コンセプト
    誰に
    • webフロントエンド開発に携わる⼈たちに
    なんと⾔ってほしい
    • 「フロントエンドテストの書き⽅にイメージがついた」
    • 「最近のwebフロントエンドのテストの潮流をつかめた」
    • 「フロントエンドのテスト書いてくぞー!!!!」

    View Slide

  3. ⽬次
    1. テストコードについておさらい
    • テストコードの意義などを軽くおさらい
    2. フロントエンドテストの⽬的と⼿法
    • どういった種類のテストがあるのか
    • 何を保証したくてフロントエンドテストを書くのか
    3. フロントエンドテストを⽀える技術
    • どのようにフロントエンドテストを書いていけば良いのか
    • 具体的なライブラリの使い⽅を知る
    4. テストを書いてみよう!
    • 実際にインテグレーションテストを書いてみる

    View Slide

  4. テストコードについて
    何のためにテストを書くのか?
    • サービスの信頼のため
    • コードに⾃信を持つため
    • 綺麗なコードを維持するため
    • コミュニケーションのため
    • リファクタをしやすくするため
    • …etc

    View Slide

  5. テストコードを書こう!

    View Slide

  6. 開発スピードが遅くなる?
    → そんなことはない!

    View Slide

  7. 品質とスピードはトレードオフではない
    • 「内部品質を犠牲にしているから遅い」
    • “内部品質への投資の損益分岐点は 3年後とかではなく 1ヶ⽉以内に現
    れる”
    • 参考) 3⽉に来ていただいた t-wadaさんの社内公演を聞くべし
    • (社内向けリンク)

    View Slide

  8. テストコードの意義
    • 数ヶ⽉先に⾃分が書いたコードが残っているはずならちゃんとテスト
    を書こう
    • 実際kintoneのClosureのコードは10年⽣きてます。
    • そうでなくても数ヶ⽉前の⾃分は他⼈です
    • 実装している時でも、「テストコード書いたおかげでレビュー前に実
    装が不⾜してる箇所を気づけた」といったことがある

    View Slide

  9. テストコードを書こう!

    View Slide

  10. でもどう書けばいいのさ

    View Slide

  11. フロントエンドテストの
    ⽬的と⼿法

    View Slide

  12. テスト範囲による分類
    ⼀般的に4つの範囲に分けることが多い
    • 静的解析
    • 単体テスト
    • 結合テスト
    • E2Eテスト

    View Slide

  13. 静的解析
    • lintツール、型解析ツールと呼ばれるもの
    • モジュール間のインターフェース不整合やコードの表現の⼀貫性を保
    証する

    View Slide

  14. 単体テスト
    • ⼀つのモジュールの機能を保証する
    • 複雑なモジュールのエッジケースなどを検証するのに有効
    • ユニット(Unit)テストとも

    View Slide

  15. 結合テスト
    • 複数のモジュールを組み合わせた挙動・機能をテストする
    • ある程度広範囲なテストをカバーできる
    • インテグレーション(integration)テストとも

    View Slide

  16. E2Eテスト
    • 本番相当の環境で、システム・アプリケーションが⼀貫して動作する
    ことを確認するテスト
    • フロントエンド〜バックエンドを⼀貫してテストする
    • よりユーザーの体験に近い形でテストができる

    View Slide

  17. それぞれの特徴
    静的解析 単体テスト 結合テスト E2Eテスト
    実⾏コスト
    テスト範囲
    不安定さ
    低 ⾼


    不安定
    安定

    View Slide

  18. テストピラミッドモデルへ
    静的解析→単体テスト→結合テスト→E2Eテストの順に
    • 👎 実⾏保守コストや開発・実⾏速度は⾼くなる
    • 👍 広範囲のテストが可能になっていく
    → テストの⽐率をピラミッド型にすればコストと保証する品質のバラ
    ンスが取れるのでは?

    View Slide

  19. テストピラミッド
    E2E
    結合テスト
    単体テスト
    $ $ $
    ¢
    🏎
    🐢

    View Slide

  20. …ここまでが復習

    View Slide

  21. フロントエンドにフォーカス
    するとどうなるか?

    View Slide

  22. 前提1 : 近年のフロントエンドの進化
    • サーバーサイドとフロントエンドが別れた世界に
    • フロントエンドできること・やることが増える
    • 複雑な状態管理
    • SPA によるシームレスなUI更新
    • ServiceWorkerによるキャッシュ管理やオフライン対応
    • etc…
    → フロントエンド単体で複雑性が増している。

    View Slide

  23. 前提2 : フロントエンド特有なこと
    • ユーザーからのインタラクションを起点に動作する
    • ある程度単純なコンポーネントの挙動の組み合わせで複雑になりがち
    • ⽂書構造があり、それをブラウザや読み上げソフトが解釈する
    • ⾒た⽬・スタイルがある

    View Slide

  24. フロントエンドにフォーカスすると⾒えてきたもの
    • フロントエンドの進化
    • フロントエンドに閉じてても複雑な挙動が増える
    • フロントエンド特有の事情
    • ⼀つのコンポーネントだけで成⽴する機能は少ない
    • ユーザーからのインタラクションを起点に動作する
    • ⾒た⽬・⽂書構造がある

    View Slide

  25. フロントエンドに特化するとどうなるか
    • Testing trophyモデルの提唱
    • フロントエンド特有の事情を鑑みた新しいテストモデルの提唱
    • VRT・a11yテストの普及
    • ⾒た⽬・⽂書構造があるをテストする⼿段

    View Slide

  26. Testing trophyモデル
    • kent.c.Dodds⽒が提唱するモデル
    • The Testing Trophy and Testing
    Classifications
    • コード・ロジックのカバレッジではなく、
    ユーザーからみたユースケースのカバレッ
    ジを重視すべきという考えに基づく

    View Slide

  27. Testing trophyモデル
    • スピード・コストに加えて信頼度とのバランスをとる
    • 信頼度 = どれだけ動作に⾃信を得られるか。
    • “The more your tests resemble the way your software is used, the more
    confidence they can give you.”
    • ユーザーの操作を起点とした、複数のモジュール・コンポーネントを
    跨ぐテストをコストを抑えつつ書いていきたい
    • 結合テストを充実させていくと良いのではないか
    • Write tests. Not too many. Mostly integration.

    View Slide

  28. Testing trophy : 静的解析
    • LintやTypeScriptが該当する
    • ⼀般的にカバレッジには含まれない
    • 適切なルールの運⽤と型を厳格にすること
    でロジックのミスをかなり減らすことがで
    きる

    View Slide

  29. Testing trophy : 単体テスト
    • オブジェクト・関数、hooks、単体コン
    ポーネントのテストなどが該当する
    • エッジケースの多いロジックなど⼀つのモ
    ジュール単位で複雑な挙動をもつものには
    最適

    View Slide

  30. Testing trophy : 結合テスト
    • コンポーネントや関数など複数のものが合
    わさったもののテスト
    • ここを厚くすることでユースケースのカバ
    レッジをあげる
    • 使われているコンポーネントや機能の結合
    テストが通れば、動きが保証できる

    View Slide

  31. Testing trophy : E2Eテスト
    • 信頼度が⼀番⾼く、コストもかかるもの
    • 重要なユーザー体験や最低限の動作を保証
    するなどの利⽤することが多い

    View Slide

  32. 結合テストのコスト
    Q . 結合テストの実⾏コスト⾼くないの?
    A. 後述する「DOMをエミュレートする」タイプのテストツールだと以
    下のような理由からE2Eテストより実⾏コストを低く抑えられる
    • ヘッドレスブラウザを介していない
    • 通信やIOを基本的にモックしている

    View Slide

  33. VRTとa11yテスト

    View Slide

  34. VRT : Visual Regression Test
    = ⾒た⽬の回帰テスト
    つまり「⾒た⽬変わっちゃってない?」を毎度確認するテスト

    View Slide

  35. a11yテスト
    • Lintツールである程度チェック可能
    • eslint-plugin-jsx-a11y など
    • コンポーネントの結合テストなどでもアクシブルな情報を利⽤した操
    作などでテストが可能(後述)
    • 読み上げなど、⾃動化が難しいものは別途⼿動試験をしたりできると
    良い

    View Slide

  36. フロントエンドテスト
    を⽀える技術

    View Slide

  37. 各テストよく使われるライブラリ
    • 静的解析
    • eslint : JavaScriptのリントツールとしてデファクトなライブラリ
    • Typescript : 型チェックを⾏える
    • 単体テスト
    • Jest : メジャーなJavaScriptテストフレームワーク
    • Vitest : Jest互換の⽐較的新しいJavaScriptテストフレームワーク

    View Slide

  38. 各テストよく使われるライブラリ
    • 結合テスト
    • Testing Library : Domをエミュレートして動作を実⾏確認できるツール
    • jest-dom : jestのDOM⽤拡張。DOM⽤のマッチャーなどが追加される。
    • E2Eテスト
    • Cypress : Selenium以降に台頭したE2Eテストフレームワーク
    • PlayWright : Microsoftが中⼼に開発する後発のOSS E2Eテストツール。

    View Slide

  39. 各テストよく使われる技術
    VRTテスト
    • Chromatic (Storybook)
    • Storybook公式が提供するサービス。
    • Storybookで表⽰するコンポーネントでそのままVRTが可能。
    • reg-suite × E2Eテストツールによるスクリーンショット
    • E2Eテストツール で撮ったスクリーンショットを元にVRTを実⾏する

    View Slide

  40. 実際にどんな感じ書くの?

    View Slide

  41. 実践編
    Testing LibraryとJestを使った
    結合テストの書き⽅
    Write tests. Not too many. Mostly integration.

    View Slide

  42. 結合テストの3要素
    やりたいことは基本的に3つ
    1. 要素を探して取ってきて
    2. 何らかの操作をして
    3. 結果を検証する

    View Slide

  43. 結合テストの3要素
    それぞれの分担
    • 要素を探して取ってきて : TestingLibrary
    • 何らかの操作をして : TestingLibrary
    • 結果を検証する : Jest(の拡張) / TestingLibrary

    View Slide

  44. 要素を探す

    View Slide

  45. 要素の探し⽅
    以下のような感じで要素を取得する
    • TestingLibraryには⾊々な要素の探し⽅(=クエリ)がある。
    • 適切なクエリを使って要素を取得することが⼤事
    const addButton = await screen.findByRole('button’)}

    View Slide

  46. TestingLibraryのクエリ分類
    get
    find
    query
    All
    (なし)
    ByRole
    ByLabelText
    ByPlaceholderText
    ByText
    ByDisplayValue
    ByAltText
    ByTitle
    ByTestId

    View Slide

  47. TestingLibraryのクエリ分類1
    get〇〇 / query〇〇/ find〇〇 の違いと Allのあるなし
    ⾒つからない時 1つ⾒つかった場

    2つ以上⾒つかっ
    た場合
    ⾒つからない場合
    にretry
    getBy○○ error 要素 error しない
    queryBy○○ nullを返す 要素 error しない
    findBy○○ error 要素 error する
    getAllBy○○ error 要素⼀つの配列 複数要素の配列 しない
    queryAllBy○○ 空配列を返す 要素⼀つの配列 複数要素の配列 しない
    findAllBy○○ error 要素⼀つの配列 複数要素の配列 する

    View Slide

  48. TestingLibraryのクエリ分類2
    どのような特徴で要素を探すか(By〇〇の部分)
    • 使うクエリには優先度がある
    • 適切なクエリを使って要素を取得することが⼤事(再掲)
    原則 : ユーザーの操作に沿うこと
    ..「ユーザーの操作に沿う」ってなに?

    View Slide

  49. よくありがちだけど🫤な取得⽅法
    body → div[0] → ul → li[0] → … → button
    • みんな普段ページを操作するとき、DOM treeみて要素探してるの?
    getById("password-input")
    • みんなDOMのIdとかClassName⾒てページ操作してるの?

    View Slide

  50. そんなわけない!

    View Slide

  51. 普段の操作を思い出してみる
    ユーザー名とパスワードを⼊⼒してログインしてください

    View Slide

  52. 普段の操作を思い出してみる
    どうして上のテキストボックスがログイン名だと分かった?
    • → プレイスホルダーにログイン名って書いてあったから
    どうして右下がログインボタンだと分かった?
    • → ログインと書いてあるボタンだから

    View Slide

  53. 優先されるべきクエリ
    以下のような情報から要素を取得すべき
    • アクセシビリティロール
    • ラベル
    • placeholder
    • テキスト
    これらは視覚的な認知とスクリーンリーダーなどによる機械的な解釈が
    ⼀致している(はず)から

    View Slide

  54. 視覚的認知できるのにうまく取得できない…
    = ⾒た⽬と⽂章の構造・解釈が⼀致していない
    = そもそもa11y上の問題を抱えている可能性が⾼い
    可能な限り治しましょう!
    誰でもアクセスできる情報をもとにしたクエリから要素を取得すること
    はa11yを確認することにもつながる!

    View Slide

  55. 具体的なTestingLibraryのクエリ
    全ての⼈にとってアクセシブルなクエリ(なるべくこれを使おう)
    • ByRole : WAI-ARIAのrole属性から取得する
    • ByLabelText : フォームなどのラベルから取得する
    • ByPlaceholderText : ⽂字⼊⼒のプレイスホルダーから取得する
    • ByText : ⼊⼒とかではない⽂書コンテンツなどを取得する
    • ByDisplayValue : フォームないの現在値から取得する。

    View Slide

  56. 具体的なTestingLibraryのクエリ
    HTML5 および ARIA 準拠ではあるものの挙動・解釈はブラウザ・⽀援
    技術によって⼤きく異なるクエリ
    • ByAltText : 画像などををalt text から取得する
    • ByTitle : title属性から要素を取得する。

    View Slide

  57. 具体的なTestingLibraryのクエリ
    どうしてもうまく要素が取れない時・要素が動的に⽣成される時などに
    利⽤するクエリ
    • ByTestId : 要素につけた data-testid 属性か要素を取得する

    View Slide

  58. 結合テストの3要素
    やりたいことは基本的に3つ
    1. 要素を探して取ってきて
    2. 何らかの操作をして
    3. 結果を検証する

    View Slide

  59. 要素に対する操作
    userEventオブジェクトを利⽤する
    クリック
    • userEvent.click([クリックしたい要素])
    テキスト⼊⼒
    • userEvent.type([⼊⼒したい要素],”打ちたい⽂字列”)

    View Slide

  60. 結合テストの3要素
    やりたいことは基本的に3つ
    1. 要素を探して取ってきて
    2. 何らかの操作をして
    3. 結果を検証する

    View Slide

  61. よくある検証したいこと
    特定の要素があるか?or 消えているか?
    要素が特定の数あるか?
    正しくリクエストが⾶んでいるか?

    View Slide

  62. 特定の要素があるか?or 消えているか?
    testing-library とjestを使う
    // 要素があるか?
    // → 要素を findBy や getBy で取得する(⾒つからないとErrorになるので)
    const addButton = await screen.findByRole('button', { name: '追加' });
    // 要素が消えているか?
    // → 要素を queryByで取得し、null であることを確かめる
    const addButton = screen.queryByRole('button', { name: '追加' });
    expect(addButton).toBeNull();

    View Slide

  63. 要素が特定の数あるか?
    testing-library とjestを使う
    // 要素が特定の数あるか?
    // → 要素を findAllBy などで探し
    const nameList = await screen.findAllByRole('listItem’,);
    // 個数を確かめる
    expect(nameList.length).toBe(3);

    View Slide

  64. 正しくリクエストが⾶んでいるか?
    ..の前にリクエストのモック(テストダブル)が必要

    View Slide

  65. リクエストのモックについて
    結合テストがカバーできるのはフロントエンドの範囲内
    • = 通信はモックする(ダミーを作る)必要がある
    ServerSide
    FrontEnd request ?

    View Slide

  66. 通信のモック
    最近では msw (mock service worker) というライブラリを使うことが
    多い
    • service worker上で通信を横取りする
    • リクエストに対応したレスポンスを設定しておくことで通信をモック
    Jestと併⽤するとリクエストの中⾝・呼ばれた回数・タイミングなどを
    検査できる

    View Slide

  67. 通信のモック
    イメージ
    ServerSide
    FrontEnd
    request MSW
    response(mock)
    requestを⾒る
    &
    いい感じの
    mockを返す

    View Slide

  68. 改 : 正しくリクエストが⾶んでいるか?
    リクエストのモックとjestのモック関数を使う
    1. jest.fn() を利⽤してモック関数を作る
    • モック関数 = どんな引数で呼ばれたか・何回呼ばれたかなどを検証可能
    2. リクエストをモックする(※⽅法は後述)
    3. 2.でリクエストが来たらモック関数にリクエストの中⾝を渡す
    4. モック関数がどう呼び出されたか検証する

    View Slide

  69. TestingLibraryを⽤いた結合テストだと難しいもの
    1. ホバー時のスタイル確認
    • DOMをエミュレートしてるだけなのでCSSの機能を確認するのは難しい
    2. ドラッグ&ドロップのような操作
    • ドラッグの位置情報などを計算することは基本できない
    3. スクロールの絡む操作
    • そもそも画⾯のサイズによって変わるのものなので難しい

    View Slide

  70. ⻑々と話してきましたが…

    View Slide

  71. 講義だけだど眠くなっちゃう
    ので実際に書いてみよう!

    View Slide

  72. 演習

    View Slide

  73. 演習で使う環境について軽く説明
    今回はStorybookを利⽤
    • Storybookでコンポーネントのインタラクションテストが書ける
    • = Storybook上でJestやtesting-libraryが動いてるイメージ
    • メリット
    • ⽬で実⾏結果が確認できる
    • 環境構築が楽

    View Slide

  74. 演習をする前に
    必要なもの
    • git 環境
    • Node.js v18 以上の環境
    • (社内リンク)
    • お気に⼊りのエディタ
    • 宗教上の理由がなければ vscode が楽です。

    View Slide

  75. 演習準備
    1. 以下のリポジトリをclone
    • https://github.com/sajikix/frontend-test-training-2023
    2. README.mdを開く
    3. READMEに従って storybookを起動
    1. npm ci と npm run storybook を叩くだけ
    4. うまく⾏くと https://localhost:6006 でstorybookが起動する。

    View Slide

  76. Storybookの操作説明
    画⾯共有して説明します。

    View Slide

  77. コードの軽い説明
    • src/page にテスト対象のコンポーネントがいる
    • 〇〇.stories.tsx ファイルがStorybookを表⽰するためのファイル
    • 〇〇.stories.tsx ファイルを開くと、Task1 みたいな名前のオブジェク
    トがある
    • この⼀つ⼀つがStorybookObjectと呼ばれるものでStorybook上の1
    ページに対応してる

    View Slide

  78. StorybookObjectの説明
    • play関数の中に処理を書いていく
    • Storybook上で書かない場合と⼤まかには⼀緒!
    • セットアップとかcanvas取ってくるとこが微妙に違うくらい
    • 変更して保存すると⾃動的にStorybookがリロードされる

    View Slide

  79. 問題を解いてみよう!
    • 問題1~5を⽳埋めで書いてみよう
    • 書けたと思ったらStorybookのinteractionタブを確認!
    • うまく動いていればPASSEDになるよ!
    • 解答例は answers/ にあるので解けたら⾒てみよう
    • 質問あったら遠慮なくコメントください !

    View Slide

  80. 盛り込めなかった話
    E2Eテストでの⾃動化⼿法について
    VRTテストの詳しい実装⽅法
    単体テストとの細かい境界
    キーボード操作など少し複雑な操作について

    View Slide

  81. 参考⽂献・学びたい⼈へ
    参考⽂献
    • https://kentcdodds.com/blog
    • https://testing-library.com/
    • https://jestjs.io/
    より詳しく学びたい⼈へ
    • 『フロントエンド開発のためのテスト⼊⾨ 今からでも知っておきたい
    ⾃動テスト戦略の必須知識』吉井 健⽂ (著) がおすすめです。

    View Slide