「【Selenium×Python】ログインフォームのバリデーションテストを全35件自動化する方法」

テスト自動化

📌 この記事はこんな方におすすめ

  • SeleniumでWebフォームのテストを自動化したい方
  • ログインフォームのバリデーションテストを網羅的にチェックしたい方
  • 境界値・特殊文字・SQLインジェクションなどのテストケース設計を学びたい方
  • PythonでE2Eテストを実装してみたいQAエンジニアの方

✅ この記事を読むとわかること

  • Seleniumで全35件のフォームバリデーションテストを自動実行する方法
  • 全角・半角・境界値・特殊文字のテストケース設計パターン
  • FAILケースのスクリーンショット自動保存の実装方法
  • テスト結果をCSV・JSONレポートへ出力する方法

👨‍💻 筆者について

QAエンジニアとして実務でSeleniumを活用した自動テストを担当。本記事で紹介するスクリプトはGitHubで公開しており、実際に動作確認済みのコードをそのまま解説しています。

ログインフォームは多くのWebアプリケーションで最も重要な機能の一つです。バリデーションが正しく動作しない場合、ユーザーがログインできないだけでなく、SQLインジェクションや不正アクセスといった重大なセキュリティ問題に発展するリスクもあります。だからこそ、リリース前に網羅的なテストを自動化しておくことが非常に重要です。

ログインフォームのテストって、手動でやると地味に大変じゃないですか?全角文字・空文字・SQLインジェクション・絵文字まで、チェックすべき項目は山ほどあります。

この記事では、Seleniumを使ってログインフォームのバリデーションテストを全35件自動実行するPythonスクリプトを解説します。テスト結果はCSV・JSONで出力でき、FAILケースはスクリーンショットも自動保存されます。


テスト観点

今回のスクリプトでは、ログインフォームに対して以下の観点を網羅的にテストしています。QAの現場でもよく使われる定番の観点ばかりなので、そのまま自社プロダクトのテスト設計にも応用できます。

  • 空入力チェック:ユーザー名・パスワードが未入力の場合にエラーが返るか
  • 文字種チェック:全角・日本語・絵文字など、想定外の文字種を弾けるか
  • メール形式・スペース混入:フォーマット違いの入力を正しく拒否できるか
  • 境界値チェック:1文字・255文字など、長さの端点で異常が起きないか
  • セキュリティチェック:SQLインジェクション・XSSなどの攻撃文字列を無害化できるか
  • 正常ログイン:正しい認証情報でログインが成功し、リダイレクトされるか

テストケース一覧(全35件)

テストケースは4つのカテゴリで構成されています。実務でよく見落とされがちな全角文字や特殊文字系を手厚くカバーしています。

カテゴリ ID 件数 主な内容
全角文字 Z-01〜Z-08 8件 全角英字・日本語・ひらがな・カタカナ・全角記号・全角スペース
半角文字 H-01〜H-07 7件 大文字・小文字・数値のみ・スペース混入・メール形式
長さ / 境界値 L-01〜L-10 10件 空文字・1文字・2文字・3文字・50文字・255文字
特殊文字 S-01〜S-10 10件 SQLインジェクション・XSS・絵文字・改行・タブ・URLエンコード

⚠️ 仕掛けポイント:H-01(正常ログイン)は expected_error: True と意図的に間違えた期待値を設定しています。これはデモ用に FAILを発生させてスクリーンショット保存の動作を確認 するための仕掛けです。実際に使う際は False に修正しましょう。

セットアップ・実行方法

必要なライブラリのインストール

以下の2つのライブラリをインストールするだけで動作します。ChromeDriverの手動管理は不要です。

pip install selenium webdriver-manager

実行コマンド

# ブラウザを表示して実行(デフォルト)
python form_validation_test.py

# ヘッドレスモード(ブラウザ非表示・CI環境向け)
python form_validation_test.py --headless

💡 Tip:初回実行時は webdriver-manager がChromeDriverを自動ダウンロードします。2回目以降はキャッシュが使われるので高速です。

実行結果サンプル

実行するとターミナルにリアルタイムで結果が流れます。FAILケースは原因と一緒にスクリーンショットのパスも表示されます。

==============================================================
  Selenium Form Validation Test (headless)
  Target : //the-internet.herokuapp.com/login
  Cases  : 35
  Start  : 2025-07-01 10:00:00
