Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
コンテキストとセマンティクスを意識してリーダブルなE2Eテストコードを書こう
Search
tsuemura
July 27, 2022
Technology
30k
12
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
コンテキストとセマンティクスを意識してリーダブルなE2Eテストコードを書こう
リーダブルなテストコードについて考えよう ~VeriServe Test Automation Talk No.3~ 2022-07-27 での講演スライドです。
tsuemura
July 27, 2022
More Decks by tsuemura
See All by tsuemura
AIに気持ちよく働いていただく技術.pdf
tsuemura
0
90
Breaking your system
tsuemura
0
1.5k
自分の軸足を見つけろ
tsuemura
3
1.8k
事業継続を支える自動テストの考え方
tsuemura
0
1.6k
テスト自動化ことはじめ(202412_オープンロジ版) / Enter the testing automation (2024 Dec, for OPENLOGI)
tsuemura
0
1.9k
E2Eテストのシナリオと抽象化の粒度の話.pdf
tsuemura
7
1.5k
テスト自動化ことはじめ
tsuemura
3
690
ようこそ、ソフトウェアテストの世界へ!
tsuemura
1
190
リーダブルなE2Eテストコードのための3つのC
tsuemura
7
1.3k
Other Decks in Technology
See All in Technology
Conditional PED-ANOVA: Hyperparameter Importance in Hierarchical & Dynamic Search Spaces
pfn
PRO
0
120
Rustuna: Rustによる高速なOptuna実装
pfn
PRO
0
170
メルペイ 会計システム概要と歴史
mewuto
1
220
ハードウェアコンペでもAI駆動開発が進んでいる話
iotengineer22
1
720
実践! SRE × AI - ABEMA における AIOps -
tetsuya28
8
3k
WasmをRustにする
sansantech
PRO
0
240
Getting Started with Google Antigravity
glaforge
0
270
Active Record Connection Adaptersにおける長年の課題に再び取り組む
yahonda
0
110
DGX Spark でどこまでハッキングができるのか?
kash1064
2
1.3k
Running Kotlin + Quarkus with Lambda
nealle
1
2.4k
Optunaの最新リリースv5.0について
pfn
PRO
0
100
M5Stackで作る BLEマウス GPSログの世界
tomokusaba
0
370
Featured
See All Featured
エンジニアに許された特別な時間の終わり
watany
109
250k
Building Flexible Design Systems
yeseniaperezcruz
330
41k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Bash Introduction
62gerente
615
220k
30 Presentation Tips
portentint
PRO
1
430
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
530
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
550
Facilitating Awesome Meetings
lara
57
7.2k
BBQ
matthewcrist
90
10k
Accessibility Awareness
sabderemane
1
250
Transcript
コンテキストとセマンティクスを意識して リーダブルなE2Eテストコードを書こう Takuya Suemura @ Autify, Inc. リーダブルなテストコードについて考えよう ~VeriServe Test
Automation Talk No.3~ 2022-07-27
おれは誰だぜ 末村 拓也 開発者、フィールドエンジニア、QAなどを経て、2019年にAutifyに入社。 自動テスト、特にWebのE2Eテストに強い。 現職ではテクニカルサポートを担当。テスト対象とテストフレームワークの互換性の 問題などを調査、解決する役割を担う。 テトリスが好き。
おれが今まで話してきたこと JaSST'22 Tokyo 60分で学ぶE2Eテスト (ベリサーブ 伊藤由貴さんと共演) https://github.com/tsuemura/jasst22-tokyo JTF2020 テストを自動化するのをやめ、自動テストを作ろう https://speakerdeck.com/tsuemura/tesutowozi-dong-hua-surufalsewoyame-zi-
dong-tesutowozuo-rou 「E2Eテストとはなにか」「理想的なE2Eテストの書き方とは」を ひたすら考えたり話したりし続けている人です
今日話すこと そもそもE2Eテストってどういうものだっけ? E2Eテストをリーダブルにする理由は? どうやってリーダブルにするの?
そもそもE2Eテストとは
よくあるE2Eテストのイメージ ブラウザとかモバイルデバイスを自動操作して WebアプリとかモバイルアプリのUIを ユーザーが操作するのと同じようにテストする
None
(Webの) E2Eテストで用いられる技術 Webブラウザの自動操作技術 Selenium, Cypress, PlayWright etc. 要素特定の手段 CSS Selector,
XPath etc.
E2Eテストの例 Cypressによる擬似コード /** ログインする **/ // メールアドレスを入力 cy.type('input[name=email]', '
[email protected]
') //
パスワードを入力 cy.type('input[name=password]', 'pass1234') // 送信ボタンをクリック cy.click('input[type=submit]') この例では CSSセレクタ で要素を特定し、 それらをクリックしたり、文字を入力したりしている
E2Eテストをリーダブルにする理由は?
E2Eテストをリーダブルにする理由は? =(脳の)メモリの無駄遣いを防ぐ E2Eテストコードは次のようなことを 想像 しながら読まないといけない 長いテストコードを最初から読みながら 今どのページにいるのか想像しながら どのボタンを押しているのか想像しながら 想像をなるべく減らす のがポイント
読みにくいUI操作の例 // 送信ボタン cy.get('button[type="submit"]').click() // 「OK 」ボタン cy.get('button.primary').click() どちらも CSSセレクタ
を用いて要素を探索しているが…… type="submit" が送信ボタンであることを知っているのは エンジニアだけ primary クラスがOKボタンに当たってるのは ただの実装上の都合 ユーザーは type="submit" や .primary のような内部的な属性値を使わず、 ラ ベル で探す 読みにくいだけでなく、ユーザー目線でもないので、誰の得にもならない
読みにくいシナリオの例 // メールアドレスを入力 cy.get('input[name="email"]').type('
[email protected]
') // パスワードを入力 cy.get('input[name="password"]').type('pass1234') // 送信ボタンをクリック cy.get('button[type="submit"]').click()
このページは ログイン ? それとも 新規登録 ?
読みにくいシナリオの例 // 新規登録ページにアクセス cy.visit('/register') // メールアドレスを入力 cy.get('input[name="email"]').type('
[email protected]
') // パスワードを入力 cy.get('input[name="password"]').type('pass1234')
// 送信ボタンをクリック cy.get('button[type="submit"]').click() 直前で「新規登録ページにアクセスした」という 文脈 が無いと読み解けない
ここまでのまとめ 想像で読む部分を減らしたい、そのために ユーザーが要素を探すときと同じ方法で要素を探したい 文脈に依存する書き方を減らしたい
どうやってリーダブルにするの?
どうやってリーダブルにするの? セマンティックな書き方を用いる ユーザーにとって意味のある書き方を用いる コンテキストを明示する 「今何をしているのか」「今どこにいるのか」を明確にする
読みにくいUI操作の例(おさらい) // 送信ボタン cy.get('button[type="submit"]').click() // 「OK 」ボタン cy.get('button.primary').click() どちらも サイトの内部構造
を用いて要素を探索しており ユーザー目線 ではない 意味のある = セマンティックな書き方 を使おう
セマンティックな書き方の例 1. 文言を用いる 2. サイトのアクセシビリティを用いる
1. 文言を用いる Cypress では文言を用いたセレクタを使える 以下の例では Sign Up という文言を含む要素をクリックする cy.contains('Sign Up').click()
※ 複数見つかった場合、一番最初に見つかった要素をクリックしてしまうので注意
文言だけでは出来ないケースはどうしたら? 例: ハンバーガーメニューのアイコン 例: あるラベルを持つ入力フォーム 例: 画像
Testing Library を使ってみよう Testing Library 要素の 役割 や ラベル などを用いてテストコードを書くためのライブラリ
// 例 getByRole("textbox", {name: / メールアドレス/})
Testing Library を使わない場合 // span タグを用いて作成した擬似的な Submit ボタン <span role="button">Submit</span>
// button タグを用いて作成した Submit ボタン <button>Submit</button> この2つは button という role と Submit という name を持つ 意味的にはほぼ等価だが テストフレームワークからは異なるセレクタを使わなければいけない cy.get('span').contain('Submit') cy.get('button').contain('Submit')
Testing Library を使う場合 Testing Library を使うとどちらも同じ形で書ける getByRole("button", {name: /Submit/})
アクセシビリティの高いサイトはテスタビリティも高い Testing Library は アクセシビリティ を用いてテストしている つまり、これら3つがシームレスに実現できる 開発者: アクセシビリティ改善 QA:
アクセシビリティ特性を用いたユーザー目線でのE2Eテスト ユーザー: アクセシビリティの利用 テスターにとってのアクセシビリティの優先度は実は高い テストしにくいサイトがあったときに、 「テストしやすく」ではなく「アクセスしやすく」という提案が出来るかも
どうやって使うの? Testing Library は Cypress, Puppeteer, TestCafe, PlayWrightなど主要なテストフレ ームワークに対応 簡単に試したいならChrome拡張
Testing Playground を使おう
コンテキストを明示する
コンテキストを明示する 読みにくいシナリオの例(おさらい) // メールアドレスを入力 cy.get('input[name="email"]').type('
[email protected]
') // パスワードを入力 cy.get('input[name="password"]').type('pass1234') // 送信ボタンをクリック
cy.get('button[type="submit"]').click() このページは ログイン ? それとも 新規登録 ?
コンテキストを明示する手法 1. Page Object 2. Context Enclosure
Page Object Pattern の利用 ページ内のロケーター、ページ特有の操作などをオブジェクトにまとめるテクニック 本来はメンテナンス性向上のための技だが、副次的にコンテキストを明示することも 出来る const loginPage =
new LoginPage() loginPage.getEmailInput().type('
[email protected]
') loginPage.getPasswordInput().type('pass1234') loginPage.getSubmitButton().click() どのUI要素も loginPage という Page Object のインスタンスから生えている = ログインページ内の要素であることが明示的に示されている
Page Object の実装例 class LoginPage { getEmailInput() { return cy.get('input[name="email"]')
} getPasswordInput() { return cy.get('input[name="password"]') } getSubmitButton() { return cy.get('button[type="submit"]') } } ログインページ内の要素をあらかじめ PageObject 内に定義する
Page Object は結構手間がかかる 例えば、ログインページに Remember me? という チェックボックスを追加したが、Page Objectには追加していないとする const
loginPage = new LoginPage() loginPage.getEmailInput().type('
[email protected]
') loginPage.getPasswordInput().type('pass1234') cy.contains('Remember me?').check() // ここだけ loginPage に属してないように見える loginPage.getSubmitButton().click() 要素を追加した際、かならず Page Object に要素を登録する必要がある Page Object はコンテキストを明示する目的に対しては 重い アプローチ
Context Enclosure 現在のコンテキストに応じてスクリプトの一部を囲う Page Objectよりも「コンテキストを明示する」という意図が明確になる cy.visit("https://demo.realworld.io/#/register"); // 新規登録ページに遷移 // この部分が
Context Enclosure cy.onRegisterPage(cy => { cy.findByPlaceholderText("Username").type("foobar") cy.findByPlaceholderText("Email").type("
[email protected]
") cy.findByPlaceholderText("Password").type("Pass1234") } ※ 名前は先日考えたのでググってもろくなのがでてきません 実装のサンプルは https://zenn.dev/tsuemura/articles/13b0ea44c1a20a
Context Enclosure の実装方法 Cypress.Commands.add("onRegisterPage", (fn) => { fn(cy); }); 1ページにつき3行で実装でき軽量
Cypressの場合はカスタムコマンドで実装する
コンテキスト内でのみ利用できるコマンド Cypress.Commands.add("onRegisterPage", (fn) => { Cypress.Command.Add("showMessage", (message) => { //
独自コマンドの定義 cy.log(message) }) cy.url().should('include', 'register') // register ページにいることを確認 fn(cy); }); onRegisterPage の中でだけ利用できる showMessage というコマンドを定義し た 例えば login や fillCredentials のようなhelperを定義してあげるとテストコ ード記述が楽になる 同時に onRegisterPage が呼ばれた段階で register を含むURLにいることを確 認している
Context Enclosure の利点 最低限の実装であれば各ページ3行ぐらいで済むので楽 全てのページに実装するのもそう大変ではない 「あるコンテキストにいる」という検証をセットで実装できる コンテキストに応じて独自のコマンドを実装できる Context Enclosure の欠点
こないだ考えたばっかりなのであんまり枯れたアイディアではないこと
今日ほとんど Context Enclosure の話をしに来たんで フィードバックもらえると助かります
まとめ
まとめ 悩まずにテストコードを読み書きするためにリーダビリティに気を使う そのためにセマンティクス(≒ アクセシビリティ)とコンテキストの2つを紹介した ユーザー目線で、文脈が明確なテストコードを書こう
Enjoy Testing! スライドか?欲しけりゃくれてやるぜ…… 探してみろ 今日の発表資料の全てをSpeakerdeckに置いてきた (Twitter / Zoomのチャットとかでも共有されると思います)