0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Pytest × Playwright】テスト関数に使われる引数pageは何をしているのか

Posted at

Pytest-Playwrightを使用して、E2Eテスト自動化を行うとき、テスト関数の引数にpageを用います。1

以下のサンプルスクリプトはcodegenを使って、HOTEL PLANISPHEREの自動テストスクリプトを作成したものです。

sample.py
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メソッドが実行されます。

test_with_pytest_fixture.py
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_playwright.py

@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メソッドを使わない場合は以下のようになります。

pytest_without_fixture_method.py
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メソッド一覧は以下のページに記載されています。

参考資料

  1. https://docs.pytest.org/en/latest/explanation/fixtures.html 2

  2. pytest_without_fixture_method.pyではテスト失敗の場合、ブラウザを閉じません。そのため、fixtureメソッドやteardown_method等を活用する必要があります。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?