12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Playwrightを用いたE2Eテストの自動化スクリプトを書く際に気をつけたこと

Last updated at Posted at 2024-11-01

こんにちは、最近Playwrightを使ってE2Eテストの自動化をやっている者です。
近年、Playwrightの注目度は右肩上がりで伸びていて、Seleniumの人気に追いつかんばかりですね。
スクリーンショット 2024-10-30 14.41.49.png
画像:GoogleTrendsより

今回は、壊れにくいE2Eテストのコードを書くために気をつけたことを紹介していきます。
また取り扱う内容はテストの実行時間を短くする上でも重要ですので、ぜひ取り入れてみてください。

タグの指定にデベロッパモードで取得したXPathを使わない

例えばこんなHTMLがあるとします。名前のinputタグをセレクトしたい時、あなたならどうしますか?

<html>
<body>
    <form>
        <div>
            <label for="username">名前</label>
            <input type="text">
        </div>
        <div>
            <label for="memberId">社員番号</label>
            <input type="text">
        </div>
    </form>
</body>
</html>

普段、特に気にせずタグを指定するときには、XPathを使ってこのように書いています。

const input = await page.locator('xpath=/html/body/form/div[1]/input')

ブラウザのデベロッパーツールを使うと要素のXPathを簡単に取得できるので、このように書くことが多いです。
しかしながら、今回は壊れにくいテストを作ることが目標です。このようにXPathを指定するとページに変更があった際に、locatorで失敗することが容易に想像できます。

ですので、私はこのように書きました。

const input = await page
                .locator('label:text("名前")')
                .locator("..")
                .locator("input");

名前というテキストのあるラベルの兄弟関係のinputタグをセレクトしています。
これで少しは壊れにくくなったかなと思います。

テストが並列で実行されても問題ないように、各テストを独立させる

テストは並列で実行されてもいいように各テストが独立して動作するようになっている必要があります。
例えば、組織一覧画面で組織の登録・削除を行うテストがあるとします。
最初は、組織を3個作成して、それを消すのに削除ボタンを3回押すという簡単な実装にしていました。

for (let i = 1; i < 4; i++) {
    await page.getByText("削除").first().click();
    await page.getByRole("button", { name: "確定" }).click();
}

しかし、テストが増えるにつれ、他のテストでも組織の作成が行われるようになりました。
そうすると、他のテストと並列で実行されたときに、誤って組織が削除されたり、完全に削除しきれないことがありました。

そのため、削除する時には組織名を指定して削除するボタンをクリックするようにしました。

const divisions = ["事業部A", "事業部B", "事業部C"];
for (const division of divisions) {
    await page.getByText(division)
              .locator("..")
              .getByText("削除").click();

    await page.getByRole("button", { name: "確定" }).click();
}

他のテストでも同様に、あるテストが他のテストの影響を受けないように、検索結果にフィルターをかけて絞り込みを行なったり、テスト間で同じ組織の使い回しをしないようにするなどを心がけました。

不用意にwaitForTimeoutを使わない

テストの実行時間が伸びる理由に、無駄にwaitForTimeoutを使っているというのがあげられます。なんとなく、マシンの読み込み時間を設けたほうがいいような気がして、適当な時間のwaitForTimeoutを設定してしまうのは、よくあります。

//こちらではなく
await page.waitForTimeout(300);
//こっちを使う
await page.waitForSelector("特定の要素セレクタ");

waitForTimeoutではなくwaitForSelectorを使うことで、セレクトした要素が表示されるまで待機するので、待つ時間を最適化することができます。

タイムアウトを設定する

クリックする要素が見つからなかったときなど、テストが失敗するまでの時間はデフォルトで30秒かかります。そんなに長い間待っていられないので、setDefaultTimeoutを5秒に設定しています。setDefaultTimeoutは、そのコンテキスト内でのすべての操作に対して、デフォルトのタイムアウトを設定するものです。また、時間がかかるテストはsetTimeoutで最大実行時間を設定しています。test.setTimeoutはテスト全体が完了するまでのタイムアウトを設定するものです。

test.beforeEach(async ({ context }) => {
    context.setDefaultTimeout(5000);
});

test("時間がかかるテスト", async ({ page }) => {
    test.setTimeout(240000); // 240秒に設定
    //時間がかかる処理
});

また、コンフィグで設定するのも一つの方法です。

playwright.config.ts
import { defineConfig } from '@playwright/test';  
 
export default defineConfig({  
 timeout: 4 * 60 * 1000,
});

まとめ

以上をまとめると私が気をつけたのは以下の4つになります。

  • タグの指定に安易にXPathを使わない
  • 各テストが独立しているようにすること
  • waitForTimeoutを多用しないこと
  • デフォルトのタイムアウトとテスト全体が完了するまでのタイムアウトを設定すること

より良い方法があるという方はぜひコメントしてください。

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?