CLOVER🍀

That was when it all began.

Selenium WebDriverで遊ぶ

仕事で使うかもしれなさそう…ということで、Selenium WebDriverを使ってみました。初めて触ってみたのですが、けっこう便利そうですね。

とりあえず、使ってみる

公式ドキュメントの写経+MavenJUnitと一緒に使ってみることにします。

こんなpom.xmlを用意。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>webdriver.example</groupId>
  <artifactId>webdriver-example</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>webdriver-example</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.0</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
          <fork>true</fork>
        </configuration>
      </plugin>
    </plugins>    
  </build>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>2.28.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

次いで、公式のドキュメントを参考にGoogleへアクセスするコードを実装。
http://seleniumhq.org/docs/03_webdriver.jsp#introducing-the-selenium-webdriver-api-by-example

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class GoogleTest {
    WebDriver driver;

    @Before
    public void setUp() {
        driver = new FirefoxDriver();
    }

    @After
    public void tearDown() {
        driver.quit();
    }

    @Test
    public void Googleでテストしてみる() throws Exception {
        // Googleへアクセス
        driver.get("http://www.google.co.jp/");

        // ページのタイトルを確認
        assertThat(driver.getTitle(), is("Google"));

        // DOMからname=qの要素を探す
        WebElement element = driver.findElement(By.name("q"));
        // 見つけた要素にキーワードを入力してサブミット
        element.sendKeys("Cheese!");
        element.submit();

        // 描画し終わるまで、最大10秒待つ
        // 終了の判定条件は、untilメソッドに渡す内容で記述
        (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver d) {
                    return d.getTitle().toLowerCase().startsWith("cheese!");
                }
            });

        // ページのタイトルを確認
        assertThat(driver.getTitle(), is("cheese! - Google 検索"));
    }
}

WebDriverは、Firefoxのものを使用しています。WebDriverのインスタンスを中心にプログラミングしていく感じなんですね。DOMから要素を抜く時は、Byを使え、と。
WebDriverのインスタンスは、とりあえず@Beforeで初期化して、@Afterで破棄しています。

一応Mavenでやっているので、

$ mvn test

で実行できます。Firefoxが起動してきて、Googleのキーワード入力欄に「Cheese!」と入力して画面遷移、そしてFirefoxが終了するところまでが確認できます。
ちょっと面白いです。

キャプチャを取る

使用しているDriverが、org.openqa.selenium.TakesScreenshotインターフェースを実装していれば、getScreenshotAsメソッドでキャプチャが取得できるようです。

これを使用するように、先ほどのテストケースをコピーして別途作成してみました。

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class GoogleCaptureTest {
    static Path captureDirectory;

    WebDriver driver;

    @BeforeClass
    public static void setUpClass() throws Exception {
        captureDirectory = Paths.get(new SimpleDateFormat("'capture'/yyyyMMddHHmmss").format(new Date()));
        Files.createDirectories(captureDirectory);
    }

    @Before
    public void setUp() {
        driver = new FirefoxDriver();
    }

    @After
    public void tearDown() {
        driver.quit();
    }

    @Test
    public void Googleでキャプチャーしてみる() throws Exception {
        driver.get("http://www.google.co.jp/");

        assertThat(driver.getTitle(), is("Google"));
        capture(driver, "capture1.png");

        WebElement element = driver.findElement(By.name("q"));
        element.sendKeys("Cheese!");
        element.submit();

        (new WebDriverWait(driver, 10)).until(ExpectedConditions.titleContains("Google 検索"));

        assertThat(driver.getTitle(), is("cheese! - Google 検索"));
        capture(driver, "capture2.png");
    }

    public void capture(WebDriver driver, String fileName) throws Exception {
        if (driver instanceof TakesScreenshot) {
            TakesScreenshot screen = (TakesScreenshot)driver;
            Path capture = captureDirectory.resolve(fileName);
            Files.write(capture, screen.getScreenshotAs(OutputType.BYTES));
        }
    }
}

アサーションは、先の例と同じです。あと、WebDriverWaitを使った待機の部分は、ExpectedConditionsを使ってちょっと端折っています。

とりあえず、最初に@BeforeClassでキャプチャ保存用のディレクトリを作っておいて、各画面描画時にキャプチャを取得するようにしています。

キャプチャしているのは、このメソッドですね。

    public void capture(WebDriver driver, String fileName) throws Exception {
        if (driver instanceof TakesScreenshot) {
            TakesScreenshot screen = (TakesScreenshot)driver;
            Path capture = captureDirectory.resolve(fileName);
            Files.write(capture, screen.getScreenshotAs(OutputType.BYTES));
        }
    }

WebDriverのインスタンスがTakesScreenshotにキャスト可能だったらキャストして、TakesScreenshot#getScreenshotAsメソッドでキャプチャを取得します。取得形式は、OutputTypeで指定できるようです。
今回は、java.nio.file.Files#writeにそのまま渡してしまえと思ってOutputType.BYTESを選びました。

実行すると、Mavenプロジェクト直下の「capture/実行日時」ディレクトリ配下にキャプチャができます。あ、形式はpngですね。

ブラウザを起動するので重たいのですが(HtmlUnitDriverを使えという話もあるようですが、キャプチャは取れない…)、けっこう面白いですね、コレ。

参考)
公式ドキュメント
http://seleniumhq.org/docs/03_webdriver.jsp#htmlunit-driver
Javadoc
http://selenium.googlecode.com/svn/trunk/docs/api/java/index.html

http://www.atmarkit.co.jp/ait/articles/1210/05/news104.html
http://www.atmarkit.co.jp/ait/articles/1211/07/news009.html