Pytest-Playwrightを使用して、E2Eテスト自動化を行うとき、テスト関数の引数にpageを用います。1
以下のサンプルスクリプトはcodegenを使って、HOTEL PLANISPHEREの自動テストスクリプトを作成したものです。
from playwright.sync_api import Page, expect
def test_example(page: Page) -> None:
page.goto("https://hotel.testplanisphere.dev/ja/reserve.html?plan-id=0")
page.get_by_label("宿泊日 必須").click()
page.get_by_label("宿泊日 必須").fill("2024/07/01")
page.get_by_label("宿泊日 必須").press("Tab")
page.get_by_label("宿泊数 必須").fill("3")
page.get_by_label("宿泊数 必須").press("Tab")
page.get_by_label("人数 必須").fill("2")
page.get_by_text("お得な観光プラン").click()
page.get_by_label("氏名 必須").click()
page.get_by_label("氏名 必須").fill("TEST")
page.get_by_label("確認のご連絡 必須").select_option("no")
page.locator("[data-test=\"submit-button\"]").click()
expect(page.get_by_role("heading", name="合計 44,000円(税込み)")).to_be_visible()
Playwright公式ページのサンプルスクリプトにも、pageは使われていますが、一体このpageは何をしているのでしょうか。
触り始めた当初は「playwright使うときはpageが必要」というくらいの感覚でした。
最近気になったので、調べてみました。
引数pageはpytest-playwrightが提供しているfixtureメソッド
このpageはPlaywrightのプラグインであるpytest-playwrightが提供しているfixtureメソッドでした。
fixtureメソッドとは
fixtureメソッドとは、テストの前後処理をまとめている関数です1。
テスト関数の引数でfixtureメソッド名を指定するとテスト関数実行前と実行後に指定されたfixtureメソッドが実行されます。
import pytest
@pytest.fixture()
def example_fixture():
# 前処理
print("テスト実施前")
# ここでテストを実行
yield "テスト実施"
# 後処理
print("テスト完了")
def test_foo(example_fixture):
print(example_fixture)
assert True
👆のスクリプトを実行(pytest -s
)すると以下になります。
テスト実施前
テスト実施
テスト完了
pageメソッドはテスト用のブラウザページを生成している
テスト関数の引数にpageを使うと、以下のメソッドが実行されます。
メソッド | 内容 |
---|---|
new_context | pytestコマンド実行時に指定された引数に基づいて、起動するブラウザの種類等を設定します。 |
context | テスト用の新しいブラウザコンテキストを生成します。 |
page | テスト用の新しいブラウザページを生成します。 |
@pytest.fixture
def new_context(
browser: Browser,
browser_context_args: Dict,
_artifacts_recorder: "ArtifactsRecorder",
request: pytest.FixtureRequest,
) -> Generator[CreateContextCallback, None, None]:
browser_context_args = browser_context_args.copy()
context_args_marker = next(request.node.iter_markers("browser_context_args"), None)
additional_context_args = context_args_marker.kwargs if context_args_marker else {}
browser_context_args.update(additional_context_args)
contexts: List[BrowserContext] = []
def _new_context(**kwargs: Any) -> BrowserContext:
context = browser.new_context(**browser_context_args, **kwargs)
original_close = context.close
def _close_wrapper(*args: Any, **kwargs: Any) -> None:
contexts.remove(context)
_artifacts_recorder.on_will_close_browser_context(context)
original_close(*args, **kwargs)
context.close = _close_wrapper
contexts.append(context)
_artifacts_recorder.on_did_create_browser_context(context)
return context
yield cast(CreateContextCallback, _new_context)
for context in contexts.copy():
context.close()
@pytest.fixture
def context(new_context: CreateContextCallback) -> BrowserContext:
return new_context()
@pytest.fixture
def page(context: BrowserContext) -> Page:
return context.new_page()
pytest-playwrightのpageメソッドを使わない場合は以下のようになります。
from playwright.sync_api import sync_playwright, Playwright
def test_reserve() -> None:
with sync_playwright() as playwright:
firefox = playwright.firefox
browser = firefox.launch()
page = browser.new_page()
page.goto("https://hotel.testplanisphere.dev/ja/reserve.html?plan-id=0")
page.get_by_label("宿泊日 必須").click()
page.get_by_label("宿泊日 必須").fill("2024/07/01")
page.get_by_label("宿泊日 必須").press("Tab")
page.get_by_label("宿泊数 必須").fill("3")
page.get_by_label("宿泊数 必須").press("Tab")
page.get_by_label("人数 必須").fill("2")
page.get_by_text("お得な観光プラン").click()
page.get_by_label("氏名 必須").click()
page.get_by_label("氏名 必須").fill("TEST")
page.get_by_label("確認のご連絡 必須").select_option("no")
page.locator("[data-test=\"submit-button\"]").click()
expect(page.get_by_role("heading", name="合計 44,000円(税込み)")).to_be_visible()
browser.close()
運用することを踏まえると素直にpytest-playwright提供のfixtureメソッドを使った方がよさそうです。2
pytest-playwrightが提供しているfixtureメソッド一覧は以下のページに記載されています。
参考資料
-
https://docs.pytest.org/en/latest/explanation/fixtures.html ↩ ↩2
-
pytest_without_fixture_method.pyではテスト失敗の場合、ブラウザを閉じません。そのため、fixtureメソッドやteardown_method等を活用する必要があります。 ↩