【実務レベル】Selenium×pytest完全ガイド|fixture・parametrize・markを体系解説

テスト自動化

Selenium × pytest でよく使う4つの機能——fixture・parametrize・conftest.py・mark——を、サンプルコード付きで解説します。

pytest の fixture・parametrize・mark を使いこなせるようになると、Selenium テストを「単発スクリプト」から「保守しやすいテストスイート」へ改善できます。

この記事では、実務でそのまま使える構成をコード付きで紹介します。

この記事で解説する4つのポイント

  • pytest fixture:WebDriver のセットアップ・ティアダウンを共通化する
  • pytest parametrize:1つのテストを複数データで繰り返すデータ駆動テスト
  • conftest.py:fixture を全テストファイルで共有するための設定ファイル
  • pytest mark:smoke・regression など目的別にテストを分類・選択実行する

👉 前提:Selenium × Python の環境構築が完了していることを前提にしています。まだの方は 環境構築ガイド を先にご確認ください。

💡 他の記事との違い

多くの記事は「テストが動く」ところで終わりですが、この記事では実務で使える設計パターン(fixture の分離・parametrize によるデータ駆動・mark によるテスト分類)まで踏み込んで解説します。

Selenium のテストは「とりあえず動く」から「チームで使える・CIで回せる」レベルへの壁があります。その壁を超えるのが pytest の fixture・parametrize・mark の使いこなしです。

📌 この記事はこんな方におすすめ / 読むと得られること

  • Selenium × pytest の環境構築が完了してテストを書き始めた方
  • fixture・parametrize・conftest.py・mark の使い方を体系的に学びたい方
  • テストコードを整理して CI/CD で使えるレベルにしたい方
  • pytest fixture・parametrize・mark の実践的な使い方がわかる
  • conftest.py を使った WebDriver の共通化パターンがわかる
  • ログインフォームの正常系・異常系テストを実装できる

👤 この記事を書いた人

QAエンジニア・テスト自動化エンジニアとして15年以上の実務経験を持つ Yoshi が執筆。Selenium × pytest の構成は実務プロジェクトで毎日使っており、fixture・parametrize・mark のパターンをチームの標準として展開しています。実装コードはGitHubで公開中:github.com/YOSHITSUGU728/automated-testing-portfolio

Selenium × pytest の全体構成とは?

フォルダ構成

まず全体のフォルダ構成と各ファイルの役割を把握します。

selenium-project/
├── venv/
├── tests/
│   ├── conftest.py          # WebDriver fixture・共通設定
│   ├── test_login.py        # ログインテスト
│   ├── test_search.py       # 検索テスト
│   └── test_register.py     # 会員登録テスト
├── pages/
│   └── login_page.py        # Page Object(別記事で解説します)
├── requirements.txt
└── pytest.ini

各ファイルの役割

📋 各ファイルの役割

conftest.pyWebDriver の起動・終了 fixture を定義。全テストファイルから自動で読み込まれる
test_*.py機能ごとにテストを分割。1ファイル=1ユースケースが理想
pytest.iniテストパス・オプション・カスタム mark の登録
pages/Page Object パターン(要素操作をクラスに集約)

STEP 1:pytest fixture とは?conftest.py で WebDriver を共通化する

conftest.py に WebDriver の fixture を定義することで、全テストファイルで共有できます。

# tests/conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

@pytest.fixture(scope="function")
def driver():
    """テストごとにChromeを起動・終了するfixture"""
    options = webdriver.ChromeOptions()
    # options.add_argument("--headless")  # CI/CD環境ではオンにする
    options.add_argument("--no-sandbox")            # Linux/CI環境用
    options.add_argument("--disable-dev-shm-usage") # Linux/CI環境用
    options.add_argument("--window-size=1920,1080")

    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service, options=options)
    driver.implicitly_wait(10)  # 要素検索時の基本待機を設定(補助的な用途)

    yield driver

    try:
        driver.quit()
    except Exception:
        pass  # セッション切断済みでも teardown が失敗しないよう保護


