PyCon JP 2022

Playwrightを使って簡単に自動テストしてみる
2022/10/14 , pyconjp_2
言語: 日本語

PythonのWeb自動テストではSeleniumを使う方法が有名ですが、
作成・メンテナンスするのに、結構手間がかかります。

他の言語であれば、PupeteerやCypressという選択肢もありますが
できればPythonで実装したいと感じていました。

Playwrightは、TypeScriptで作成されたテスト自動化フレームワークで
多言語対応されており、Pythonでも利用できます。

このトークでは、Pythonで実装できるいくつかの自動化の方法と比較し、
Playwrightを使うことでより簡単に実装できることをお話します。


Playwrightは、Microsoft社製のWebテスト・自動化のための
フレームワークです。
言語はTypeScriptですが、多言語対応されているため
python用のライブラリも公開されています。

私はこれまで、
Selenium IDE
Selenium Webdriver(pythonによる)
Robot Framework + SeleniumLibrary
Pylenium.io
と、いくつかの方法を試してきました。

それぞれとても良いツール、ライブラリで、
作成することは当然可能なのですが、
もっと簡単に作成できる方法はないかなと感じていたところに
Playwrightの存在を知りました。

Playwrightは直感的に書けるだけでなく、
Selenium IDEのようなレコーディング機能も用意されており
これまでの方法に比べて圧倒的に簡単に作成することができます。

このトークではPythonを使ったテスト自動化について、
私が試してきた方法を例として取り上げて、
それぞれの特徴と比較しつつ、Playwrightの良さを伝えたいと考えています。

参考)実装例
① Chromeを起動
② Googleのサイトを表示
③ PyConJP2022を検索
④ 検索結果にPyConJP2022という文字がある要素をリンク
⑤ PyConJP2022のサイトを表示する

Seleniumを使った実装

Seleniumでテストを作った場合は、
以下のようなロジックになります。

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

driver = webdriver.Chrome(ChromeDriverManager().install())

try:
    wait = WebDriverWait(driver, timeout=10)
    driver.get('https://google.com')

    search = driver.find_element(By.NAME, 'q')
    search.send_keys('PyConJP 2022')

    btn = driver.find_element(By.NAME, 'btnK')
    btn.submit()

    elements = driver.find_elements_by_xpath("//a[@href]")
    for element in elements:
        if element.text.count('PyCon JP 2022') >= 1:
            element.click()
            break

finally:
    driver.quit()

今回は単純なものなので、それほど難しいコードにはなっていませんが、
複雑なものになると、待ちを入れたり動作させるまでの手間は増えていくと考えています。

RobotFramework + Seleniumを使った実装

RobotFrameworkは、Pythonで作成された自動化フレームワークです。
テスト自動化やRPAに使用することができます。
RobotFramework用のテストは独自の記法になりますが、
Pythonでメソッドを作成することもできます。

*** Settings ***
Documentation     Search PyConJP 2022 Site
Library           SeleniumLibrary
Library           my_library.py

*** Test Cases ***
PyConJP2022のサイトを検索して開く
    Chromeで https://google.com を開く
    ブラウザで PyCon JP 2022 を検索する
    検索結果から PyCon JP 2022 をクリックする

    [Teardown]
    ブラウザを閉じる


*** Keywords ***
Chromeで ${URL} を開く
    ${driver_path} =    my_library.Get Driver
    Create Webdriver  Chrome  chrome  executable_path=${driver_path}
    Go To    ${URL}


ブラウザで ${KEYWORD} を検索する
    Wait Until Element Is Visible    CSS=[name=q]
    Input Text    CSS=[name=q]    ${KEYWORD}
    Wait Until Element Is Visible    CSS=[name=btnK]
    Click Element    CSS=[name=btnK]


検索結果から ${KEYWORD} をクリックする
    @{elements} =     Get WebElements    xpath=//a[@href]
    FOR    ${element}    IN    @{elements}
        ${el_text} =    Get Text    ${element}
        ${el_text} =  Replace Lf    ${el_text}
        ${check_result} =  Check Include Str    ${el_text}    PyCon JP 2022
        Run Keyword If    ${check_result}
        ...    Click Element    ${element}
        Exit For Loop If    ${check_result}
    END
ブラウザを閉じる
    Close Browser

