Slide 1

Slide 1 text

リーダブルな E2Eテストコードのための 3つの C Takuya Suemura 2024-08-08 Playwright本出版記念! Node学園 43時限目 1

Slide 2

Slide 2 text

自己紹介 末村 拓也 プロフィール写真を 37年間更新し忘れている Quality Evangelist at Autify エンジニアですが、マーケティングチームに異 動しました(今日) テトリスとキーボードが好きです 2

Slide 3

Slide 3 text

宣伝だ テスト自動化実践ガイド CodeceptJS + Playwright の組み合わせ で E2Eテストを書く テスト自動化を始める前から運用まで広 くカバー ハンズオンのためにゴミみたいなアプリ を頑張って作りました ゴミみたいなアプリに自動テストを 書かないといけない場合に便利 3

Slide 4

Slide 4 text

CodeceptJS って何 WebDriverや Playwright などをラップして単一の APIを提供する 一人称の I から始まる 自然な英文法のシンタッ クス I click something I see something 4

Slide 5

Slide 5 text

その他執筆・登壇 ORDER BY LIKES 我が名は神龍 ……どんなテストもひとつだけ自動化してやろう (Qiita, 2018) クロスブラウザテストの闇と闇と闇 (D-CUBE, 2019) テストを自動化するのをやめ、自動テストを作ろう (July Tech Festa, 2020) etc 5

Slide 6

Slide 6 text

今日話すこと E2Eテストの認知負荷の話 認知負荷を下げる 3つの C Context Capability Component これらを CodeceptJSや Playwrightでどう表現するか 説明は CodeceptJSでやりますが Playwrightでもできます(たぶん) 書籍ではハンズオンパートの最後に説明があります 3つの Cという言葉は出版後に思いついたので今回が初出です 6

Slide 7

Slide 7 text

認知負荷 (Cognitive Load) とは コードリーディングを妨げる「覚えておかないといけない」 「想像しないといけない」こと クソデカスコープ変数 クソデカ依存関数 何の意味があるのか分からない処理 7

Slide 8

Slide 8 text

E2Eテストの認知負荷 どこのページにいるのか、どのユーザーでログインしているのか いまやっているのはテストなのか準備なのか 何を操作しようとしているのか E2Eテストは何も考えずに書くとゴリゴリの手続き型プログラミングになるので こういうところをケアしながら書いてあげたい 8

Slide 9

Slide 9 text

認知負荷を下げる工夫 「あるページにいる」 「あるユーザーでログインしている」という 状況 (Context) を表 現する 「何が出来るべきなのか」という 能力 (Capability) を表現する 「何を操作しようとしているのか」という 部品 (Component) を表現する 9

Slide 10

Slide 10 text

Context 状況を表現する 10

Slide 11

Slide 11 text

Context(状況) いま、どのページにいるのか いま、どのユーザーでログインしているのか etc 11

Slide 12

Slide 12 text

ある ECサイトのテストコード I.click('購入に進む') // 商品購入画面へ遷移 I.click('決済方法を選択') '購入に進む ' をクリックすると画面遷移が期待されているが、 コードでは表されていない状態 ページ遷移に失敗すると、 決済方法を選択 要素が存在せずエラーになるが、 欲を言うと 期待したページに遷移していない エラーで失敗してほしい 12

Slide 13

Slide 13 text

Contextをコードで表現する I.click('購入に進む') I.shouldBeOnPurchasePage(I => { I.click('決済方法を選択') }) 13

Slide 14

Slide 14 text

実装はシンプル shouldBeOnPurchasePage(fn) { I.seeInUrl('/purchase') fn(I) } 14

Slide 15

Slide 15 text

その他の使い道 : ログイン状態を表現する amStoreStaff(fn) { const I = actor({}); session('StoreStaff', () => { I.amOnPage("/"); I.click("ログインする"); I.fillField("ユーザー名", "admin"); I.fillField("パスワード", "admin"); I.click("ログイン"); fn(I) }) }, amAnonimousUser(fn) { const I = actor({}); session('AnonimousUser', () => { fn(I) }) } 15

Slide 16

Slide 16 text

Capability (前提条件として)何が出来るべきか 16

Slide 17

Slide 17 text

ある ECサイトのテストデータ準備コード const itemName = `牛ハラミ弁当-テスト-${utils.now.format("YYYYMMDDHHmmss")}`; I.amOnPage('/items/add') I.seeInTitle('商品追加') I.fillField("商品名", itemName); I.fillField("商品説明", "テスト用の商品です"); I.fillField("価格", "500"); I.fillField("在庫数", "10") I.click("追加"); 17

Slide 18

Slide 18 text

I.canPurchaseItem テストデータが準備されている状態が重要なので、手順は隠蔽する I can purchase item (商品を購入できる)という前提条件として記述する const itemName = `テスト商品-${Date.now()}` I.canPurchaseItem(itemName) // 商品を購入する I.click(itemName) 18

Slide 19

Slide 19 text

canPurchaseItem の実装例 canPurchaseItem: (name) { const I = actor({}); if (!name) name = `牛ハラミ弁当-テスト-${utils.now.format("YYYYMMDDHHmmss")}`; I.amOnPage('/items/add') I.seeInTitle('商品追加') I.fillField("商品名", name); I.fillField("商品説明", "テスト用の商品です"); I.fillField("価格", "500"); I.click("追加"); I.see(name) return name; } 19

Slide 20

Slide 20 text

Component ページ内のコンポーネントを抽象化する 20

Slide 21

Slide 21 text

UIコンポーネントを抽象化する

ハムエッグの材料

卵 1個 購入 ハム 2枚 購入 const row(header) { return locate('tr').withChild( locate('th').withText(header) ) } 21

Slide 22

Slide 22 text

ハムエッグの材料

卵 1個 購入 ハム 2枚 購入 within(row('ハム'), () => { I.click('購入') }) 22

Slide 23

Slide 23 text

アンチパターン : 操作と要素探索をまとめてしまう I.purchaseHam() I.purchaseEgg() purchase ("購入 " ボタンのクリック ) と Ham/Egg (購入する商品)をまとめたメソッドを作っている 中で何をしているのかが分からなくなる 項目の数だけメソッドを定義しなければならなくなる 23

Slide 24

Slide 24 text

まとめ E2Eテストの認知負荷を下げるには "3つの C" を理解しよう Context(状況) Capability(能力≒前提条件) Component(部品) ユーザー目線で抽象化していくことで、コメントに頼らずテストコードが書ける CodeceptJSはいいぞ(今日何の会だっけ?) 24

Slide 25

Slide 25 text

Enjoy Testing! 25