@pytest.fixture(scope="function")
def logged_in_driver(driver):
    """ログイン状態が必要なテスト専用fixture。
    ログイン不要なテストには driver fixture を直接使うこと。"""
    # ※ example-app.com はサンプルURLです。実際のテスト対象URLに置き換えてください。
    driver.get("https://example-app.com/login")
    driver.find_element(By.ID, "username").send_keys("test_user")
    driver.find_element(By.ID, "password").send_keys("test_pass")
    driver.find_element(By.ID, "login-btn").click()

    # ログイン完了を待機(これがないとCI環境で不安定になる)
    WebDriverWait(driver, 10).until(
        EC.url_contains("/dashboard")
    )

    yield driver
    # teardown(driver.quit())は driver fixture 側で行うため、ここでは不要

💡 logged_in_driver の使用例

# ログイン状態が必要なテストだけ logged_in_driver を使う
def test_dashboard_title(logged_in_driver):
    """ログイン後のダッシュボードタイトルを確認"""
    assert "ダッシュボード" in logged_in_driver.title

# ログイン不要なテストは driver を直接使う
def test_top_page_title(driver):
    """トップページのタイトルを確認(ログイン不要)"""
    driver.get("https://example-app.com")
    assert "トップページ" in driver.title

💡 fixture のポイント

  • scope="function"(デフォルト):テストごとにブラウザを起動・終了。安全だが遅い
  • scope="session":テストスイート全体で1つのブラウザを使い回す。速いが状態汚染に注意
  • driver.implicitly_wait(10):全要素検索に適用される基本待機です。WebDriverWait を主軸にし、implicitly_wait は最小限にするのが一般的です。なお 完全に併用禁止ではありませんが、両方設定すると待機時間の予測が難しくなるため注意してください

⚠️ CI環境での注意点

  • scope="session" を pytest-xdist(並列実行)と組み合わせる場合、セッション共有に追加設定が必要です
  • ChromeDriverManager().install() はCI起動時に毎回バージョン確認が入るため遅くなるケースがあります。大規模CIではDockerイメージで固定バージョンを使う構成も一般的です
📖 WebDriverWait(明示的待機)の使い方を詳しく知りたい方はこちらSelenium×Python環境構築完全ガイド

STEP 2:基本的なテストの書き方(正常系・異常系)

base_url fixture を引数で受け取ることで URL を一元管理できます。定数との混在がなくなり、環境ごとの切り替えも fixture 側だけで対応できます。

# tests/test_login.py
# ※ 以降のコードでも同じ import を使用します
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# ── 正常系 ─────────────────────────────────────────
def test_login_success(driver, base_url):
    """正常系:正しいID・PWでログイン成功する"""
    driver.get(f"{base_url}/login")

    driver.find_element(By.ID, "username").send_keys("valid_user")
    driver.find_element(By.ID, "password").send_keys("valid_pass")
    driver.find_element(By.ID, "login-btn").click()

    WebDriverWait(driver, 10).until(
        EC.url_contains("/dashboard")
    )
    assert "/dashboard" in driver.current_url

# ── 異常系 ─────────────────────────────────────────
def test_login_failure_wrong_password(driver, base_url):
    """異常系:パスワードが間違っている場合にエラーメッセージが表示される"""
    driver.get(f"{base_url}/login")

    driver.find_element(By.ID, "username").send_keys("valid_user")
    driver.find_element(By.ID, "password").send_keys("wrong_pass")
    driver.find_element(By.ID, "login-btn").click()

    error_msg = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located(
            (By.CSS_SELECTOR, "[data-testid='login-error']")  # data-testid 推奨
        )
    )
    assert "パスワードが正しくありません" in error_msg.text

def test_login_failure_empty_fields(driver, base_url):
    """異常系:IDとPWが空のまま送信するとバリデーションエラーが出る"""
    driver.get(f"{base_url}/login")
    driver.find_element(By.ID, "login-btn").click()

    error = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CLASS_NAME, "validation-error"))
    )
    assert error.is_displayed()

STEP 3:pytest parametrize によるデータ駆動テストの実装

@pytest.mark.parametrize を使うと、同じテストロジックを複数のデータで繰り返し実行できます。異常系のパターンが多い場合に特に効果的です。

# tests/test_login.py(続き)
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# テストデータをリストで定義
INVALID_LOGIN_CASES = [
    ("", "valid_pass", "ユーザー名を入力してください"),          # ID空
    ("valid_user", "", "パスワードを入力してください"),          # PW空
    ("wrong_user", "valid_pass", "認証に失敗しました"),          # ID間違い
    ("valid_user", "wrong_pass", "パスワードが正しくありません"),  # PW間違い
    ("a" * 256, "valid_pass", "入力値が長すぎます"),             # ID最大長超過
]

