特集
» 2018年03月05日 05時00分 公開

Seleniumの弱点を補うブラウザ自動テスト用フレームワーク「Selenide」とは (3/3)

[今泉俊幸,株式会社ビーブレイクシステムズ]
前のページへ 1|2|3       

【3】テスト対象の要素に対し操作を行う

 操作対象の要素を取得したら、その要素に対しテキスト入力やクリックが行えます。操作する要素の種類に合わせて、SelenideElementの以下のメソッドを利用します。

メソッド名 役割
val(String) テキストボックスに引数の値を設定する。設定前の値はクリアされる
selectRadio(String) 引数で指定したvalueの値を持つラジオを選択する
selectOption(String) 引数で指定した表示名のコンボの候補を選択する(「<option value="1">値1</option>」の「値1」の部分)
selectOptionByValue(String) 引数で指定したvalueを持つコンボの候補を選択する
click() 要素をクリックする
contextClick() 要素を右クリックする

 なお$メソッドを実行した際、指定した要素がHTML中に見つからなくてもエラーにはなりません。返り値のSelenideElementに対し上記のメソッドで操作を行おうとして、要素が存在しなかったり編集不可であったりしてその操作が実行できなかった場合はエラーになります。

 また条件に合致する要素が複数取得できた場合も、エラーにはなりません。条件に合致する要素が複数あるSelenideElementに対し、クリックや値設定のメソッドを呼ぶと、条件に合致した最初の要素が操作されます。

 ただし、ラジオボタンを操作するselectRadioメソッドについては、少し挙動が異なります。selectRadioメソッドは「条件に合致する要素の中で指定のvalueを持つ要素をクリックする」挙動となっています。これは、ラジオボタンは以下のようにname属性を使ってグルーピングされるため、ラジオボタンを取得する際もname属性を指定して複数の要素がヒットするように取得する方が自然だからです。

<input type="radio" name="typeRadio" value="1" class="ui-input" checked/>値1
<input type="radio" name="typeRadio" value="2" class="ui-input" />値2

 ラジオの値を設定する例として下記コードを挙げておきます。

// byNameにより複数の要素が取得されるが、「selectRadio」はその中から指定のvalueを持つ要素を探してクリックする
$(byName("typeRadio")).selectRadio("2");

 条件に合致する要素全てに対し同じ操作を行いたい場合は、「$」メソッドではなく「$$」メソッドを利用し、結果をリストで取得します。「$$」を使ったサンプルコードは下記のようになります。

// スタイルが「.ui-input-text」である要素全てに「2」を設定
for (SelenideElement elem : $$(".ui-input-text")) {
    elem.val("2");
}

【4】操作結果のアサーションを行う

 最後に、要素が指定の値やテキスト、属性などを持っているかの検証(アサーション)を行います。要素の検証はSelenideElementの「should」メソッドを使います。このメソッドの引数に対し、「Condition」という検証内容を表すクラスを渡すことでアサーションを行います。

 ConditionのインスタンスはConditionクラス自身のstaticメソッドを使って生成します。Conditionを作成するメソッドには以下のようなものがあります。

メソッド名、フィールド名 検証する内容
value(String) valueの値が引数で指定した値と等しい
text(String) テキストの値が引数で指定した値と部分一致する
exactText(String) テキストの値が引数で指定した値と完全一致する
attribute(String) 指定の属性を持っている
attribute (String name,String value) nameで指定した属性の値がvalueと等しい
visible 要素が表示されている
exist 要素が存在している(表示、非表示は問わない)
hidden 要素が不可視である、もしくは存在しない
enabled 要素が利用可能である
disabled 要素が利用不能である

 サンプルコードは以下のようになります。

// IDがresultである要素のvalueが3である
$(byId("result")).should(value("3"));

 また、should以外に、「shouldBe」「shouldHave」というメソッドも用意されています。shouldと全く同じ挙動です。これらを利用することで、以下のように英語として自然なテストコードが書けるようになります。

$(byId("result")).shouldHave(attribute("hoge"));

 条件を反転させたい場合は「SelenideElement」の「shouldNot」メソッド、もしくは「Condition」クラスの「not」メソッドを利用できます。以下のコードはいずれも同じ意味になります。

// 「IDがresultである要素」がvisibleである
$(byId("result")).should(visible);
$(byId("result")).shouldNot(hidden);
$(byId("result")).should(not(hidden));

テストコードの例

 上記【1】〜【4】で説明した内容を使った、簡単なWebアプリのテストコードを見てみます。以下Webページのテストを記述する例を考えてみます。

<script type="text/javascript">
    <!--
    function add(){
        var val1 = document.getElementById('value1').value;
        var val2 = document.getElementById('value2').value;
        var added = parseInt(val1) + parseInt(val2);
        document.getElementById('result').value = added;
    }
  -->
