Seleniumでcheckboxのテストを極める

checkboxのテストの流れはradioボタンと似ていますが、特別気にかける必要があるとすれば、

「どうやって複数のcheckboxを選択させるか?」

「どうやって確認画面で複数の入力内容の正否を評価するのか?」

ということでしょうか、複数選択できるということがテストの難易度をワンランク挙げてきますね。

結論から言えば、

「入力画面のlabelタグの活用は必須、確認画面では個別に目印となるタグを付けよ!」

です。

<body>
	<form method="POST" action="confirm.php">
		<table border="1">
			<tr>
				<th>どんなSNSを利用してますか?</th>
				<td>
					<input id="sns_1" name="sns[]" type="checkbox" value="1" ><label for="sns_1">Twitter</label><br>
					<input id="sns_2" name="sns[]" type="checkbox" value="2" ><label for="sns_2">Facebook</label><br>
					<input id="sns_3" name="sns[]" type="checkbox" value="3" ><label for="sns_3">Instagram</label>
				</td>
			</tr>
		</table>
		<input name="submit" type="submit" value="送信">
	</form>
</body>

スポンサーリンク

checkboxに関係する構成要素を整理する

radioボタンと同じですが、まずcheckboxの構成要素を整理します。

ここでいう構成要素とは、「checkboxのvalue値」、「checkboxを表すテキスト」の2つです。

以下のようなcheckboxが存在した場合、checkboxと、その右に存在するテキストが一つの組み合わせであることを明確化します。

何故構成要素を整理する必要があるのか?

構成要素を整理する理由は、主に「どうやって確認画面で複数の入力内容の正否を評価するのか?」を解決するためです。

具体的には下記2点をテストするために必要となります。

確認画面から戻った時、checkboxがチェック済みであるか

入力画面で表示されているテキストと、確認画面委表示されているテキストが一致するか

構成要素を取りまとめる手段としては、下記が考えられますが、、、

  • Python側で整理済みの構成要素を保持しておく
  • HTML構造をSeleniumで解析する
  • 構成要素を管理する必要のないHTML構造にする

ここではSeleniumによって解析する場合の手順について解説していきます。

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

radioボタンと同様、紐づけ情報(連想配列やディクショナリ)に落とし込みます。

HTML構造の想定はradioボタンと同様で考えます。

checkboxがlabelタグに囲われているケース

<label><input name="sns[]" type="checkbox" value="1">Twitter</label><br>
<label><input name="sns[]" type="checkbox" value="2">Facebook</label><br>
<label><input name="sns[]" type="checkbox" value="3">Instagram</label>

checkboxがlabelに囲われているケースは簡単です。

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

で取得可能です。

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

クリックする場合

クリックしてチェックを入れたい場合は、if文を追記します。

※data["sns"]にはチェックを入れたいcheckboxのvalue値を指定します。

※一つしかチェックしない場合"1"や"2"とし、複数チェックする場合は"1,2"とカンマ区切りにします。

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

checkboxとlabelタグが独立しているケース

<input id="sns_1" name="sns[]" type="checkbox" value="1"><label for="sns_1">Twitter</label><br>
<input id="sns_2" name="sns[]" type="checkbox" value="2"><label for="sns_2">Facebook</label><br>
<input id="sns_3" name="sns[]" type="checkbox" value="3"><label for="sns_3">Instagram</label>

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

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

  1. checkboxで、nameがsnsを探す
  2. 全てのlabelを探す
  3. 上記手順で取得したcheckbox、label要素で2重ループする
  4. checkboxのidと、labelのfor属性が一致したときディクショナリ化する
elements = driver.find_elements(By.XPATH, "//input[@name='sns[]'][@type='checkbox']")  # snsのchexkbox要素をすべて取得
labels = driver.find_elements(By.XPATH, "//label")
checkbox_arr = {}  # ディクショナリを初期化
for elem in elements:
    for label in labels:
        if (elem.get_attribute("id") == label.get_attribute("for")):  # idとlabelkが一致したらディクショナリ化
            checkbox_arr[elem.get_attribute("value")] = label.text

クリックする場合

クリックしてチェックを入れたい場合は、if文にさらにif文を追加します。

※data["sns"]にはチェックを入れたいcheckboxのvalue値を指定します。

※一つしかチェックしない場合"1"や"2"とし、複数チェックする場合は"1,2"とカンマ区切りにします。

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

labelがないケース

<input name="sns[]" type="checkbox" value="1" >Twitter
<input name="sns[]" type="checkbox" value="2" >Facebook
<input name="sns[]" type="checkbox" value="3" >Instagram

radioボタン同様、HTML構造から取得することが困難なので、諦めてlabelタグを貼ったほうが無難かと思います。

確認画面の必須構成

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

そのため下記のようにspanタグなどで目印を設定すればOK、checkboxで複数選択される可能性があるので、id属性にvalue値を加えて特定できるようにしておきます。

<span id='sns_1'>Twitter</span>
<span id='sns_3'>Instagram</span>

python側の判定

  1. テストデータをカンマ区切りでテキスト化されたdata["sns"]でループする
  2. data["sns"]から取り出したIDを部分的に持つspanタグを探す
  3. IDが見つかったら、spanタグで囲っているテキストと、入力画面のテキストが一致しているか判定する
  4. 見つかったらその旨を通知

for sns_id in data["sns"].split(","): #1
    if len(driver.find_elements_by_id("sns_"+sns_id)) > 0: #2
        if checkbox_arr[sns_id] == driver.find_element_by_id("sns_" + sns_id).text: #3
            print(driver.find_element_by_id("sns_" + sns_id).text + "は見つかりました\n")
            continue
    print(driver.find_element_by_id("sns_" + sns_id).text + "は見つかりませんでした\n")

※checkbox_arrは事前のpython処理により、checkboxのvalue値と該当テキストがディクショナリとして保持しています

まとめ

複数を選択できるという点で、チェックする難易度がやや上がりました。

やはり前提としては目印となるIDやlabel付けが必須になります。

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