@pytest.mark.parametrize("username, password, expected_error", INVALID_LOGIN_CASES)
def test_login_validation(driver, base_url, username, password, expected_error):
    """parametrize:複数の異常系パターンをまとめてテスト"""
    driver.get(f"{base_url}/login")  # base_url fixture で統一

    driver.find_element(By.ID, "username").send_keys(username)
    driver.find_element(By.ID, "password").send_keys(password)
    driver.find_element(By.ID, "login-btn").click()

    error_msg = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CLASS_NAME, "error-message"))
    )
    assert expected_error in error_msg.text

実行結果サンプル

pytest tests/test_login.py -v

collected 8 items

test_login.py::test_login_success                                     PASSED
test_login.py::test_login_failure_wrong_password                      PASSED
test_login.py::test_login_failure_empty_fields                        PASSED
test_login.py::test_login_validation[-valid_pass-ユーザー名を入力してください]  PASSED
test_login.py::test_login_validation[valid_user--パスワードを入力してください]  PASSED
test_login.py::test_login_validation[wrong_user-valid_pass-認証に失敗しました] PASSED
test_login.py::test_login_validation[valid_user-wrong_pass-パスワードが…]     PASSED
test_login.py::test_login_validation[aaaa…-valid_pass-入力値が長すぎます]     PASSED

========================== 8 passed in 18.34s ==========================
💡 実務Tip:テストデータをリスト変数(INVALID_LOGIN_CASES)として切り出すと、テストロジックとデータを分離できます。仕様変更でデータだけ追加・修正すれば済むため、保守性が大幅に上がります。
📖 pytest の parametrize・fixture をさらに深く学びたい方はこちらPython pytest 完全ガイド|fixture・parametrize・conftest.pyを実務レベルで解説

STEP 4:pytest mark でテストを分類・選択実行する

@pytest.mark を使うとテストにタグを付けて、特定のグループだけ選んで実行できます。CI/CD で「スモークテストだけ実行」「全テストを実行」と使い分けるときに必須です。

pytest.ini にカスタム mark を登録

[pytest]
testpaths = tests
addopts = -v
markers =
    smoke: スモークテスト(最重要・毎回実行)
    regression: リグレッションテスト(全体確認時に実行)
    slow: 実行に時間がかかるテスト

テストに mark を付ける

# tests/test_login.py
import pytest

@pytest.mark.smoke
def test_login_success(driver):
    """スモークテスト:正常ログインは最優先で確認"""
    ...

@pytest.mark.regression
def test_login_failure_wrong_password(driver):
    """リグレッション:パスワード間違いのエラー表示確認"""
    ...

@pytest.mark.slow
@pytest.mark.regression
def test_login_session_timeout(driver):
    """遅いテスト:セッションタイムアウトの動作確認"""
    ...

mark を指定して実行

# スモークテストだけ実行(デプロイ後の確認に)
pytest -m smoke

# リグレッションテストだけ実行(リリース前に)
pytest -m regression

# 遅いテストを除いて実行
pytest -m "not slow"

# スモーク かつ リグレッション
pytest -m "smoke and regression"
📖 GitHub Actions で mark を活用した CI/CD 自動実行はこちらGitHub Actions × PlaywrightでE2Eテストを自動化する方法

STEP 5:fixture の応用(セットアップ・ティアダウン)

fixture を使うと「テスト前後の処理」を一元管理できます。

# tests/conftest.py(追加)
import pytest

@pytest.fixture(scope="function")
def go_to_login(driver):
    """ログインページに移動するfixture"""
    driver.get("https://example-app.com/login")
    yield driver
    # ティアダウン:テスト後にスクリーンショットを保存(失敗時のデバッグ用)
    import os
    import time
    os.makedirs("screenshots", exist_ok=True)  # フォルダがなければ作成
    # タイムスタンプでファイル名を生成(タイトルの特殊文字・日本語によるエラーを防ぐ)
    # ※ 実務では pytest hook(pytest_runtest_makereport)を使って失敗時のみ保存する構成が一般的です
    driver.save_screenshot(f"screenshots/{int(time.time())}.png")


