PythonとSeleniumを組み合わせたUI自動テストフレームワークの実装方法について解説します。本実装では126メールサービスを対象に、Page Objectパターンを採用した堅牢なテスト基盤を構築します。
開発環境の準備
- Python 3.xのインストール確認
- PyCharmとSeleniumの開発環境構築
- 必要なライブラリのインストール:
pip install pytest pytest-html pypiwin32 openpyxl yagmail - Chrome/Firefoxブラウザと対応ドライバの設定
- メール送信用アカウントの事前設定
プロジェクト構造
PytestAutoTestFrameWork
├── config
│ ├── conf.py
│ └── config.ini
├── data
│ └── tcData.xlsx
├── Page
│ ├── BasePage.py
│ ├── ContactPage.py
│ ├── HomePage.py
│ ├── LoginPage.py
│ └── SendMailPage.py
├── report
├── TestCases
│ ├── test_contactCase.py
│ ├── test_loginCase.py
│ └── test_sendMailCase.py
├── util
│ ├── clipboard.py
│ ├── keyboard.py
│ ├── parseConFile.py
│ ├── parseExcelFile.py
│ └── sendMailForReport.py
├── conftest.py
├── pytest.ini
└── RunTestCase.py
ユーティリティモジュールの実装
添付ファイル操作のためにクリップボードとキーボード操作をシミュレートするモジュールを実装します。
クリップボード操作モジュール
class ClipBoardUtility:
"""Windowsクリップボード操作のユーティリティクラス"""
@staticmethod
def get_content():
"""クリップボード内容の取得"""
WC.OpenClipboard()
content = WC.GetClipboardData(win32con.CF_TEXT)
WC.CloseClipboard()
return content.decode('utf-8') if content else ""
@staticmethod
def set_content(text):
"""クリップボード内容の設定"""
WC.OpenClipboard()
WC.EmptyClipboard()
WC.SetClipboardData(win32con.CF_UNICODETEXT, text)
WC.CloseClipboard()
キーボード操作シミュレーション
class KeyboardSimulator:
"""キーボード操作をシミュレートするクラス"""
KEY_CODES = {
'return': 0x0D,
'tab': 0x09,
'control': 0x11,
'v_key': 0x56,
'a_key': 0x41
}
@classmethod
def press_key(cls, key_name):
"""キーダウン操作"""
key_name = key_name.lower()
if key_name in cls.KEY_CODES:
win32api.keybd_event(cls.KEY_CODES[key_name], 0, 0, 0)
@classmethod
def release_key(cls, key_name):
"""キーアップ操作"""
key_name = key_name.lower()
if key_name in cls.KEY_CODES:
win32api.keybd_event(cls.KEY_CODES[key_name], 0, win32con.KEYEVENTF_KEYUP, 0)
@classmethod
def input_text(cls, text):
"""テキスト入力シミュレーション"""
ClipBoardUtility.set_content(text)
cls.press_key('control')
cls.press_key('v_key')
cls.release_key('v_key')
cls.release_key('control')
テストデータ管理
Excelファイルからテストデータを読み込み、設定ファイルでUI要素を管理します。
Excelパーサーの実装
class ExcelParser:
"""Excelテストデータの解析クラス"""
def __init__(self, file_path):
self.workbook = load_workbook(file_path)
def get_sheet_data(self, sheet_name):
"""シートデータの取得"""
sheet = self.workbook[sheet_name]
return [
tuple(cell.value or "" for cell in row)
for row in sheet.iter_rows(min_row=2)
]
設定ファイルの解析
class ConfigParser:
"""INI設定ファイルの解析クラス"""
def __init__(self, file_path):
self.config = configparser.ConfigParser()
self.config.read(file_path, encoding='utf-8')
def get_locator(self, section, element):
"""ロケータ情報の取得"""
locator_str = self.config.get(section, element)
return tuple(locator_str.split('->')) if '->' in locator_str else locator_str
Page Objectの実装
基本操作をカプセル化したベースページクラスを実装します。
class BasePage:
"""WebDriver操作の基本機能を提供する基底クラス"""
def __init__(self, driver, timeout=30):
self.driver = driver
self.timeout = timeout
self.wait = WebDriverWait(driver, timeout)
def find_element(self, locator):
"""要素の検索(表示待ち込み)"""
by, value = locator
return self.wait.until(
EC.presence_of_element_located((getattr(By, by.upper()), value))
)
def input_text(self, locator, text):
"""テキスト入力操作"""
element = self.find_element(locator)
element.clear()
element.send_keys(text)
def click_element(self, locator):
"""要素クリック操作"""
element = self.wait.until(
EC.element_to_be_clickable(self._convert_locator(locator))
)
element.click()
def switch_to_frame(self, locator):
"""フレーム切り替え操作"""
self.wait.until(
EC.frame_to_be_available_and_switch_to_it(self._convert_locator(locator))
)
テストケースの実装
ログイン機能のテストケースを例に実装方法を示します。
class TestLoginFunction:
"""126メールのログイン機能テスト"""
@pytest.mark.parametrize("username,password,expected",
ExcelParser(DATA_FILE).get_sheet_data('login'))
def test_login_process(self, driver, username, password, expected):
"""ログインフローの検証"""
login_page = LoginPage(driver)
login_page.open()
login_page.enter_credentials(username, password)
if expected == "ログイン成功":
assert login_page.is_logged_in()
else:
assert login_page.get_error_message() == expected
テスト実行とレポート生成
pytestのフック機能を活用して失敗時スクリーンショットを自動添付します。
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item):
"""テスト失敗時のスクリーンショット自動取得"""
outcome = yield
report = outcome.get_result()
if report.when == 'call' and report.failed:
screenshot = driver.get_screenshot_as_base64()
report.extra.append(
pytest_html.extras.image(screenshot, '失敗スクリーンショット')
)
テスト実行スクリプト
一括実行とレポート送信を自動化します。
def execute_tests():
"""テスト実行とレポート送信のワークフロー"""
pytest_args = [
'pytest',
'-v',
'--html=report/test_report.html',
'--self-contained-html'
]
subprocess.run(pytest_args)
# レポートをメールで送信
mail_sender = ReportMailer(
SMTP_SERVER,
SENDER_EMAIL,
SENDER_PASSWORD,
RECIPIENTS
)
mail_sender.send('自動テストレポート', 'テスト結果の詳細は添付ファイルをご覧ください')