Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
webフロントエンドテストと自動化
Search
Cybozu
PRO
July 13, 2023
Technology
35
24k
webフロントエンドテストと自動化
Cybozu
PRO
July 13, 2023
Tweet
Share
More Decks by Cybozu
See All by Cybozu
サイボウズフロントエンドエキスパートチームについて / FrontendExpert Team
cybozuinsideout
PRO
5
38k
2024/11/25 ReDesigner Online Meetup 会社紹介
cybozuinsideout
PRO
0
270
サイボウズ 開発本部採用ピッチ / Cybozu Engineer Recruit
cybozuinsideout
PRO
9
46k
テクニカルライティング
cybozuinsideout
PRO
4
390
サイボウズのアジャイルクオリティ2024
cybozuinsideout
PRO
3
330
モブに早く慣れたい人のためのガイド2024
cybozuinsideout
PRO
3
460
モバイル
cybozuinsideout
PRO
3
240
ソフトウェアライセンス
cybozuinsideout
PRO
4
210
ソフトウェアテスト
cybozuinsideout
PRO
3
330
Other Decks in Technology
See All in Technology
20241214_WACATE2024冬_テスト設計技法をチョット俯瞰してみよう
kzsuzuki
3
510
.NET 9 のパフォーマンス改善
nenonaninu
0
980
ブラックフライデーで購入したPixel9で、Gemini Nanoを動かしてみた
marchin1989
1
540
Google Cloud で始める Cloud Run 〜AWSとの比較と実例デモで解説〜
risatube
PRO
0
110
KubeCon NA 2024 Recap / Running WebAssembly (Wasm) Workloads Side-by-Side with Container Workloads
z63d
1
250
ずっと昔に Star をつけたはずの思い出せない GitHub リポジトリを見つけたい!
rokuosan
0
150
日本版とグローバル版のモバイルアプリ統合の開発の裏側と今後の展望
miichan
1
130
あの日俺達が夢見たサーバレスアーキテクチャ/the-serverless-architecture-we-dreamed-of
tomoki10
0
460
株式会社ログラス − エンジニア向け会社説明資料 / Loglass Comapany Deck for Engineer
loglass2019
3
32k
MLOps の現場から
asei
6
650
普通のエンジニアがLaravelコアチームメンバーになるまで
avosalmon
0
100
バクラクのドキュメント解析技術と実データにおける課題 / layerx-ccc-winter-2024
shimacos
2
1.1k
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
2
170
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
6
520
Designing for humans not robots
tammielis
250
25k
Code Review Best Practice
trishagee
65
17k
The Cult of Friendly URLs
andyhume
78
6.1k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
247
1.3M
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.9k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Building Your Own Lightsaber
phodgson
103
6.1k
Why Our Code Smells
bkeepers
PRO
335
57k
Transcript
webフロントエンドテスト ⾃動化 フロントエンドエキスパートチーム 左治⽊隆成
コンセプト 誰に • webフロントエンド開発に携わる⼈たちに なんと⾔ってほしい • 「フロントエンドテストの書き⽅にイメージがついた」 • 「最近のwebフロントエンドのテストの潮流をつかめた」 •
「フロントエンドのテスト書いてくぞー!!!!」
⽬次 1. テストコードについておさらい • テストコードの意義などを軽くおさらい 2. フロントエンドテストの⽬的と⼿法 • どういった種類のテストがあるのか •
何を保証したくてフロントエンドテストを書くのか 3. フロントエンドテストを⽀える技術 • どのようにフロントエンドテストを書いていけば良いのか • 具体的なライブラリの使い⽅を知る 4. テストを書いてみよう! • 実際にインテグレーションテストを書いてみる
テストコードについて 何のためにテストを書くのか? • サービスの信頼のため • コードに⾃信を持つため • 綺麗なコードを維持するため • コミュニケーションのため
• リファクタをしやすくするため • …etc
テストコードを書こう!
開発スピードが遅くなる? → そんなことはない!
品質とスピードはトレードオフではない • 「内部品質を犠牲にしているから遅い」 • “内部品質への投資の損益分岐点は 3年後とかではなく 1ヶ⽉以内に現 れる” • 参考)
3⽉に来ていただいた t-wadaさんの社内公演を聞くべし • (社内向けリンク)
テストコードの意義 • 数ヶ⽉先に⾃分が書いたコードが残っているはずならちゃんとテスト を書こう • 実際kintoneのClosureのコードは10年⽣きてます。 • そうでなくても数ヶ⽉前の⾃分は他⼈です • 実装している時でも、「テストコード書いたおかげでレビュー前に実
装が不⾜してる箇所を気づけた」といったことがある
テストコードを書こう!
でもどう書けばいいのさ
フロントエンドテストの ⽬的と⼿法
テスト範囲による分類 ⼀般的に4つの範囲に分けることが多い • 静的解析 • 単体テスト • 結合テスト • E2Eテスト
静的解析 • lintツール、型解析ツールと呼ばれるもの • モジュール間のインターフェース不整合やコードの表現の⼀貫性を保 証する
単体テスト • ⼀つのモジュールの機能を保証する • 複雑なモジュールのエッジケースなどを検証するのに有効 • ユニット(Unit)テストとも
結合テスト • 複数のモジュールを組み合わせた挙動・機能をテストする • ある程度広範囲なテストをカバーできる • インテグレーション(integration)テストとも
E2Eテスト • 本番相当の環境で、システム・アプリケーションが⼀貫して動作する ことを確認するテスト • フロントエンド〜バックエンドを⼀貫してテストする • よりユーザーの体験に近い形でテストができる
それぞれの特徴 静的解析 単体テスト 結合テスト E2Eテスト 実⾏コスト テスト範囲 不安定さ 低 ⾼
広 狭 不安定 安定
テストピラミッドモデルへ 静的解析→単体テスト→結合テスト→E2Eテストの順に • 👎 実⾏保守コストや開発・実⾏速度は⾼くなる • 👍 広範囲のテストが可能になっていく → テストの⽐率をピラミッド型にすればコストと保証する品質のバラ
ンスが取れるのでは?
テストピラミッド E2E 結合テスト 単体テスト $ $ $ ¢ 🏎 🐢
…ここまでが復習
フロントエンドにフォーカス するとどうなるか?
前提1 : 近年のフロントエンドの進化 • サーバーサイドとフロントエンドが別れた世界に • フロントエンドできること・やることが増える • 複雑な状態管理 •
SPA によるシームレスなUI更新 • ServiceWorkerによるキャッシュ管理やオフライン対応 • etc… → フロントエンド単体で複雑性が増している。
前提2 : フロントエンド特有なこと • ユーザーからのインタラクションを起点に動作する • ある程度単純なコンポーネントの挙動の組み合わせで複雑になりがち • ⽂書構造があり、それをブラウザや読み上げソフトが解釈する •
⾒た⽬・スタイルがある
フロントエンドにフォーカスすると⾒えてきたもの • フロントエンドの進化 • フロントエンドに閉じてても複雑な挙動が増える • フロントエンド特有の事情 • ⼀つのコンポーネントだけで成⽴する機能は少ない •
ユーザーからのインタラクションを起点に動作する • ⾒た⽬・⽂書構造がある
フロントエンドに特化するとどうなるか • Testing trophyモデルの提唱 • フロントエンド特有の事情を鑑みた新しいテストモデルの提唱 • VRT・a11yテストの普及 • ⾒た⽬・⽂書構造があるをテストする⼿段
Testing trophyモデル • kent.c.Dodds⽒が提唱するモデル • The Testing Trophy and Testing
Classifications • コード・ロジックのカバレッジではなく、 ユーザーからみたユースケースのカバレッ ジを重視すべきという考えに基づく
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.
Testing trophy : 静的解析 • LintやTypeScriptが該当する • ⼀般的にカバレッジには含まれない • 適切なルールの運⽤と型を厳格にすること
でロジックのミスをかなり減らすことがで きる
Testing trophy : 単体テスト • オブジェクト・関数、hooks、単体コン ポーネントのテストなどが該当する • エッジケースの多いロジックなど⼀つのモ ジュール単位で複雑な挙動をもつものには
最適
Testing trophy : 結合テスト • コンポーネントや関数など複数のものが合 わさったもののテスト • ここを厚くすることでユースケースのカバ レッジをあげる
• 使われているコンポーネントや機能の結合 テストが通れば、動きが保証できる
Testing trophy : E2Eテスト • 信頼度が⼀番⾼く、コストもかかるもの • 重要なユーザー体験や最低限の動作を保証 するなどの利⽤することが多い
結合テストのコスト Q . 結合テストの実⾏コスト⾼くないの? A. 後述する「DOMをエミュレートする」タイプのテストツールだと以 下のような理由からE2Eテストより実⾏コストを低く抑えられる • ヘッドレスブラウザを介していない •
通信やIOを基本的にモックしている
VRTとa11yテスト
VRT : Visual Regression Test = ⾒た⽬の回帰テスト つまり「⾒た⽬変わっちゃってない?」を毎度確認するテスト
a11yテスト • Lintツールである程度チェック可能 • eslint-plugin-jsx-a11y など • コンポーネントの結合テストなどでもアクシブルな情報を利⽤した操 作などでテストが可能(後述) •
読み上げなど、⾃動化が難しいものは別途⼿動試験をしたりできると 良い
フロントエンドテスト を⽀える技術
各テストよく使われるライブラリ • 静的解析 • eslint : JavaScriptのリントツールとしてデファクトなライブラリ • Typescript :
型チェックを⾏える • 単体テスト • Jest : メジャーなJavaScriptテストフレームワーク • Vitest : Jest互換の⽐較的新しいJavaScriptテストフレームワーク
各テストよく使われるライブラリ • 結合テスト • Testing Library : Domをエミュレートして動作を実⾏確認できるツール • jest-dom
: jestのDOM⽤拡張。DOM⽤のマッチャーなどが追加される。 • E2Eテスト • Cypress : Selenium以降に台頭したE2Eテストフレームワーク • PlayWright : Microsoftが中⼼に開発する後発のOSS E2Eテストツール。
各テストよく使われる技術 VRTテスト • Chromatic (Storybook) • Storybook公式が提供するサービス。 • Storybookで表⽰するコンポーネントでそのままVRTが可能。 •
reg-suite × E2Eテストツールによるスクリーンショット • E2Eテストツール で撮ったスクリーンショットを元にVRTを実⾏する
実際にどんな感じ書くの?
実践編 Testing LibraryとJestを使った 結合テストの書き⽅ Write tests. Not too many. Mostly
integration.
結合テストの3要素 やりたいことは基本的に3つ 1. 要素を探して取ってきて 2. 何らかの操作をして 3. 結果を検証する
結合テストの3要素 それぞれの分担 • 要素を探して取ってきて : TestingLibrary • 何らかの操作をして : TestingLibrary
• 結果を検証する : Jest(の拡張) / TestingLibrary
要素を探す
要素の探し⽅ 以下のような感じで要素を取得する • TestingLibraryには⾊々な要素の探し⽅(=クエリ)がある。 • 適切なクエリを使って要素を取得することが⼤事 const addButton = await
screen.findByRole('button’)}
TestingLibraryのクエリ分類 get find query All (なし) ByRole ByLabelText ByPlaceholderText ByText
ByDisplayValue ByAltText ByTitle ByTestId
TestingLibraryのクエリ分類1 get〇〇 / query〇〇/ find〇〇 の違いと Allのあるなし ⾒つからない時 1つ⾒つかった場 合
2つ以上⾒つかっ た場合 ⾒つからない場合 にretry getBy◦◦ error 要素 error しない queryBy◦◦ nullを返す 要素 error しない findBy◦◦ error 要素 error する getAllBy◦◦ error 要素⼀つの配列 複数要素の配列 しない queryAllBy◦◦ 空配列を返す 要素⼀つの配列 複数要素の配列 しない findAllBy◦◦ error 要素⼀つの配列 複数要素の配列 する
TestingLibraryのクエリ分類2 どのような特徴で要素を探すか(By〇〇の部分) • 使うクエリには優先度がある • 適切なクエリを使って要素を取得することが⼤事(再掲) 原則 : ユーザーの操作に沿うこと ..「ユーザーの操作に沿う」ってなに?
よくありがちだけど🫤な取得⽅法 body → div[0] → ul → li[0] → …
→ button • みんな普段ページを操作するとき、DOM treeみて要素探してるの? getById("password-input") • みんなDOMのIdとかClassName⾒てページ操作してるの?
そんなわけない!
普段の操作を思い出してみる ユーザー名とパスワードを⼊⼒してログインしてください
普段の操作を思い出してみる どうして上のテキストボックスがログイン名だと分かった? • → プレイスホルダーにログイン名って書いてあったから どうして右下がログインボタンだと分かった? • → ログインと書いてあるボタンだから
優先されるべきクエリ 以下のような情報から要素を取得すべき • アクセシビリティロール • ラベル • placeholder • テキスト
これらは視覚的な認知とスクリーンリーダーなどによる機械的な解釈が ⼀致している(はず)から
視覚的認知できるのにうまく取得できない… = ⾒た⽬と⽂章の構造・解釈が⼀致していない = そもそもa11y上の問題を抱えている可能性が⾼い 可能な限り治しましょう! 誰でもアクセスできる情報をもとにしたクエリから要素を取得すること はa11yを確認することにもつながる!
具体的なTestingLibraryのクエリ 全ての⼈にとってアクセシブルなクエリ(なるべくこれを使おう) • ByRole : WAI-ARIAのrole属性から取得する • ByLabelText : フォームなどのラベルから取得する
• ByPlaceholderText : ⽂字⼊⼒のプレイスホルダーから取得する • ByText : ⼊⼒とかではない⽂書コンテンツなどを取得する • ByDisplayValue : フォームないの現在値から取得する。
具体的なTestingLibraryのクエリ HTML5 および ARIA 準拠ではあるものの挙動・解釈はブラウザ・⽀援 技術によって⼤きく異なるクエリ • ByAltText : 画像などををalt
text から取得する • ByTitle : title属性から要素を取得する。
具体的なTestingLibraryのクエリ どうしてもうまく要素が取れない時・要素が動的に⽣成される時などに 利⽤するクエリ • ByTestId : 要素につけた data-testid 属性か要素を取得する
結合テストの3要素 やりたいことは基本的に3つ 1. 要素を探して取ってきて 2. 何らかの操作をして 3. 結果を検証する
要素に対する操作 userEventオブジェクトを利⽤する クリック • userEvent.click([クリックしたい要素]) テキスト⼊⼒ • userEvent.type([⼊⼒したい要素],”打ちたい⽂字列”)
結合テストの3要素 やりたいことは基本的に3つ 1. 要素を探して取ってきて 2. 何らかの操作をして 3. 結果を検証する
よくある検証したいこと 特定の要素があるか?or 消えているか? 要素が特定の数あるか? 正しくリクエストが⾶んでいるか?
特定の要素があるか?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();
要素が特定の数あるか? testing-library とjestを使う // 要素が特定の数あるか? // → 要素を findAllBy などで探し
const nameList = await screen.findAllByRole('listItem’,); // 個数を確かめる expect(nameList.length).toBe(3);
正しくリクエストが⾶んでいるか? ..の前にリクエストのモック(テストダブル)が必要
リクエストのモックについて 結合テストがカバーできるのはフロントエンドの範囲内 • = 通信はモックする(ダミーを作る)必要がある ServerSide FrontEnd request ?
通信のモック 最近では msw (mock service worker) というライブラリを使うことが 多い • service
worker上で通信を横取りする • リクエストに対応したレスポンスを設定しておくことで通信をモック Jestと併⽤するとリクエストの中⾝・呼ばれた回数・タイミングなどを 検査できる
通信のモック イメージ ServerSide FrontEnd request MSW response(mock) requestを⾒る & いい感じの
mockを返す
改 : 正しくリクエストが⾶んでいるか? リクエストのモックとjestのモック関数を使う 1. jest.fn() を利⽤してモック関数を作る • モック関数 =
どんな引数で呼ばれたか・何回呼ばれたかなどを検証可能 2. リクエストをモックする(※⽅法は後述) 3. 2.でリクエストが来たらモック関数にリクエストの中⾝を渡す 4. モック関数がどう呼び出されたか検証する
TestingLibraryを⽤いた結合テストだと難しいもの 1. ホバー時のスタイル確認 • DOMをエミュレートしてるだけなのでCSSの機能を確認するのは難しい 2. ドラッグ&ドロップのような操作 • ドラッグの位置情報などを計算することは基本できない 3.
スクロールの絡む操作 • そもそも画⾯のサイズによって変わるのものなので難しい
⻑々と話してきましたが…
講義だけだど眠くなっちゃう ので実際に書いてみよう!
演習
演習で使う環境について軽く説明 今回はStorybookを利⽤ • Storybookでコンポーネントのインタラクションテストが書ける • = Storybook上でJestやtesting-libraryが動いてるイメージ • メリット •
⽬で実⾏結果が確認できる • 環境構築が楽
演習をする前に 必要なもの • git 環境 • Node.js v18 以上の環境 •
(社内リンク) • お気に⼊りのエディタ • 宗教上の理由がなければ vscode が楽です。
演習準備 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が起動する。
Storybookの操作説明 画⾯共有して説明します。
コードの軽い説明 • src/page にテスト対象のコンポーネントがいる • 〇〇.stories.tsx ファイルがStorybookを表⽰するためのファイル • 〇〇.stories.tsx ファイルを開くと、Task1
みたいな名前のオブジェク トがある • この⼀つ⼀つがStorybookObjectと呼ばれるものでStorybook上の1 ページに対応してる
StorybookObjectの説明 • play関数の中に処理を書いていく • Storybook上で書かない場合と⼤まかには⼀緒! • セットアップとかcanvas取ってくるとこが微妙に違うくらい • 変更して保存すると⾃動的にStorybookがリロードされる
問題を解いてみよう! • 問題1~5を⽳埋めで書いてみよう • 書けたと思ったらStorybookのinteractionタブを確認! • うまく動いていればPASSEDになるよ! • 解答例は answers/
にあるので解けたら⾒てみよう • 質問あったら遠慮なくコメントください !
盛り込めなかった話 E2Eテストでの⾃動化⼿法について VRTテストの詳しい実装⽅法 単体テストとの細かい境界 キーボード操作など少し複雑な操作について
参考⽂献・学びたい⼈へ 参考⽂献 • https://kentcdodds.com/blog • https://testing-library.com/ • https://jestjs.io/ より詳しく学びたい⼈へ •
『フロントエンド開発のためのテスト⼊⾨ 今からでも知っておきたい ⾃動テスト戦略の必須知識』吉井 健⽂ (著) がおすすめです。