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

コンテキストとセマンティクスを意識してリーダブルなE2Eテストコードを書こう

 コンテキストとセマンティクスを意識してリーダブルなE2Eテストコードを書こう

リーダブルなテストコードについて考えよう ~VeriServe Test Automation Talk No.3~ 2022-07-27 での講演スライドです。

tsuemura

July 27, 2022
Tweet

More Decks by tsuemura

Other Decks in Technology

Transcript

  1. コンテキストとセマンティクスを意識して
    リーダブルなE2Eテストコードを書こう
    Takuya Suemura @ Autify, Inc.
    リーダブルなテストコードについて考えよう ~VeriServe Test Automation Talk No.3~
    2022-07-27

    View Slide

  2. おれは誰だぜ
    末村 拓也
    開発者、フィールドエンジニア、QAなどを経て、2019年にAutifyに入社。
    自動テスト、特にWebのE2Eテストに強い。
    現職ではテクニカルサポートを担当。テスト対象とテストフレームワークの互換性の
    問題などを調査、解決する役割を担う。
    テトリスが好き。

    View Slide

  3. おれが今まで話してきたこと
    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テストの書き方とは」を
    ひたすら考えたり話したりし続けている人です

    View Slide

  4. 今日話すこと
    そもそもE2Eテストってどういうものだっけ?
    E2Eテストをリーダブルにする理由は?
    どうやってリーダブルにするの?

    View Slide

  5. そもそもE2Eテストとは

    View Slide

  6. よくあるE2Eテストのイメージ
    ブラウザとかモバイルデバイスを自動操作して
    WebアプリとかモバイルアプリのUIを
    ユーザーが操作するのと同じようにテストする

    View Slide

  7. View Slide

  8. (Webの) E2Eテストで用いられる技術
    Webブラウザの自動操作技術
    Selenium, Cypress, PlayWright etc.
    要素特定の手段
    CSS Selector, XPath etc.

    View Slide

  9. E2Eテストの例
    Cypressによる擬似コード
    /**
    ログインする **/

    //
    メールアドレスを入力

    cy.type('input[name=email]', '[email protected]')

    //
    パスワードを入力

    cy.type('input[name=password]', 'pass1234')

    //
    送信ボタンをクリック

    cy.click('input[type=submit]')

    この例では CSSセレクタ で要素を特定し、
    それらをクリックしたり、文字を入力したりしている

    View Slide

  10. E2Eテストをリーダブルにする理由は?

    View Slide

  11. E2Eテストをリーダブルにする理由は?
    =(脳の)メモリの無駄遣いを防ぐ
    E2Eテストコードは次のようなことを 想像 しながら読まないといけない
    長いテストコードを最初から読みながら
    今どのページにいるのか想像しながら
    どのボタンを押しているのか想像しながら
    想像をなるべく減らす のがポイント

    View Slide

  12. 読みにくいUI操作の例
    //
    送信ボタン

    cy.get('button[type="submit"]').click()

    //
    「OK
    」ボタン

    cy.get('button.primary').click()

    どちらも CSSセレクタ を用いて要素を探索しているが……
    type="submit"
    が送信ボタンであることを知っているのは エンジニアだけ
    primary
    クラスがOKボタンに当たってるのは ただの実装上の都合
    ユーザーは type="submit"
    や .primary
    のような内部的な属性値を使わず、 ラ
    ベル で探す
    読みにくいだけでなく、ユーザー目線でもないので、誰の得にもならない

    View Slide

  13. 読みにくいシナリオの例
    //
    メールアドレスを入力

    cy.get('input[name="email"]').type('[email protected]')

    //
    パスワードを入力

    cy.get('input[name="password"]').type('pass1234')

    //
    送信ボタンをクリック

    cy.get('button[type="submit"]').click()

    このページは ログイン ? それとも 新規登録 ?

    View Slide

  14. 読みにくいシナリオの例
    //
    新規登録ページにアクセス

    cy.visit('/register')

    //
    メールアドレスを入力

    cy.get('input[name="email"]').type('[email protected]')

    //
    パスワードを入力

    cy.get('input[name="password"]').type('pass1234')

    //
    送信ボタンをクリック

    cy.get('button[type="submit"]').click()

    直前で「新規登録ページにアクセスした」という 文脈 が無いと読み解けない

    View Slide

  15. ここまでのまとめ
    想像で読む部分を減らしたい、そのために
    ユーザーが要素を探すときと同じ方法で要素を探したい
    文脈に依存する書き方を減らしたい

    View Slide

  16. どうやってリーダブルにするの?

    View Slide

  17. どうやってリーダブルにするの?
    セマンティックな書き方を用いる
    ユーザーにとって意味のある書き方を用いる
    コンテキストを明示する
    「今何をしているのか」「今どこにいるのか」を明確にする

    View Slide

  18. 読みにくいUI操作の例(おさらい)
    //
    送信ボタン

    cy.get('button[type="submit"]').click()

    //
    「OK
    」ボタン

    cy.get('button.primary').click()

    どちらも サイトの内部構造 を用いて要素を探索しており
    ユーザー目線 ではない
    意味のある = セマンティックな書き方 を使おう

    View Slide

  19. セマンティックな書き方の例
    1. 文言を用いる
    2. サイトのアクセシビリティを用いる

    View Slide

  20. 1. 文言を用いる
    Cypress では文言を用いたセレクタを使える
    以下の例では Sign Up
    という文言を含む要素をクリックする
    cy.contains('Sign Up').click()


    複数見つかった場合、一番最初に見つかった要素をクリックしてしまうので注意

    View Slide

  21. 文言だけでは出来ないケースはどうしたら?
    例: ハンバーガーメニューのアイコン
    例: あるラベルを持つ入力フォーム
    例: 画像

    View Slide

  22. Testing Library を使ってみよう
    Testing Library
    要素の 役割 や ラベル などを用いてテストコードを書くためのライブラリ
    //


    getByRole("textbox", {name: /
    メールアドレス/})

    View Slide

  23. Testing Library を使わない場合
    // span
    タグを用いて作成した擬似的な Submit
    ボタン

    Submit

    // button
    タグを用いて作成した Submit
    ボタン

    Submit

    この2つは button
    という role と Submit
    という name
    を持つ
    意味的にはほぼ等価だが
    テストフレームワークからは異なるセレクタを使わなければいけない
    cy.get('span').contain('Submit')

    cy.get('button').contain('Submit')

    View Slide

  24. Testing Library を使う場合
    Testing Library を使うとどちらも同じ形で書ける
    getByRole("button", {name: /Submit/})

    View Slide

  25. アクセシビリティの高いサイトはテスタビリティも高い
    Testing Library は アクセシビリティ を用いてテストしている
    つまり、これら3つがシームレスに実現できる
    開発者: アクセシビリティ改善
    QA: アクセシビリティ特性を用いたユーザー目線でのE2Eテスト
    ユーザー: アクセシビリティの利用
    テスターにとってのアクセシビリティの優先度は実は高い
    テストしにくいサイトがあったときに、
    「テストしやすく」ではなく「アクセスしやすく」という提案が出来るかも

    View Slide

  26. どうやって使うの?
    Testing Library は Cypress, Puppeteer, TestCafe, PlayWrightなど主要なテストフレ
    ームワークに対応
    簡単に試したいならChrome拡張 Testing Playground を使おう

    View Slide

  27. コンテキストを明示する

    View Slide

  28. コンテキストを明示する
    読みにくいシナリオの例(おさらい)
    //
    メールアドレスを入力

    cy.get('input[name="email"]').type('[email protected]')

    //
    パスワードを入力

    cy.get('input[name="password"]').type('pass1234')

    //
    送信ボタンをクリック

    cy.get('button[type="submit"]').click()

    このページは ログイン ? それとも 新規登録 ?

    View Slide

  29. コンテキストを明示する手法
    1. Page Object
    2. Context Enclosure

    View Slide

  30. Page Object Pattern の利用
    ページ内のロケーター、ページ特有の操作などをオブジェクトにまとめるテクニック
    本来はメンテナンス性向上のための技だが、副次的にコンテキストを明示することも
    出来る
    const loginPage = new LoginPage()

    loginPage.getEmailInput().type('[email protected]')

    loginPage.getPasswordInput().type('pass1234')

    loginPage.getSubmitButton().click()

    どのUI要素も loginPage
    という Page Object のインスタンスから生えている
    = ログインページ内の要素であることが明示的に示されている

    View Slide

  31. 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 内に定義する

    View Slide

  32. 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 はコンテキストを明示する目的に対しては 重い アプローチ

    View Slide

  33. 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

    View Slide

  34. Context Enclosure の実装方法
    Cypress.Commands.add("onRegisterPage", (fn) => {

    fn(cy);

    });

    1ページにつき3行で実装でき軽量
    Cypressの場合はカスタムコマンドで実装する

    View Slide

  35. コンテキスト内でのみ利用できるコマンド
    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にいることを確
    認している

    View Slide

  36. Context Enclosure の利点
    最低限の実装であれば各ページ3行ぐらいで済むので楽
    全てのページに実装するのもそう大変ではない
    「あるコンテキストにいる」という検証をセットで実装できる
    コンテキストに応じて独自のコマンドを実装できる
    Context Enclosure の欠点
    こないだ考えたばっかりなのであんまり枯れたアイディアではないこと

    View Slide

  37. 今日ほとんど Context Enclosure の話をしに来たんで
    フィードバックもらえると助かります

    View Slide

  38. まとめ

    View Slide

  39. まとめ
    悩まずにテストコードを読み書きするためにリーダビリティに気を使う
    そのためにセマンティクス(≒
    アクセシビリティ)とコンテキストの2つを紹介した
    ユーザー目線で、文脈が明確なテストコードを書こう

    View Slide

  40. Enjoy Testing!
    スライドか?欲しけりゃくれてやるぜ……
    探してみろ 今日の発表資料の全てをSpeakerdeckに置いてきた
    (Twitter / Zoomのチャットとかでも共有されると思います)

    View Slide