PlaywrightとChatGPTで、過去に作った【テスト自動化プログラム】を改良する

## 目次
- [環境情報](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E7%92%B0%E5%A2%83%E6%83%85%E5%A0%B1)
- [概要](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E6%A6%82%E8%A6%81)
- [ChatGPTのファイル出力依頼](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#chatgpt%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B%E4%BE%9D%E9%A0%BC)
    - [依頼文（最終版）](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E4%BE%9D%E9%A0%BC%E6%96%87%E6%9C%80%E7%B5%82%E7%89%88)
    - [最終版作成までの道のり](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E6%9C%80%E7%B5%82%E7%89%88%E4%BD%9C%E6%88%90%E3%81%BE%E3%81%A7%E3%81%AE%E9%81%93%E3%81%AE%E3%82%8A)
    - [他機能・サイトでの応用](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E4%BB%96%E6%A9%9F%E8%83%BD%E3%82%B5%E3%82%A4%E3%83%88%E3%81%A7%E3%81%AE%E5%BF%9C%E7%94%A8)
- [出力ファイル](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E5%87%BA%E5%8A%9B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)
- [プログラム](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0)
- [学び](https://qiita.com/Chiisan87/items/7f832113f9e0363c6ad1#%E5%AD%A6%E3%81%B3)

## 環境情報
- OS
    - Windows11
- 言語
    - Python3.13.3
- ライブラリ
    - playwright==1.51.0
    - openpyxl==3.1.5 
<br>

- ChatGPTモデル
    - GPT-4o
- 使用サイト
    - [HOTEL PLANISPHERE](https://hotel-example-site.takeyaqa.dev/ja/index.html)
    ※テスト自動化の学習用練習サイト

## 概要
以下記事で作成したプログラムの改良版
- [プログラミング初心者のQAエンジニアがExcelからテスト実行するプログラムを作ってみた](https://qiita.com/Chiisan87/items/d447f72af485971f95e0)
<br>

改良点は以下
- 実行速度
    - SeleniumからPlaywrightに変更で、実行時間削減
    - 体感として、実行時間は半分以下になった
- テスト作成工数
    - ChatGPTにテスト観点考案・テスト手順のexcelファイル出力を任せることで、手動対応時間削減

## ChatGPTのファイル出力依頼
### 依頼文（最終版）
以下文でChatGPTに依頼すると、今回のプログラムで使用するexcelファイルが出力される

<details><summary>依頼文</summary>
あなたはテストエンジニアです。
以下の手順に従い、最終成果物として対応済みのExcelファイルを提出してください。  

1.ログイン機能のテストケースを網羅的に作成する。

2.各テストケースごとにExcelのシートを作成する。 

3.シート名は内容が分かる20文字以内とする。

4.各シートには、以下のフォーマットでB2セルからテスト手順を記載する： 
手順No.|手順|Path|入力値|期待値|操作|結果
例： 
1|メールアドレス入力|//*[@id="email"]|ichiro@example.com||入力| 
2|パスワード入力|//*[@id="password"]|pass1234||入力| 
3|ログインボタン押下|//*[@id="login-button"]|||押下| 
4|メールアドレスの比較|//*[@id="email"]||ichiro@example.com|比較| 

操作の種類と使い分け： 
入力：テキストボックスに入力値を入力する 
押下：ボタンを押す 
比較：画面上の値を期待値と比較する  

5.入力値なしの入力操作を含む手順は削除する  

6.正常ログイン時の入力値は以下とし、該当箇所を更新する： 
メールアドレス：ichiro@example.com
パスワード：password 

7.各手順に使用するXPathを以下で統一する： 
メールアドレス入力：//*[@id="email"] 
パスワード入力：//*[@id="password"] 
ログインボタン：//*[@id="login-button"] 
メールアドレス比較：//*[@id="email"] 
メールアドレスエラーメッセージ比較：//*[@id="email-message"] 
パスワードエラーメッセージ比較：//*[@id="password-message"]  

8.エラー発生時の期待値は以下とする： 
未登録のメールアドレス：メールアドレスまたはパスワードが違います。 
メールアドレス形式誤り：メールアドレスを入力してください。
メールアドレス未入力：このフィールドを入力してください。 
不正なパスワード：メールアドレスまたはパスワードが違います。 
パスワード未入力：このフィールドを入力してください。

9.未登録メールアドレスまたは不正パスワードの場合は、既存の比較手順を削除し、//*[@id="email-message"] および //*[@id="password-message"] の比較手順を追加する。

10.正常ログイン時は、最後にログアウト操作（//*[@id="logout-form"]/button）を追加する。
</details>

### 最終版作成までの道のり
<details><summary>道のり</summary>
1.以下のように個別で質問して、ファイル出力まで誘導

![スクリーンショット 2025-05-11 171543.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842711/4d0ad736-c964-480e-befe-5fae4470ef3b.png)
![スクリーンショット 2025-05-11 171720.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842711/912b94fe-f349-496e-89fa-97d302ec2719.png)

2.思い通りのファイルが出力されたタイミングで、個別の質問をまとめて投稿する形に変更
![スクリーンショット 2025-05-11 172423.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842711/cd96fb2a-1213-49df-9b99-ce96ddcdb4b1.png)

3.ChatGPTにプロンプトの修正を依頼
![スクリーンショット 2025-05-11 172701.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842711/ba60ecd3-4599-41e8-8d1f-cdf02c0d3402.png)
</details>

### 他機能・サイトでの応用
まだ試していないが、以下変更で他機能やサイトでも応用できると考えている。（今後確認予定）
- 手順1で対象機能を変更する
- 手順6以降で対象サイトに合った情報を入力する
    - 以下2つの情報を変更
        - xpath
        - IDやPassword

## 出力ファイル
![スクリーンショット 2025-05-11 191056.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842711/190af45b-8291-4e9e-b98d-87f70516ff36.png)

構成は以下
- シート名
    - ログイン観点
- シート内の内容
    - 手順No.
    - 手順
        - 手順の具体説明の記載列
    - Path
        - 該当手順をテスト自動実行する際に使用するxpathの記載列
        - コードの以下部分で使用する値が記載される
        ```
        try:
            if action_type == "押下":
                click_button(page, xpath)
            elif action_type == "入力":
                input_value(page, xpath, value)
            elif action_type == "比較":
                compare_value(page, xpath, expect)
        ```
    - 入力値
        - 操作：入力の際に使用するテキストの記載列
        - コードの以下部分で使用する値が記載される
        ```
            elif action_type == "入力":
                input_value(page, xpath, value)
        ```
    - 期待値
        - 操作：比較の際に使用するテキストの記載列
        - コードの以下部分で使用する値が記載される
        ```
            elif action_type == "比較":
                compare_value(page, xpath, expect)
        ``` 
    - 操作
        - テスト自動実行で実行してほしい操作の記載列
        - 現在は「押下」「入力」「比較」のみ
    - 結果
        - 各手順の自動実行結果がOKかNGか記載される列
        - コードの以下部分で使用する値が記載される
        ```
        except:
            print(traceback.format_exc())
            result_list.append('NG')
        else:
            result_list.append('OK')
        ```



## プログラム
```test_automation.py
from playwright.sync_api import sync_playwright
import openpyxl as excel
import traceback

# 押下の定義
def click_button(page, xpath):
    page.wait_for_selector(xpath, state="visible", timeout=5000)
    page.click(xpath)

# 入力の定義
def input_value(page, xpath, value):
    page.wait_for_selector(xpath, state="visible", timeout=5000)
    page.fill(xpath, value)

# 比較の定義
def compare_value(page, xpath, expect):
    page.wait_for_selector(xpath, state="visible", timeout=5000)
    raw_text = page.locator(xpath).inner_text()
    assert raw_text == expect

# テストケース実行の定義
def run_testcase(browser, sheet_name):
    context = browser.new_context()
    page = context.new_page()

    page.goto('https://hotel-example-site.takeyaqa.dev/ja/login.html')
    page.wait_for_load_state("networkidle")

    ws_case_1 = book1[sheet_name]
    testcase = []
    column_list = []
    row_list = []
    result_list = []

    # 最大行数は必要に応じて調整
    for data_number, raw_row in enumerate(ws_case_1['B2':'H100']):
        temp = {}
        if data_number == 0:
            for column_cell in raw_row:
                column_list.append(column_cell.value)
            continue

        for column_name, cell in zip(column_list, raw_row):
            temp[column_name] = cell.value

        # 空行チェック：すべての値が空ならスキップ
        if all(value is None for value in temp.values()):
            continue

        testcase.append(temp)
        # 実際のExcelの行番号（2行目開始）
        row_list.append(data_number + 2)

    for fixed_row in testcase:
        xpath = fixed_row['Path']
        value = fixed_row['入力値']
        expect = fixed_row['期待値']
        action_type = fixed_row['操作']

        try:
            if action_type == "押下":
                click_button(page, xpath)
            elif action_type == "入力":
                input_value(page, xpath, value)
            elif action_type == "比較":
                compare_value(page, xpath, expect)
        except:
            print(traceback.format_exc())
            result_list.append('NG')
        else:
            result_list.append('OK')

    # 結果をExcelに書き込み
    for row_num, result in zip(row_list, result_list):
        ws_case_1.cell(row=row_num, column=8).value = result

    context.close()

# 実行本体
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    # [ファイル名].xlsxにChatGPTで出力したファイルのpathを記載
    book1 = excel.load_workbook(r"C:[ファイル名].xlsx")
    for sheet_name in book1.sheetnames:
        run_testcase(browser, sheet_name)
    book1.save(r"C:[ファイル名].xlsx")
    book1.close()

    browser.close()

```

## 学び
- ChatGPTのテスト観点
    - 一般的なテスト観点の提案はできる
    - サイト特有のテスト観点や仕様などは細かく伝えれば、作成してくれる
        - ChatGPTに細かく伝える工数>手動作成の工数となる場合は、その部分だけ手動で作成するのはあり
        - 今回だと、以下の仕様部分（ChatGPTへの依頼文から抜粋）が上記状況に近い
        >9.未登録メールアドレスまたは不正パスワードの場合は、既存の比較手順を削除し、//*[@id="email-message"] および //*[@id="password-message"] の比較手順を追加する。
- ChatGPTのファイル出力
    - テスト自動化ひいては手動検証でも使用できるファイルの出力は可能
        - ただし、フォーマットは作成依頼時に細かく指定する必要あり
- プログラム
    - プログラムの書き方（使用するライブラリなど）で工数がかなり変わる
        - 新規のライブラリや書き方が登場したときは、とりあえず試してみる精神が重要
    - どうしても書き方が分からない場合やコードレビューはChatGPTに相談
        - プログラムの完成度が段違いになる