@pytest.fixture(scope="session")
def base_url():
    """ベースURLをfixture化(環境ごとに切り替えやすくなる)
    ※ これにより BASE_URL 定数は不要。全テストで base_url fixture を使うこと。"""
    return "https://example-app.com"  # ※ サンプルURLです。実際のURLに置き換えてください

💡 失敗時のみスクリーンショットを保存する(CI推奨構成)

毎回保存するとCIでストレージが肥大化します。pytest hook(pytest の実行タイミングに独自処理を差し込める拡張機能)の pytest_runtest_makereport を使うと失敗したテストのみ保存できます。

# conftest.py に追加
import os, re, pytest

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.failed:
        driver = item.funcargs.get("driver")
        if driver:
            os.makedirs("screenshots", exist_ok=True)
            # Windows禁止文字をまとめて除去(\ / * ? : " < > |)
            safe_name = re.sub(r'[\\/*?:"<>|]', "_", item.name)
            driver.save_screenshot(f"screenshots/{safe_name}.png")
# fixture を組み合わせて使う
def test_login_page_title(go_to_login, base_url):
    """ログインページのタイトルを確認"""
    assert "ログイン" in go_to_login.title
    assert base_url in go_to_login.current_url
💡 fixture チェーンのポイントgo_to_login fixture の引数に driver を受け取ることで fixture を組み合わせられます。「ドライバー起動 → ログインページ移動 → テスト実行 → スクリーンショット保存」という処理の流れをコードで明示できます。
📖 テストコードをさらに整理する「Page Object Model」パターンはこちらPage Object Modelとは?Playwright × pytestでの実装方法

STEP 6:実践的なテスト例(会員登録フォーム)

これまで学んだ fixture・parametrize・mark を組み合わせた実践例です。

# tests/test_register.py
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 正常系データ
# ※ 実案件では同じメールアドレスを繰り返すと重複登録エラーになります
# テストごとにユニーク化する場合は f"test_{int(time.time())}@example.com" を使いましょう
VALID_USER = {
    "name": "テスト太郎",
    "email": "test@example.com",
    "password": "SecurePass123!",
}

# バリデーション異常系データ
VALIDATION_CASES = [
    ("name", "", "お名前を入力してください"),
    ("email", "not-an-email", "正しいメールアドレスを入力してください"),
    ("email", "", "メールアドレスを入力してください"),
    ("password", "short", "パスワードは8文字以上で入力してください"),
]

@pytest.fixture
def register_page(driver, base_url):
    """会員登録ページに移動するfixture(base_url fixture と組み合わせて URL を統一)"""
    driver.get(f"{base_url}/register")
    yield driver


@pytest.mark.smoke
def test_register_success(register_page):
    """スモーク:正常な会員登録が完了する"""
    d = register_page
    d.find_element(By.ID, "name").send_keys(VALID_USER["name"])
    d.find_element(By.ID, "email").send_keys(VALID_USER["email"])
    d.find_element(By.ID, "password").send_keys(VALID_USER["password"])
    d.find_element(By.ID, "submit-btn").click()

    success = WebDriverWait(d, 10).until(
        EC.visibility_of_element_located((By.CLASS_NAME, "success-message"))
    )
    assert "登録が完了しました" in success.text


@pytest.mark.regression
@pytest.mark.parametrize("field, value, expected_error", VALIDATION_CASES)
def test_register_validation(register_page, field, value, expected_error):
    """リグレッション:バリデーションエラーのパターンテスト"""
    d = register_page

    # 有効な値でフォームを埋めてから対象フィールドだけ変更
    d.find_element(By.ID, "name").send_keys(VALID_USER["name"])
    d.find_element(By.ID, "email").send_keys(VALID_USER["email"])
    d.find_element(By.ID, "password").send_keys(VALID_USER["password"])

    # 対象フィールドをクリアして異常値を入力
    target = d.find_element(By.ID, field)
    target.clear()
    target.send_keys(value)
    d.find_element(By.ID, "submit-btn").click()

    error = WebDriverWait(d, 10).until(
        # By.ID でフィールドごとのエラー要素を特定(field-error より精度が高い)
        EC.visibility_of_element_located((By.ID, f"{field}-error"))
    )
    assert expected_error in error.text

📋 ここまでのまとめ

  • conftest.py:WebDriver の起動・終了を fixture に集約 → 全テストから共有
  • parametrize:テストデータをリストで管理 → 異常系パターンを効率的に網羅
  • mark:smoke / regression に分類 → CI/CD で必要なテストだけ選択実行
  • fixture チェーン:driver → go_to_login → テスト、という処理の流れを明示