==============================================================
  (01/35) Full-width alpha username      ... ✅ PASS (1823ms)
  (02/35) Full-width numeric password    ... ✅ PASS (1654ms)
  (03/35) Full-width space in username   ... ✅ PASS (1701ms)
  ...
  (09/35) Valid login (correct creds)    ... ❌ FAIL (2100ms)
       → expected_error:True / actual_error:False
       📸 screenshots/H-01_FAIL.png
  ...
  (35/35) URL-encoded characters         ... ✅ PASS (1590ms)

──────────────────────────────────────────────────────────────
  Test Summary
──────────────────────────────────────────────────────────────
  Total  : 35
  ✅ PASS  : 34
  ❌ FAIL  : 1
  ⚠️  ERROR : 0
  Rate   : 97.1%
──────────────────────────────────────────────────────────────

コード解説:主要ポイント3選

① JavaScript経由でフォームに値を入力する

Seleniumの send_keys() では改行(\n)やタブ(\t)などの特殊文字を正確に入力できないケースがあります。JavaScriptで直接値をセットすることで、あらゆる文字を安全に入力できます。

arguments[0] にはWebElement、arguments[1] には入力したい文字列を渡します。これにより、ブラウザのキーイベント処理をスキップしてDOM要素に直接値を書き込めます。絵文字・全角文字・制御文字のような「send_keysでは化けやすい文字」でも確実に動作するため、特殊文字系テストケース(S-05の改行・S-06のタブ)はこの方式があって初めて成立しています。

driver.execute_script("arguments[0].value = arguments[1];", u_elem, tc["username"])
driver.execute_script("arguments[0].value = arguments[1];", p_elem, tc["password"])

② HTML5バリデーションをバイパスする

空文字テスト(L-01〜L-03)はHTML5の required 属性によってブラウザ側でブロックされてしまいます。novalidate を付与することでリクエストをサーバーまで届かせ、サーバー側の挙動まで確認できます。

「ブラウザがエラーを出さない=テスト通過」ではなく、「サーバーが正しくエラーを返すか」まで確認するのがバリデーションテストの本来の目的です。フロントエンドのバリデーションを意図的に無効化することで、バックエンドの堅牢性を直接テストできます。

driver.execute_script(
    "document.querySelector('form').setAttribute('novalidate','true');"
)

③ FAILケースだけスクリーンショットを保存する

全件スクリーンショットを撮ると枚数が膨大になります。FAILのときだけ保存する設計にすることで、後から原因確認がしやすくなります。

ファイル名は {テストID}_FAIL.png の形式にしているため、どのテストケースで失敗したかがファイル名だけで一目でわかります。GitHub ActionsなどのCI/CD環境でテストを実行する場合も、このスクリーンショットをアーティファクトとして保存しておくとデバッグ効率が格段に上がります。

if r.status == "FAIL":
    os.makedirs("screenshots", exist_ok=True)
    ss = f"screenshots/{tc['id']}_FAIL.png"
    driver.save_screenshot(ss)
    r.screenshot = ss

💡 実務Tip:Page Object Model(POM)について
実務では Page Object Model(POM) を使い、テストコードとページ操作を分離する設計が推奨されています。今回のスクリプトはシンプルな構成ですが、テストケースが増えてきたら LoginPage クラスにフォーム操作をまとめることで、メンテナンス性と再利用性が大幅に向上します。

レポートをCSV・JSONで自動出力する

テスト完了後、タイムスタンプ付きのレポートファイルが自動生成されます。

📄 report_20250701_100000.csv
📄 report_20250701_100000.json
出力形式 主な用途
CSV Excelで開いてレビュー・フィルタリング・チーム共有・エビデンス提出
JSON GitHub Actions連携・Slack通知・Allureレポートへのデータ連携

さらに発展させるアイデア

⚡ 並列実行で高速化

concurrent.futures を使ってブラウザを複数起動し、テストを並列実行すれば実行時間を大幅に短縮できます。

🔄 リトライ処理の追加

ネットワークが不安定な環境でも安定して動作するよう、ERRORケースに自動リトライロジックを追加できます。

📊 レポートの可視化

JSONレポートをもとにAllureやHTMLレポートを生成すれば、チームへの共有がさらにスムーズになります。

🤖 CI/CD連携

GitHub ActionsやJenkinsに組み込んで、PRごとに自動テストを実行する仕組みにできます。

💡 活用シーン・拡張例

  • リグレッションテストの一環として毎リリース前に実行
  • 新機能リリース前のログイン機能の健全性チェック
  • ステージング環境でのセキュリティ確認(SQLi・XSS)
  • 会員登録・パスワード変更フォームへの横展開
  • テストケースをCSVから読み込む形式に変更してノーコード管理
  • pytest-htmlやAllureと組み合わせてレポートを見える化

