Slide 1

Slide 1 text

E2Eテストのシナリオと抽象化の粒度の話 Takuya Suemura @ Autify, Inc. 2024-9-19 東京ソフトウェア QA ミートアップ

Slide 2

Slide 2 text

自己紹介 末村 拓也 X: @tsueeemura Quality Evangelist at Autify, Inc. 開発からマーケティングまで全部やる方のフルスタ ックエンジニア テトリスなどシンプルなパズルゲームが好き 最近はずっとツムツムをやっている

Slide 3

Slide 3 text

よくある疑問 E2Eテストを書く中で良く出る疑問が ・どのくらいの粒度でシナリオを書いたらいいのか ・どのくらいの粒度で抽象化(共通化)したらいいのか です 今日はその話をしたいと思います

Slide 4

Slide 4 text

シナリオの粒度

Slide 5

Slide 5 text

シナリオの表現 シナリオは必ずベースとなる観点があります どんな粒度のものをテストしたいのかで 出来上がるテストシナリオの形も変わります 「シナリオ」と言っちゃってるのでシナリオテストが多いのかな

Slide 6

Slide 6 text

混ぜるな危険 異なるレベルの観点を混ぜてはいけません ビジネスロジックのテストは単体・結合レベル シナリオレベルのテストは E2Eレベル 単体・結合テストはプログラムを「分けてテストする」技術でもある E2Eテストは分けないので原理上全てをテスト可能になるかもしれないが 何でも出来るからと言って何でも押し込んではいけない

Slide 7

Slide 7 text

単体テスト・結合テストの観点はプログラマー視点 このコードは正しく実装されているだろうか?

Slide 8

Slide 8 text

E2Eテストの観点はユーザー視点 ユーザーは期待した通りにシステムを使えるだろうか? ユーザーはバリデーションの詳細を知りたいだろうか? ユーザーにとって役に立つレベルのテストを E2Eで書く ユーザーに使い方を説明する粒度のものがテストとしては最適

Slide 9

Slide 9 text

抽象化(共通化)の話

Slide 10

Slide 10 text

共通化 手順や画面などいろんなものが繰り返し書かれる 共通化したい気持ちが出てくる

Slide 11

Slide 11 text

共通化と抽象化 操作手順として考えると共通化に寄ってしまうが、本質はテスト対象の抽象化 共通化は結果的に起きるかもしれないが、目的ではない 再利用とかはあまり考えない テスト対象の操作や要素探索といった手順をどのように状況や状態の表現に抽象化するのか が肝 「似たような手順があるから共通化して再利用しよう」は本質を見ていないのでだいたい失 敗する

Slide 12

Slide 12 text

少しずつ抽象化する 早すぎる最適化は NG 最短距離で最適解にたどり着ける人はいないので、まずは作ってみて少しずつ変えていくの が良いやり方

Slide 13

Slide 13 text

何を抽象化するのか? 操作対象の画面 操作の手順 今いるページ 前提条件

Slide 14

Slide 14 text

ページオブジェクトモデル 操作対象のページ あるページにいるというコンテキスト ページ内の要素を選択するロケーター ページ内でのみ利用できる手順

Slide 15

Slide 15 text

POMの例 import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; /** * Page Object encapsulates the Sign-in page. */ public class SignInPage { protected WebDriver driver; // private By usernameBy = By.name("user_name"); // private By passwordBy = By.name("password"); // private By signinBy = By.name("sign_in"); public SignInPage(WebDriver driver){ this.driver = driver; if (!driver.getTitle().equals("Sign In Page")) { throw new IllegalStateException("This is not Sign In Page," + " current page is: " + driver.getCurrentUrl()); } }

Slide 16

Slide 16 text

POMの例 /** * Login as valid user * * @param userName * @param password * @return HomePage object */ public HomePage loginValidUser(String userName, String password) { driver.findElement(usernameBy).sendKeys(userName); driver.findElement(passwordBy).sendKeys(password); driver.findElement(signinBy).click(); return new HomePage(driver); } }

Slide 17

Slide 17 text

POMの欠点 様々な観点が入り混じっており、個人的には使いにくいと感じた 特にロケーターは当時の技術的制約やテスタビリティの低さに依存するものが大きい 全てのページに POMを作らないといけないのもちょっと冗長

Slide 18

Slide 18 text

コンテキストベースの抽象化 いま〜〜にいる いま〜〜でログインしている

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

前提条件 前提条件を手続きで書きがち 前提条件は「〜を持っている」 「〜である」という状態

Slide 22

Slide 22 text

状態ベースの抽象化 状態の抽象化 前提条件(〜を持っている) state.haveItem のように書いておけば IDEで中身を見られるようになる 状態は全てここに入れるようにする

Slide 23

Slide 23 text

ロケーター ロケーター UIコンポーネントをどうやって探すのかの技術

Slide 24

Slide 24 text

要素探索の抽象化 →セマンティクスの抽象化 基本はユーザーにとって意味のある(セマンティックな)ロケーターを用いる 難しい場合は、特定のコンポーネントの探し方ではなく、セマンティクスを抽象化すると良 い UIコンポーネントはたいてい標準化されている(悪くてもコピペされている)ので、小回り がきく

Slide 25

Slide 25 text

まとめ E2Eテストはユーザー視点の粒度にしましょう 開発者視点とユーザー視点の違いを理解しましょう 操作手順を共通化するのではなくユーザーとテスト対象を抽象化しましょう

Slide 26

Slide 26 text

おしまい Enjoy Testing!