Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

60分で学ぶE2Eテスト(実装編)

 60分で学ぶE2Eテスト(実装編)

tsuemura

March 10, 2022
Tweet

More Decks by tsuemura

Other Decks in Programming

Transcript

  1. テストケース 1. 非会員で予約 2. 会員登録→ 予約→ ログアウト 3. プレミアム会員でログイン→ 予約→

    ログアウト 4. 一般会員でログイン→ 予約→ ログアウト 5. 一般会員の画面にプレミアム会員限定プランが表示されないこと 6. 非会員の画面に一般・プレミアム会員限定プランが表示されないこと
  2. 非会員で予約するシナリオの手順 (1/2) 1. https://hotel.testplanisphere.dev/ja/ を開く 2. メニューから「宿泊予約」を選択 3. 宿泊プラン一覧から「お得な特典付きプラン」の「このプランで予約」を選 択

    4. 宿泊日を翌月1 日に設定 5. 宿泊数を7 泊に設定 6. 人数を2 に設定 7. 朝食バイキング、昼からチェックインプラン、お得な観光プランを選択 8. 氏名に「テスト太郎」を入力
  3. 非会員で予約するシナリオの手順 (2/2) 9. 確認のご連絡をメールに設定 10. メールアドレスに[email protected] を設定 11. ご要望・ご連絡事項に「テスト」と入力 12.

    予約内容を確認するボタンを選択 13. 宿泊予約確認画面で、以下を確認 i. 合計金額が121,000 円であること ii. 期間、人数、追加プラン、お名前、確認のご連絡、ご要望・ご連絡が 入力通りになっていること 14. この内容で予約するボタンを選択し、以下を確認 i. 予約が完了しましたダイアログが表示されること
  4. テストコードを書いてみよう cypress/integration/smoke_test.js を作成 describe(' スモークテスト', () => { it(' 非会員で予約',

    () => { // ここにテストコードを書く }) }) describe 〜 it は「何をテストするのか」を書く部分
  5. 設計したテスト手順をそのままコメントとして書いちゃえ describe(' スモークテスト', () => { it(' 非会員で予約', () =>

    { // 1. https://hotel.testplanisphere.dev/ja/ を開く // 2. メニューから「宿泊予約」を選択 // 3. 宿泊プラン一覧から「お得な特典付きプラン」の「このプランで予約」を選択 // 4. 宿泊日を翌月1 日に設定 // 5. 宿泊数を7 泊に設定 // 6. 人数を2 に設定 // 7. 朝食バイキング、昼からチェックインプラン、お得な観光プランを選択 // 8. 氏名に「テスト太郎」を入力 // 9. 確認のご連絡をメールに設定 // 10. メールアドレスに[email protected] を設定 // 11. ご要望・ご連絡事項に「テスト」と入力 // 12. 予約内容を確認するボタンを選択 // 13. 宿泊予約確認画面で、以下を確認 // 1. 合計金額が123,000 円であること // 2. 期間、人数、追加プラン、お名前、確認のご連絡、ご要望・ご連絡が入力通りになっていること // 14. この内容で予約するボタンを選択し、以下を確認 // 1. 予約が完了しましたダイアログが表示されること }) })
  6. テストコードを書いてみよう テスト対象のサイトにアクセス describe(' スモークテスト', () => { it(' 非会員で予約', ()

    => { // 1. https://hotel.testplanisphere.dev/ja/ を開く cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); }) }) コマンドは(一部の例外を除き) cy から始まる cy.visit() は指定したURL に移動するコマンド
  7. テストコードを書いてみよう describe(' スモークテスト', () => { it(' 非会員で予約', () =>

    { // テスト対象のサイトにアクセス cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); // 2. メニューから「宿泊予約」を選択 ← イマココ cy.▪▪▪▪▪▪.click() }) }) クリックは click() でOK 宿泊予約、というリンクを どうやって指定する?
  8. 現在のテストコード describe(' スモークテスト', () => { it(' 非会員で予約', () =>

    { // テスト対象のサイトにアクセス cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); // 2. メニューから「宿泊予約」を選択 cy.contain(' 宿泊予約').click() }) })
  9. 続けて書いていきましょう 宿泊プランの選択 describe(' スモークテスト', () => { it(' 非会員で予約', ()

    => { // テスト対象のサイトにアクセス cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); // 2. メニューから「宿泊予約」を選択 cy.contain(' 宿泊予約').click() // 3. 宿泊プラン一覧から「お得な特典付きプラン」の「このプランで予約」を選択 ← イマココ }) })
  10. 現在のテストコード describe(' スモークテスト', () => { it(' 非会員で予約', () =>

    { // 1. https://hotel.testplanisphere.dev/ja/ を開く cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); // 2. メニューから「宿泊予約」を選択 cy.contain(' 宿泊予約').click() // 3. 宿泊プラン一覧から「お得な特典付きプラン」の「このプランで予約」を選択 cy.contains('div.card-body', ' お得な特典付きプラン') .contains(' このプランで予約').click() }) })
  11. カスタムコマンドを追加する cypress/support/commands.js に以下を追加する Cypress.Commands.add("getCardByText", (text) => { const selector =

    'div.card-body' cy.contains(selector, text) }); こう書けるようになった // before cy.contains('div.card-body', ' お得な特典付きプラン') .contains(' このプランで予約').click() // after cy.getCardByText(' お得な特典付きプラン').contains(' このプランで予約').click()
  12. 新たなカスタムコマンドを定義しよう 予約プランを開く カスタムコマンドを定義する Cypress.Commands.add("openReservationPlan", (planName) => { const buttonText =

    " このプランで予約" cy .getCardByText(planName) .contains(buttonText) .invoke("removeAttr", "target") .click() }) テストコードはこう書ける // before cy.getCardByText(' お得な特典付きプラン').contains(' このプランで予約').click() // after cy.openReservationPlan(' お得な特典付きプラン')
  13. 続けて書いていきましょう 4. 宿泊日を翌月1 日に設定 5. 宿泊数を7 泊に設定 6. 人数を2 に設定

    7. 朝食バイキング、昼からチェックインプラン、お得な観光プランを選択 8. 氏名に「テスト太郎」を入力 9. 確認のご連絡をメールに設定 10. メールアドレスに[email protected] を設定 11. ご要望・ご連絡事項に「テスト」と入力 12. 予約内容を確認するボタンを選択
  14. HTML のフォームの仕組みについておさらい <label for="name"> お名前</label> <input id="name" type="text" /> label

    と input で出来ていることが多い label に for 属性を付けると label と input が紐付けられる label をクリックすると input にフォーカスが移る
  15. Cypress ではどう扱われるか <label for="name"> お名前</label> <input id="name" type="text" /> //

    label が返ってくる cy.contains(" お名前") contains で取得できる要素は厳密には label 要素なので フォームに対する操作の場合、 contains では上手く動かない場合がある 普通の入力フォームへの入力はOK セレクトボックスやチェックボックスはNG Clickable な要素として扱われない
  16. 宿泊予約 cy.getByLabel(' 宿泊日').type('2022-02-12') cy.getByLabel(' 宿泊数').type('7') cy.getByLabel(' 人数').type('1') cy.getByLabel(' 朝食バイキング').check() cy.getByLabel('

    氏名').type(' ジャスト 太郎') cy.getByLabel(' 確認のご連絡').select(' 希望しない') cy.contains(' 予約内容を確認する').click()
  17. これもカスタムコマンドにしてしまえ 値を一度削除してから入力する fill メソッドを定義する Cypress.Commands.add("fill", { prevSubject: 'element' }, (subject,

    text) => { subject.clear(); subject.type(text) }) テストコードはこうなる cy.getByLabel(' 宿泊日').fill('2022/02/21{esc}')
  18. 宿泊日を翌月 1 日に設定 日付処理をする dayjs というライブラリを使う $ npm install dayjs

    describe(" スモークテスト", () => { const dayjs = require("dayjs"); const checkInDate = dayjs().add(1, "month").startOf("month"); it(" 会員登録して予約してログアウト", () => { // ... // 4. 宿泊日を翌月1 日に設定 cy.getByLabel(" 宿泊日").fill(`${checkInDate.format("YYYY/MM/DD")}{esc}`);
  19. この日付が表す意味を表現する context はテストコードに「文脈」を与える describe(" スモークテスト", () => { context(" 翌月1

    日から7 日間予約する", () => { const dayjs = require("dayjs"); const checkInDate = dayjs().add(1, "month").startOf("month"); const checkOutDate = checkInDate.add(7, "day"); it(" 会員登録して予約してログアウト", () => {
  20. 現在のテストコード describe(" スモークテスト", () => { context(" 翌月1 日から7 日間予約する",

    () => { const dayjs = require("dayjs"); const checkInDate = dayjs().add(1, "month").startOf("month"); const checkOutDate = checkInDate.add(7, "day"); it(" 会員登録して予約してログアウト", () => { // 1. https://hotel.testplanisphere.dev/ja/ を開く cy.visit("https://hotel.testplanisphere.dev/ja/index.html"); // 2. メニューから「宿泊予約」を選択 cy.contains(" 宿泊予約").click(); // 3. 宿泊プラン一覧から「お得な特典付きプラン」の「このプランで予約」を選択 cy.openReservationPlan(" お得な特典付きプラン"); cy.wait(1000); // 4. 宿泊日を翌月1 日に設定 cy.getByLabel(" 宿泊日").fill(`${checkInDate.format("YYYY/MM/DD")}{esc}`); // 5. 宿泊数を7 泊に設定 cy.getByLabel(" 宿泊数").fill("7"); // 6. 人数を2 に設定 cy.getByLabel(" 人数").fill("2");
  21. // 7. 朝食バイキング、昼からチェックインプラン、お得な観光プランを選択 cy.getByLabel(" 朝食バイキング").check(); cy.getByLabel(" 昼からチェックインプラン").check(); cy.getByLabel(" お得な観光プラン").check(); //

    8. 氏名に「テスト太郎」を入力 cy.getByLabel(" 氏名").fill(" テスト 太郎"); // 9. 確認のご連絡をメールに設定 cy.getByLabel(" 確認のご連絡").select(" メールでのご連絡"); // 10. メールアドレスに[email protected] を設定 cy.getByLabel(" メールアドレス").fill("[email protected]"); // 11. ご要望・ご連絡事項に「テスト」と入力 cy.getByLabel(" ご要望・ご連絡事項等ありましたらご記入ください").fill( " テスト" ); // 12. 予約内容を確認するボタンを選択 cy.contains(" 予約内容を確認する").click(); }); }); });
  22. テストコード // 13. 宿泊予約確認画面で、以下を確認 // 1. 合計金額が123,000 円であること // 2.

    期間、人数、追加プラン、お名前、確認のご連絡、ご要望・ご連絡が入力通りになっていること cy.contains(" 合計").should("contain", "123,000 円"); cy.contains(" お得な特典付きプラン"); cy.contains(" 期間") .next() .should( "contain", `${checkInDate.format("YYYY 年M 月D 日")} 〜 ${checkOutDate.format("YYYY 年M 月D 日")} 7 泊` ); cy.contains(" 人数").next().should("contain", "2 名様"); cy.contains(" 追加プラン").next().should("contain", " 朝食バイキング"); cy.contains(" 追加プラン").next().should("contain", " 昼からチェックインプラン"); cy.contains(" お名前").next().should("contain", " テスト 太郎様"); cy.contains(" 追加プラン").next().should("contain", " お得な観光プラン"); cy.contains(" お名前").next().should("contain", " テスト 太郎様"); cy.contains(" 確認のご連絡")next().should("contain", " メール:[email protected]"); cy.contains(" ご要望・ご連絡事項等").next().should("contain", " テスト"); // 14. この内容で予約するボタンを選択し、以下を確認 // 1. 予約が完了しましたダイアログが表示されること cy.contains(" この内容で予約する").click(); cy.wait(2000); cy.contains(" 予約を完了しました");
  23. おさらい : わかりやすいテストコードを書くコツ 1. ユーザー目線の表記を心がける サイトの内部構造を使わず、表示されたテキストで選択する 2. あいまいな部分を減らす 「xx の中のyy

    」というように指定して、要素探索の範囲を絞り込む 3. 「何をテストしているのか」と「どうテストするのか」を分ける テストコードから不要な情報を出来るだけ省いて シンプルなコードを保つ