⚠️ よくあるはまりポイント7選

Selenium × pytest を使い始めたときに詰まりやすいポイントをまとめました。

① test_ プレフィックスを忘れる

関数名が test_ で始まっていないと pytest に収集されません。collected 0 items が出たら確認しましょう。

② conftest.py の置き場所を間違える

conftest.py は参照したいテストファイルと同じディレクトリか、その上位に置く必要があります。

tests/
├── conftest.py   ← ここに置く(tests/ 直下)
└── test_login.py

③ scope=”session” で状態が引き継がれる

scope="session" を使うとテスト間でブラウザの状態(Cookie・セッション)が引き継がれます。テスト順序に依存するバグが起きやすいので scope="function" から始めましょう。

④ カスタム mark を pytest.ini に未登録で警告が出る

@pytest.mark.smoke などのカスタム mark は pytest.ini に登録しないと PytestUnknownMarkWarning が出ます。

[pytest]
markers =
    smoke: スモークテスト
    regression: リグレッションテスト

⑤ print() が -s なしで表示されない

デフォルトでは print() の出力がキャプチャされて表示されません。デバッグ中は pytest -s で実行するか、addopts = -v -spytest.ini に追加しましょう。

⑥ assert 前の例外で ERROR になる

要素が見つからないなど assert より前で例外が起きると FAILED ではなく ERROR になります。WebDriverWait で要素の存在を確認してから assert しましょう。

⑦ プロジェクトルート以外から実行すると ModuleNotFoundError

pytest はプロジェクトルートで実行する必要があります。

# NG
cd tests && pytest

# OK
cd selenium-project  # プロジェクトルートで
pytest

FAQ

Q. fixture の scope はどれを使えばいいですか?

最初は scope="function"(デフォルト) を推奨します。テストごとにブラウザが起動・終了するので状態汚染が起きにくく安全です。テストが増えて速度が問題になったら scope="session" への変更を検討しましょう。ただし session スコープは Cookie やログイン状態が引き継がれる点と、pytest-xdist(並列実行)との組み合わせには追加設定が必要な点に注意が必要です。

Q. implicitly_wait と WebDriverWait はどう使い分けますか?

implicitly_wait は全要素検索にグローバルで適用される待機です。WebDriverWait は「表示されているか」「クリック可能か」など特定の条件を指定できる明示的な待機です。完全に併用禁止ではありませんが、両方設定すると待機時間の合算で予期しない遅延が起きる場合があります。WebDriverWait を主軸にし、implicitly_wait は最小限の値に設定しておくのが標準です。

Q. parametrize のデータはファイルから読み込めますか?

できます。CSV や JSON ファイルからデータを読み込んで parametrize に渡す方法がよく使われます。pytest-datafiles プラグインを使うか、シンプルに json.load()csv.reader() でリストを作って @pytest.mark.parametrize に渡す方法が一般的です。

Q. pytest -m "smoke and regression" は何を実行しますか?

smokeregression両方の mark が付いているテストだけ実行されます。「smoke または regression のどちらかが付いているテスト」を実行したい場合は pytest -m "smoke or regression" を使います。初学者が混乱しやすいポイントなので覚えておきましょう。

Q. By.CLASS_NAME でエラー要素を取得するとき不安定になることがありますか?

あります。同一クラス名の要素が複数存在するとき、意図しない要素を取得してしまいます。本記事では By.ID, f"{field}-error" のようにフィールドごとの ID を使う方法を採用しています。さらに安定させたい場合は data-testid 属性を使ったロケーター(By.CSS_SELECTOR, "[data-testid='name-error']")が実務では推奨されています。

Q. テストで同じメールアドレスを使うと重複登録エラーになりますか?

なります。本記事の VALID_USER は説明のためにメールアドレスを固定していますが、実案件では2回目以降に「メールアドレスが既に登録されています」のエラーになります。テストごとにユニークなメールを生成する場合は f"test_{int(time.time())}@example.com" のようにタイムスタンプを使う方法が一般的です。

Q. fixture と setUp/tearDown(unittest形式)の違いは?