</script>
    
<form name="calcForm">
  <div id="inputArea" class="input-area">
    <label id="val1Label" class="ui-label">値1</label>
    <input type="text" id="value1" class="ui-input" />
    <label id="val2Label" class="ui-label">値2</label>
    <input type="text" id="value2" class="ui-input" />
  </div>
  <div id="buttonArea">
    <input type="button" value="加算" id="addButton" onClick="add()">
  </div>
  <div id="resultArea">
    <label id="resultLabel" class="ui-label">計算結果</label>
    <input type="text" id="result" readonly="readonly">
  </div>
</form>

 上記ページをブラウザで表示すると以下のようになります。

 加算ボタンをクリックすると、値1と値2のテキストボックスに入力された値を加算し、計算結果のテキストボックスに設定されます。また、上記ページは基準となるURLから「/calc.html」でアクセスできる想定です。

 上記ページをテストするコードは以下のようになります。

@Test
public void addTest() {
    // 1. テスト対象のURLを開く
    open("/calc.html");
    // 2. テスト対象の要素(ボタンやテキストボックスなど)を取得する
    // 3. テスト対象の要素に対し操作(クリックやキー入力)を行う
    // テキストボックスに値入力
    $(byId("value1")).val("1");
    $(byId("value2")).val("2");
    // 加算ボタン押下
    $(byId("addButton")).click();
    // 4. 操作結果のアサーションを行う
    $(byId("result")).should(value("3"));
}

 とても小さな機能に対するテストコードですが、もしコメントがなくても何をしているかがすぐ分かるような記述となっていると思います。豊富なstaticメソッドを使って、上記のように簡潔にテストコードが書けるのがSelenideの魅力です。

非同期処理への対応

 先ほどの例は、「ボタンを押したら即座に結果が反映されアサーションを実行できる」という前提でした。しかし、例えば「ボタンを押した後にサーバで計算を行い、非同期処理で結果を反映する」こともよくあります。この場合のテストコードを考えてみます。

 実は先ほどのサンプルページの計算処理が非同期処理になったとしても、テストコードは変更する必要がありません。その秘密は、SelenideElementのアサーションメソッドにあります。SelenideElementのアサーションメソッドは、実行時にアサーションに失敗した場合でも、規定の時間に達するまで待ってくれるのです。アサーションを待つ規定の時間はデフォルトでは4秒となっています。

 もし4秒以上かかるような非同期処理がある場合、以下のどちらかの対応を行う必要があります。

アサーションの際に明示的にタイムアウト時間を指定する

 shouldメソッドの代わりに「waitUntil」メソッドを利用すると、第2引数にタイムアウトまでの時間が指定できます。

// valueが3になるまで10000ms(10秒)待つ
$(byId("result")).waitUntil(value("3"), 10000);

 一部の処理だけ重い処理であることが分かっている場合はwaitUntilを使うといいでしょう。

デフォルトのタイムアウト時間を伸ばす

 全体的に非同期処理が重いような場合、Configurationクラスの「timeout」フィールドの値を変更することで、デフォルトのタイムアウト時間を設定できます。指定はミリ秒単位で行います。

 また、実行時引数で「-Dselenide.timeout=10000」のように値を設定することでもタイムアウト時間を変更できます。

スクリーンショットの取得

 テスト中の任意のタイミングでスクリーンショットを取得したい場合、Selenideクラスのstaticメソッドである「screenShot」メソッドが利用できます。

 このメソッドの引数に指定した名称で、メソッド実行時点のWebページの「png形式のスクリーンショット」と「htmlファイル」を保存できます。保存先のディレクトリはデフォルトでは「build/reports/tests」です。

 保存先のディレクトリを変更したい場合は、Configurationクラスの「reportsFolder」フィールドの値を変更する、もしくは実行時引数で「-Dselenide.reportsFolder=reports/selenide」のように値を指定することで変更できます。

 また、テストに失敗した際も、自動でその時点のスクリーンショットとhtmlファイルを保存してくれます。

終わりに

 本稿ではSelenideの基本的な利用方法を紹介しました。Selenideを利用することで、簡潔で読みやすいテストコードが書けることが伝わっていれば幸いです。

 特に、非同期処理を意識せずテストコードが書ける点は、Seleniumに対する大きなアドバンテージです。現在Seleniumを利用している方はぜひSelenideの利用を検討してみてください。

筆者紹介

今泉 俊幸(いまいずみ としゆき)

2011年から、株式会社ビーブレイクシステムズに在籍。

「あるべきものをあるべき姿に」をモットーに、保守しやすいプログラムを書くため日々まい進中。


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。