コード量は、むしろ普通にSeleniumを使うよりも多くなっていますが、
RobotFrameworkを使うことで、可読性の高いテストが作れるところが魅力です。
例のような日本語のテストも作ることができます。
また標準で実行結果のレポートも作成してくれるところも魅力です。

Pylenium.ioを使った実装

Pylenium.ioはCypressのように使いやすい構文でSeleniumを使いやすくした
フレームワークになっており
- 簡単にテスト環境をセットアップできる
- Webドライバーの自動インストール - Cypressのように使いやすい構文でSeleniumを利用できる

を実現したPythonのフレームワークです。

def test_pyconjp_google_search(py):
    py.visit('https://google.com')
    py.get('[name="q"]').type('PyConJP 2022')
    py.get('[name="btnK"]').submit()
    elements = py.findx('//a[@href]')
    for element in elements:
        if 'PyCon JP 2022' in element.text():
            element.click()
            break

マイナーなフレームワークですが、seleniumで普通にコーディングするよりも
コード量は、圧倒的に少なく書くことができます。
尚、このフレームワークは基本的にはSeleniumを使いやすくしたものになるので
Seleniumで出来ていたことは基本実施できます。
但しpytest上で動作することが前提なので、自動化への利用には向いていないかと思います。

Githubのスター数は少ないですが、私は個人的にはとても気に入っています。

4.Playwrightを使った実装

(1)Playwright-pythonを使った実装

playwright-pythonは標準のpython用ライブラリです。
Playwright InspectorというGUIツールを呼び出すことができます。
このツールを使うことで、操作をレコードすることができ、
以下のコードであれば、すぐに生成できます

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    page.goto("https://www.google.com/webhp")
    page.locator("[aria-label=\"検索\"]").click()
    page.locator("[aria-label=\"検索\"]").fill("PyConJP 2022")

    with page.expect_navigation():
        page.locator("[aria-label=\"検索\"]").press("Enter")

    with page.expect_navigation():
        page.locator("text=PyCon JP 2022https://pycon.jp >> h3").click()

これで動作としては同じように動作します。

尚、これまでの例と同じような書き方をしたい場合は、
以下のように書くことも可能です。

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    page.goto("https://www.google.com/webhp")
    page.locator("[name=q]").fill("PyConJP 2022")
    with page.expect_navigation():
        page.locator("[aria-label=\"検索\"]").press("Enter")

    for element in page.query_selector_all('//a[@href]'):
        if 'PyCon JP 2022' in element.text_content():
            element.click()
            break

作成までに掛かる時間が圧倒的に短く、Seleniumよりも高速に動作します。

(2)RobotFramework-Browserを使った実装

robotframework用のライブラリとして、playwrightを使ったPlaywight-browserというライブラリがあります。
Seleniumlibraryと書き方は異なりますが、同じように書くことはできそうです。

*** Settings ***
Library    Browser
Library    my_library.py

*** Test Cases ***
PyConJP2022のサイトを検索して開く
    Chromeで https://google.com を開く
    ブラウザで PyCon JP 2022 を検索する
    検索結果から PyCon JP 2022 をクリックする


*** Keywords ***
Chromeで ${URL} を開く
    Open Browser  ${URL}    headless=False


ブラウザで ${KEYWORD} を検索する
    Fill Text    [name=q]    ${KEYWORD}
    @{btns} =     Get Elements    [name=btnK]
    FOR    ${btn}    IN    @{btns}
        Click  ${btn}
        Exit For Loop
    END


検索結果から ${KEYWORD} をクリックする
    @{elements} =     Get Elements    xpath=//a[@href]
    FOR    ${element}    IN    @{elements}
        ${el_text} =    Get Text    ${element}
        ${el_text} =  Replace Lf    ${el_text}
        ${check_result} =  Check Include Str    ${el_text}    ${KEYWORD}
        Run Keyword If    ${check_result}
        ...  Click    ${element}
        Exit For Loop If    ${check_result}
    END

今回作成した程度では、速度比較は難しいですが、
実際にはSeleniumlibraryよりも高速により安定的に動作するようです。
robotframeworkでWEB自動化を行う場合は、今後はこちらが使われることが多くなりそうです。

福岡在住の地方エンジニアです。
P九州でPythonのコミュニティ活動も行っています。
・PyConKyushu実行委員会(運営代表)
・Python Boot Camp Fukuoka2nd(スタッフ)など。