Seleniumでラジオボタンをテストを極める

radioボタンのテストはテキストボックスと比べると、難易度がワンランクアップします。

何故かといえば、テキストボックスはinputタグひとつであらゆる要素(ID、名前、入力値)が特定できるのですが、radioボタンはボタン自身と、ボタンが何を表すかのテキスト要素が独立しているからです。

例えば下記のようなradioボタンがあった場合、ユーザーから見れば”男性”を意味するradioボタンは感覚で伝わりますが、Seleniumは感覚で処理できないため、このradioボタンを直接"男性"と判断できません。

<body>
	<form method="POST" action="confirm.php">
		<table border="1">
			<tr>
				<th>性別</th>
				<td>
					<input type="radio" name="sex" value="1">男性
					<input type="radio" name="sex" value="2">女性
				</td>
			</tr>
		</table>
		<input name="submit" type="submit" value="送信">
	</form>
</body>

このようなradioボタンとテキストは何らかの手段で紐づけないとテストができません。

スポンサーリンク

何故ボタンとテキストを紐づけなければいけないのか?

radioボタンとテキストが独立していることはわかりました、で、何でテストできないことにつながるの(。´・ω・)? っていうと、一番は”確認画面の表示が正しいか評価できないから”につきます。

確認画面の表示が正しいか評価できないから

<input type="radio" name="sex" value="1">男性

この場合、POSTした値はvalue=1ですが、確認画面では"男性"と表示しないとユーザーに伝わりません。

Seleniumでは、「value="1"のradioボタンにチェックする」、「チェックした結果、確認画面には"男性"が表示されたか確認する」というテストをしたいはずです。

紐づけする手段をざっくり分けると、

  • 全て把握している前提で紐づけるか
  • HTML構造を参照して紐づけるか
  • そもそも紐づけの必要がないか

に区分されるものと思います。

全て把握している前提で紐づけるか

sex_arr = {"1": "男性", "2": "女性"}

テスト実行前にradioボタンのvalue値とテキストをプログラム上(または外部ファイル)で行います。

Selenium的な工夫は一切必要ありません。

■メリット

HTMLの構造関わらず紐づけができる

■デメリット

radioボタンが増えた場合にメンテナンスが必要になる

HTML構造を参照して紐づけるか

SeleniumによってHTML構造を解析し、紐づけを行います。

■メリット

radioボタンが増えた場合でもメンテナンスが必要ない

■デメリット

HTML構造に変化があると改修が必要な可能性がある

そもそも紐づけの必要がないか

<input type="radio" name="sex" value="男性">男性

そもそも紐付ける必要がないケースもあります。value値とテキストが一致しているケースです。

■メリット

紐づけの必要がない

■デメリット

正規化とか。。。

SeleniumでHTML構造を参照して紐づける・クリックする

radioボタンの表現はWebサイトによって様々、紐づけ情報(連想配列やディクショナリ)に落とし込むためのSelenium処理例をpythonで挙げていきます。

処理例の結果は共通して「{'1': '男性', '2': '女性'}」となります。

radioボタンがlabelタグに囲われているケース

<label><input name="sex" type="radio" value="1">男性</label>
<label><input name="sex" type="radio" value="2">女性</label>

radioボタンがlabelに囲われているケースは簡単です。

  1. radioボタンで、nameがsexを探す
  2. 見つかった要素のvalueをディクショナリのキーに設定する
  3. 見つかった要素から親要素に遡り、親(label)のタグが囲っているテキストをディクショナリの値に設定する

で取得可能です。

elements = driver.find_elements(By.XPATH, "//input[@name='sex'][@type='radio']")  # 性別radioボタンの要素をすべて取得
radio_arr = {}  # 性別のディクショナリを初期化
for elem in elements:
    # キーはradioのvalue、値は親要素に遡りタグ内のテキスト
    radio_arr[elem.get_attribute(("value"))] = elem.find_element_by_xpath("ancestor::label").text

クリックする場合

クリックしてチェックを入れたい場合は、for文を下記のように改修します。

※data["sex"]にはチェックを入れたいradioボタンのvalue値を指定します。

~~~
radio_arr[elem.get_attribute(("value"))] = elem.find_element_by_xpath("ancestor::label").text

# テストデータと一致するラジオボタンがあればチェックする
if elem.get_attribute("value") == data["sex"]:
    elem.click()

radioボタンとlabelタグが独立しているケース

<input id="sex_1" name="sex" type="radio" value="1"><label for="sex_1">男性</label>
<input id="sex_2" name="sex" type="radio" value="2"><label for="sex_2">女性</label>

radioボタンと、テキストを囲むlabelタグが独立している場合、xpathで相対位置で取得することが難しくなります。

しかし独立している場合radioボタンのidとlabelタグのfor属性で紐づけができます。

  1. radioボタンで、nameがsexを探す
  2. 全てのlabelを探す
  3. 上記手順で取得したradio、label要素で2重ループする
  4. radioのidと、labelのfor属性が一致したときディクショナリ化する

elements = driver.find_elements(By.XPATH, "//input[@name='sex'][@type='radio']")  # 性別radioボタンの要素をすべて取得
labels = driver.find_elements(By.XPATH, "//label")
radio_arr = {}  # 性別のディクショナリを初期化
for elem in elements:
    for label in labels:
        if (elem.get_attribute("id") == label.get_attribute("for")):  # idとlabelkが一致したらディクショナリ化
            radio_arr[elem.get_attribute("value")] = label.text

クリックする場合

クリックしてチェックを入れたい場合は、if文中にを下記のように改修します。

※data["sex"]にはチェックを入れたいradioボタンのvalue値を指定します。

~~~
if (elem.get_attribute("id") == label.get_attribute("for")):  # idとlabelkが一致したらディクショナリ化
    radio_arr[elem.get_attribute("value")] = label.text
    # テストデータと一致するラジオボタンがあればチェックする
    if elem.get_attribute("value") == data["sex"]:
        elem.click()

labelがないケース

<input name="sex" type="radio" value="1">男性
<input name="sex" type="radio" value="2">女性

このケースではHTML構造から取得することは諦めたほうがいいと思います。

関係する情報が存在しないからです。プログラム側で連想配列持たせたほうが無難です。

※作りこむ場合はxpathを使って~、親要素から見て~などと考えられますが、男性と女性のテキストを取り出すのがかなり大変です。

確認画面の必須構成

入力した内容が確認画面に表示されているかを確認するため、確認画面には、「どこにradioボタンで選択した値が表示されるのか?」を目印として設定しないと、Seleniumで表示位置が特定できません。

下記のようにspanタグなどで目印を設定すればOK、あとはSeleniumで判定させるだけです。

<th>性別</th>
<td><span name="sex">男性or女性</span></td>

まとめ

radioボタンが含まれるフォームは、labelが使われていることが多いので、基本的にはlabelの書かれ方次第で実装を変えれば難なくSeleniumで参照可能です。

labelタグがないケースは、テキストをクリックしてもチェックが付かないので、最近はあまり見かけないですね、あまり考慮する必要はないと思います。

スポンサーリンク
おすすめの記事