Outline
webのテスト自動化はseleniumでコードベースで作成するものから、キーワード駆動、ローコードツールで作成するものまでいろいろなものがあります。
ここではあえてレトロ(失礼しました)なやり方 Pythonを使ってみた。
素のpythonでやると結構大変であり、おなじpythonでできているrobot frameworkのありがたみを実感できた
Setup
Python
Pythonサイトから、Stable Releasesのバージョンをダウンロードする
「Add python.exe to PATH」のチェックを入れて、どこからでも実行できるようにする。
その後、「Install Now」をクリックし、インストールを完了させる。
任意の場所でコマンドプロンプトを開き、以下コマンドを実施し、動作確認をする
python --version
library
Pythonで使用するライブラリをインストールする
pip install selenium
テストシナリオ
テスト自動化演習サイトを使います。
以下のように、ログインを行うテストを作成します
単純なテストスクリプト
スクリプト
# selenium library
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
# parameter
URL = 'https://hotel-example-site.takeyaqa.dev/ja/'
EMAIL = 'ichiro@example.com'
PASSWORD = 'password'
USERNAME = '山田一郎'
# web driver setting
options = webdriver.ChromeOptions()
new_driver = ChromeDriverManager().install()
service = ChromeService(executable_path=new_driver)
driver = webdriver.Chrome(service=service , options=options)
driver.maximize_window()
# test scenario
driver.get(URL)
driver.find_element(By.XPATH,"//li[@id='login-holder']/a").click()
# input id & password
driver.find_element(By.XPATH,"//input[@id='email']").send_keys(EMAIL)
driver.find_element(By.XPATH,"//input[@id='password']").send_keys(PASSWORD)
driver.find_element(By.XPATH,"//button[@id='login-button']").click()
# validate login user name
element_text = driver.find_element(By.XPATH,'//p[@id="username"]').text
assert element_text == USERNAME
driver.quit()
解説
旧来では、chrome driverをダウンロードしてきて、それを指定してwebdriverを動かす必要があった。
ただ、WebDriverManagerを用いると、そのあたりを全部ライブラリで吸収してくれる!
new_driver = ChromeDriverManager().install()
WebDriverManagerの詳細
テスト実行
では、実行してみましょう
python test_login.py
実行結果はこのようになります。
はて? ローコードツールなど使っていた方にとって、物足らないと思います。
はい、テスト結果がほとんどありません。
では、あえて失敗したときも試してみましょう
# parameter
URL = 'https://hotel-example-site.takeyaqa.dev/ja/'
EMAIL = 'ichiro@example.com'
PASSWORD = 'password'
USERNAME = '山田三郎'
ログインのUSERNAMEを'山田一郎'から'山田三郎'に変更しました
すると、以下のようにログイン名のEXPECTATIONとACTUALが異なるエラーになることがわかります。
まあ、テストはできるけど、これだとテスト結果分析とか大変だなーって思います。
そこで少しアップグレードさせます
Pytest
pytestは、Python用のオープンソーステストフレームワークで、シンプルかつ柔軟なテスト作成を可能にします。
豊富なプラグインシステムにより、カバレッジ測定や並列テストなどの追加機能も簡単に利用できます。
ユニットテストからインテグレーションテストまで幅広く対応し、効率的なテスト作成と実行をサポートします
pytestを用いると、もっと充実したテストが可能になります
fixture
fixtureは、pytestにおいてテストの前後に必要な準備や後片付けを行うための関数です。
Setup , teardownに該当します
fixtureは、テスト関数に引数として渡され、自動的に実行されます。これにより、テストコードの再利用性が高まり、テストの可読性も向上します。
今回、共通処理を conftest.py に定義します
import pytest
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from dotenv import load_dotenv
@pytest.fixture(scope="session")
def driver():
load_dotenv()
options = webdriver.ChromeOptions()
new_driver = ChromeDriverManager().install()
service = ChromeService(executable_path=new_driver)
driver = webdriver.Chrome(service=service , options=options)
driver.maximize_window()
driver.implicitly_wait(3)
print("[SET UP] finish loading driver.")
yield driver
print("[TEAR DWON] finish loading driver.")
driver.quit()
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
# get test result
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
# save screen shot when test is failed
if report.when == 'call' and report.failed:
driver = item.funcargs['driver']
screenshot_path = os.path.join(os.getcwd(), f"{item.name}.png")
print("[MAKE REPORT] save screenshot " + screenshot_path)
driver.save_screenshot(screenshot_path)
screenshot = driver.get_screenshot_as_base64()
extra.append(pytest_html.extras.image(screenshot, ''))
report.extra = extra
report.extra.append(pytest_html.extras.image(screenshot_path))
2つ共通処理を定義しています
driver
起動時のwebdriverを設定します
pytest_runtest_makereport
テスト失敗時に自動的にスクリーンショットを保存します
テスト本体
他のテストでも用いる共有部分(setup / teardown)をconftest.pyに外だしすることができたため、テスト本体はシンプルになります
import os
from selenium.webdriver.common.by import By
URL = 'https://hotel.testplanisphere.dev/ja/index.html'
EMAIL = 'ichiro@example.com'
PASSWORD = 'password'
USERNAME = '山田一郎'
def test_login(driver):
driver.get(URL)
driver.find_element(By.XPATH,"//*[@id='login-holder']/a").click()
# input id & password
driver.find_element(By.XPATH,"//*[@id='email']").send_keys(EMAIL)
driver.find_element(By.XPATH,"//*[@id='password']").send_keys(PASSWORD)
driver.find_element(By.XPATH,"//*[@id='login-button']").click()
# validate
element_text = driver.find_element(By.XPATH,"//p[@id='username']").text
assert element_text == USERNAME
screenshot_path = os.path.join(os.getcwd(), f"success.png")
driver.save_screenshot(screenshot_path)
return
if __name__ == "__main__":
test_login()
テスト実行
pytest test_login_pytest.py --html=report.html
pytestを行うことで、レポートhtmlも作成されます
では、失敗した例です
レポートhtmlはこちら
また、失敗したときのスクショはこちら
このように、pytestを用いると、スクリプトの保守性を上げ、かつ実行結果作成まで対応してくれる。
ただ、ある程度基盤を作りこまないといけないため、robot frameworkなどのありがたみを感じた。