setUp/tearDown はクラスベースのテストに組み込まれる初期化・終了処理です。pytest の fixture は関数ベースで使え、scope で共有範囲を柔軟に制御できる点が大きな違いです。また fixture は引数として注入するため、再利用・組み合わせが簡単です。pytest プロジェクトでは fixture が標準です。

Q. conftest.py は複数作れますか?

作れます。pytest はディレクトリ階層に沿って conftest.py を自動的に読み込みます。プロジェクトルートの conftest.py に全体共通の fixture を、サブディレクトリの conftest.py にそのディレクトリ専用の fixture を定義するパターンが一般的です。

Q. Selenium テストは Page Object に分割すべきですか?

テストが10〜20個を超えてきたら分割を検討しましょう。Page Object Model(POM)を使うとロケーター定義と操作ロジックをページクラスに集約でき、要素の変更がクラス1箇所の修正で済みます。最初から作る必要はなく、重複コードが増えてきたら導入するのが現実的です。

Q. pytest-xdist で並列実行できますか?

できます。pip install pytest-xdist して pytest -n auto で実行すると CPU コア数に応じて並列実行されます。ただし scope="session" の fixture は並列実行時に注意が必要で、ブラウザの共有ができないため各ワーカーで独立した fixture を使う設計が必要です。

Q. CI/CD では headless モードは必須ですか?

ディスプレイのない CI 環境(GitHub Actions・Jenkins 等)では --headless が必須です。ローカル開発では外した方がブラウザの動作を目視確認できてデバッグしやすくなります。conftest.py の --headless 行をコメントアウトしてローカルではオフ、CI では環境変数で切り替える構成が使いやすいです。

Q. fixture の autouse とは何ですか?

@pytest.fixture(autouse=True) を付けると、テスト関数の引数に書かなくても自動で適用されます。例えば「全テストでブラウザを最大化したい」「全テストでログを出力したい」ような横断的な処理に使います。ただし意図しないテストにも適用されるため、必要な範囲を scope や conftest.py の配置で制御しましょう。

Q. fixture の yield と return の違いは?

return はテスト前の処理(セットアップ)だけ書けます。yield を使うと yield の前がセットアップ・後がティアダウン(後処理)になります。WebDriver のように「起動 → テスト → 終了」という前後の処理が必要な場合は yield 一択です。return はシンプルな値を返す fixture(base_url 等)に使います。

Q. Selenium と Playwright はどちらを学ぶべきですか?

まず Selenium を学ぶことを推奨します。理由は求人・Upwork 案件数が圧倒的に多いからです。Selenium の基礎(WebDriver・待機処理・pytest 連携)を習得してから Playwright に移行すると、概念の違いが理解しやすくなります。両方できるとエンジニアとしての市場価値が大きく上がります。

Q. pytest.ini と pyproject.toml の違いは?

どちらも pytest の設定ファイルですが役割が異なります。pytest.ini は pytest 専用の設定ファイルでシンプルです。pyproject.toml は Python プロジェクト全体(依存関係・ビルド・リンター等)を一元管理できるモダンな形式で、[tool.pytest.ini_options] セクションに同じ設定を書けます。新規プロジェクトは pyproject.toml 推奨ですが、既存プロジェクトは pytest.ini で問題ありません。

実務では、fixture・parametrize・mark のパターンをチームの標準として定義し、新メンバーが参加したときに同じ構成でテストを書けるようにしています。特に「テストデータをリストで切り出して parametrize に渡す」パターンは、仕様変更のたびにロジックではなくデータだけ修正すればよくなるため、保守コストが大幅に下がります。

この記事の手順を一通り実装すれば、テスト設計とテスト実装が明確に分離された、保守しやすいテストスイートが完成します。

📋 この記事のまとめ

  • conftest.py に WebDriver fixture を定義することで全テストファイルから共有できる
  • parametrize でテストデータとロジックを分離し、異常系パターンを効率的に網羅できる
  • mark でテストを分類し、「スモークだけ」「リグレッションだけ」と選択実行できる
  • fixture のチェーンでセットアップ・ティアダウン処理を一元管理できる
  • はまりポイント(test_ 忘れ・conftest 置き場・scope の引き継ぎ)を事前に知っておくと詰まらない

まずは parametrize を使って既存のテストのデータを切り出すことから始めてみてください。たった1つのパターンを実装するだけで「テスト設計」と「テスト実装」が分離され、テストスイート全体の見通しが一気によくなります。

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