1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VALTES Advent Calendar 2024

Day 25

[Python] WEBアプリケーションをテスト自動化する

Last updated at Posted at 2024-12-24

Outline

webのテスト自動化はseleniumでコードベースで作成するものから、キーワード駆動、ローコードツールで作成するものまでいろいろなものがあります。

ここではあえてレトロ(失礼しました)なやり方 Pythonを使ってみた。
素のpythonでやると結構大変であり、おなじpythonでできているrobot frameworkのありがたみを実感できた

Setup

Python

Pythonサイトから、Stable Releasesのバージョンをダウンロードする

image.png

「Add python.exe to PATH」のチェックを入れて、どこからでも実行できるようにする。
その後、「Install Now」をクリックし、インストールを完了させる。

任意の場所でコマンドプロンプトを開き、以下コマンドを実施し、動作確認をする

python --version

image.png

library

Pythonで使用するライブラリをインストールする

pip install selenium

テストシナリオ

テスト自動化演習サイトを使います。

以下のように、ログインを行うテストを作成します

image.png

単純なテストスクリプト

スクリプト

test_login.py
# 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

image.png

実行結果はこのようになります。
はて? ローコードツールなど使っていた方にとって、物足らないと思います。
はい、テスト結果がほとんどありません。

では、あえて失敗したときも試してみましょう

# parameter
URL = 'https://hotel-example-site.takeyaqa.dev/ja/'
EMAIL = 'ichiro@example.com'
PASSWORD = 'password'
USERNAME = '山田三郎'

ログインのUSERNAMEを'山田一郎'から'山田三郎'に変更しました

すると、以下のようにログイン名のEXPECTATIONとACTUALが異なるエラーになることがわかります。

image.png

まあ、テストはできるけど、これだとテスト結果分析とか大変だなーって思います。
そこで少しアップグレードさせます

Pytest

pytestは、Python用のオープンソーステストフレームワークで、シンプルかつ柔軟なテスト作成を可能にします。
豊富なプラグインシステムにより、カバレッジ測定や並列テストなどの追加機能も簡単に利用できます。
ユニットテストからインテグレーションテストまで幅広く対応し、効率的なテスト作成と実行をサポートします

pytestを用いると、もっと充実したテストが可能になります

fixture

fixtureは、pytestにおいてテストの前後に必要な準備や後片付けを行うための関数です。
Setup , teardownに該当します
fixtureは、テスト関数に引数として渡され、自動的に実行されます。これにより、テストコードの再利用性が高まり、テストの可読性も向上します。

今回、共通処理を conftest.py に定義します

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に外だしすることができたため、テスト本体はシンプルになります

test_login_pytest.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

image.png

pytestを行うことで、レポートhtmlも作成されます

image.png

では、失敗した例です

image.png

レポートhtmlはこちら

image.png

また、失敗したときのスクショはこちら

test_login.png

このように、pytestを用いると、スクリプトの保守性を上げ、かつ実行結果作成まで対応してくれる。

ただ、ある程度基盤を作りこまないといけないため、robot frameworkなどのありがたみを感じた。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?