Selenium WebDriverでマウス操作 & 処理待機

はじめに

Selenium WebDriverを使えばブラウザを自動操作できます。WebDriverは単純なクリックから複雑なマウス操作や非同期処理の確認などもできます。最近のWebサイトやWebシステムではAjaxが多用されていたり、ドラッグ&ドロップを使用していたりと、結構複雑です。そんな場合のSelenium WebDriverの簡単な使い方をご紹介します。

「Selenium WebDriverとは?」「どうやって使うのか?」と思った方は、簡単・便利、ブラウザの自動操作!~Selenium WebDriver~をご一読下さい。

複数操作

マウスで複数の操作を一連の動きとして実行する場合、どのようにするのか、簡単にやってみます。アシアル・ホームページのトップ画面上部に、下の画像の部分があります。


この部分(スライダー)をマウスでドラッグすると、その下の構築事例や会社情報が切り替わります。このドラッグをSelenium WebDriverで実行してみます。以下のコードを実行すると、スライダーが右に移動し、パネル部分が変化します。


package jp.co.asial.test;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
public class AsialHpSliderTest {
    public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        driver.get("http://www.asial.co.jp");
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        
        // 興味のある技術分野の選択
        Action action = new Actions(driver)
            .clickAndHold(driver.findElement(By.cssSelector(".ui-slider-handle")))
            .moveByOffset(100, 0)
            .release()
            .build();
        action.perform();
    }
}

実際にマウスでスライダーを移動させる場合、マウス・ダウン、ドラッグ、リリースの3つの処理を実行します。このようにマウス操作を同時に複数行う場合、SeleniumではActionsクラスを使います。このクラスは、マウスの基本操作を実行するメソッドを多数保持しています。上記の通りBuilderパターンを使ってインスタンスを作成し、peform()メソッドで一連の処理を実行します。

単純なドラッグ&ドロップのみであれば、以下のようにも記述できます。dragAndDropBy()メソッドはclickAndHold()、moveByOffset()、release()を組み合わせたメソッドです。


Action action = new Actions(driver)
    .dragAndDropBy(driver.findElement(By.cssSelector(".ui-slider-handle")), 100, 0)
    .build();
action.perform();

アニメーション終了確認

先のコードではブラウザを開いたまま処理を終えました。本当はアニメーション終了とともにブラウザを閉じたいのですが、素直には実行できません。例えば、上記コードの最後にdriver.quit()を入れると、アニメーション前にブラウザを閉じてしまいます。ドラッグ&ドロップを終えるとすぐに次の処理を実行してしまうからです。

意図通りに動かしたい場合、処理を中断(待機)し、適切なタイミングで再開する必要があります。こういう場合、WebDriverWaitクラスを用いて待機処理を実装します。

上記パネルの更新は次のようになっています。

  1. パネル要素を画面外へ移動する
  2. 新しい要素を追加する
  3. 古い要素を削除する
  4. 新しいパネルを画面内へ移動する

このパネル更新を確認するためには、①パネルが動き出すまで待ち、②新しい要素が画面内の定位置に来るまで待つ、ことが必要です。これは以下のように実装できます。


package jp.co.asial.test;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
public class AsialHpSliderTest {
    public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        driver.get("http://www.asial.co.jp");
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        
        // スライダーを移動
        Action action = new Actions(driver)
            .dragAndDropBy(driver.findElement(By.cssSelector(".ui-slider-handle")), 100, 0)
            .build();
        action.perform();
        // 画面が動き出すまで待つ: 10秒間、50ms間隔で確認
        new WebDriverWait(driver, 10, 50).until(new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                final WebElement element = driver.findElement(By.cssSelector(".topCardList > article:first-child"));
                return !element.getCssValue("top").equals("0px");
            }
        });
        // 最初の要素の位置が確定するまで待つ: 10秒間、100ms間隔で確認
        new WebDriverWait(driver, 10, 100).until(new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                final WebElement element = driver.findElement(By.cssSelector(".topCardList > article:nth-last-child(21)"));
                return element.getCssValue("top").equals("0px");
            }
        });
        driver.quit();
    }
}

WebDriverWaitコンストラクタの第二、第三引数にはそれぞれ最大待ち時間[秒]と確認間隔時間[ミリ秒]を渡します。そして、WebDriverWaitオブジェクトのuntil()メソッドへExpectedConditionオブジェクトを渡すことで処理を待機します。この仕組みはAjaxなどの非同期処理を確認する際には多用します。ちなみに、アシアル・サイトのパネル操作でもAjax通信を行っています。

実際のところ、上記のコードはたまに失敗してしまいます。2番目の待機で、最初の要素を上手く特定できないことがあるためです。Webサイトの自動テスト等を考える場合、HTMLの構造とJavaScriptの使い方も考える必要がありそうです。適切にIDを割り当てる、Ajaxが終了したら何らかの属性を変化させるなど、ちょっとした処理を入れておくと自動操作もかなり楽になります。もちろん、複雑にならないようなバランス感覚も重要です。

おわりに

マウス操作や処理待機の扱いになれれば、Selenium WebDriverを使ったブラウザ自動操作もかなり楽になります。その他にもダブルクリックや、キーボード入力なども扱えます。かなり万能に操作やテストを実行できますので、ぜひ試してみて下さい。