ハマりポイント

実装中に実際につまずいた箇所をまとめました。同じところでハマる方の参考になれば嬉しいです。


① send_keys() で特殊文字が正しく入力できない

改行(\n)・タブ(\t)・絵文字などの特殊文字を send_keys() で入力すると、文字化けしたり意図しない動作が起きることがありました。特殊文字はJavaScript経由で直接DOMに書き込むことで解決しました。

# ❌ send_keys()では特殊文字が化けることがある
username_field.send_keys("test\nuser")

# ✅ JS経由で直接値をセットする
driver.execute_script("arguments[0].value = arguments[1];", username_field, "test\nuser")

💡 ポイント: arguments[0] にWebElement、arguments[1] に入力したい文字列を渡すことで、ブラウザのキーイベント処理をスキップして確実に値を書き込めます。


② HTML5の required 属性でブラウザにブロックされる

空文字テストを実行しようとしたところ、フォームの required 属性によってブラウザ側でリクエストが止まってしまい、サーバー側の挙動を確認できませんでした。

# ❌ requiredがあるとブラウザがブロックしてサーバーに届かない
submit_button.click()

# ✅ novalidateを付与してHTML5バリデーションをバイパス
driver.execute_script(
    "document.querySelector('form').setAttribute('novalidate', 'true');"
)
submit_button.click()

💡 ポイント: 「ブラウザがエラーを出さない=テスト通過」ではなく、「サーバーが正しくエラーを返すか」まで確認するのがバリデーションテストの本来の目的です。


③ 全件スクリーンショットで枚数が膨大になった

最初は全35件で毎回スクリーンショットを撮っていたところ、ファイルが大量に生成されてどれが重要なのかわからなくなりました。FAILケースだけ保存する設計に変えることで管理しやすくなりました。

# ❌ 全件保存すると枚数が膨大になる
driver.save_screenshot(f"screenshots/{tc['id']}.png")

# ✅ FAILのときだけ保存する
if result.status == "FAIL":
    os.makedirs("screenshots", exist_ok=True)
    driver.save_screenshot(f"screenshots/{tc['id']}_FAIL.png")

💡 ポイント: ファイル名を {テストID}_FAIL.png の形式にしておくと、どのテストケースで失敗したかがファイル名だけで一目でわかります。


④ 期待値の設定ミスでFAILが出続けた

正常ログインのテストケース(H-01)に expected_error: True と間違えて設定していたため、正しくログインできているのにずっとFAILと判定されていました。期待値の設定ミスはデバッグに気づきにくいので要注意です。

# ❌ 期待値が間違っていると正常動作でもFAILになる
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": True}

# ✅ 正常ログインはexpected_error: Falseが正しい
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": False}

⚠️ 注意: テストがFAILしたとき、まず「コードのバグか」「期待値の設定ミスか」を切り分けることが大事です。


⑤ エラーメッセージの取得タイミングが早すぎた

フォーム送信直後にエラーメッセージを取得しようとしたところ、まだ画面が更新されていないためエラーメッセージが見つからないことがありました。送信後の待機処理が必要でした。

# ❌ 送信直後にエラーを取得しようとすると見つからないことがある
submit_button.click()
error = driver.find_element(By.ID, "flash")

# ✅ WebDriverWaitで要素が表示されるまで待機する
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

submit_button.click()
error = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "flash"))
)

💡 ポイント: time.sleep() で固定待機するより WebDriverWait で条件待機する方が高速で安定します。

まとめ

この記事では、SeleniumでログインフォームのバリデーションテストをPythonで自動化する方法を紹介しました。

ポイント 内容
テストケース 全角・半角・境界値・特殊文字の4カテゴリ 計35件
入力方法 JS経由でどんな特殊文字も確実・安全に入力
スクリーンショット FAILケースのみ自動保存(枚数を絞って管理しやすく)
レポート出力 CSV・JSON形式でタイムスタンプ付き自動生成
実行モード –headless オプションでCI/CD環境にも対応

実務ではこれらのテストをCI/CDパイプラインに組み込み、プルリクエストのたびに自動実行することで品質を継続的に検証します。Seleniumテストは回帰テストにも広く活用されており、デプロイのたびにログイン機能が正常に動作しているかを自動で確認できます。

GitHubにソースコードを公開していますので、ぜひ自分の環境でも試してみてください!フォームの種類を変えるだけでさまざまなWebアプリのテストに応用できます。何かご質問があればコメント欄からどうぞ👇

タイトルとURLをコピーしました