Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Java | JUnit4 + Selenium WebDriverでWebの自動テストを行う構成の1例

概要

Selenium WebdriverでWebの自動テストを行う構成の1例

準備

Selenium Webdriverによるテスト作成の設定について。

Gradleの場合、build.gradleのdependenciesに以下を追加
compile 'org.seleniumhq.selenium:selenium-java:2.32.0'

手動の場合は下記よりjarを取得してクラスパスに追加します。
http://selenium.googlecode.com/files/selenium-java-2.33.0.zip
http://selenium.googlecode.com/files/selenium-server-2.33.0.zip

selenium-java-2.33.0.jar
selenium-server-standalone-2.33.0.jar

ブラウザのDriver

各ブラウザのDriverには以下のようなものがあります。

Driver Comment
HtmlUnitDriver GUIでブラウザを動かさないため早い。開発用に。画面が表示されないのでキャプチャ不可
FirefoxDriver FireFox用のドライバ ※今動かない
InternetExplorerDriver InternetExplorer用のドライバ
ChromeDriver GoogleChrome用のドライバ

構成

クラス 内容
IntegratedTest.java テストの実行用。複数のシナリオを実行可能
/scenario/.java シナリオ。複数のオペレーションをまとめるもの。
/operation/.java シナリオに含まれる1つのオペレーション。ページの呼び出しとアサーションが主
/pages/.java 1つの画面に対応したクラス。画面に対する操作を共通化することが目的。Seleniumラッパーを利用した画面操作の実装が主
/util/Selenium.java SeleniumDriverをラップして利用

UML

ソースコード

IntegratedTest

IntegratedTest.java

package gr.java_conf.tb.devhelper.integrated;

import gr.java_conf.tb.devhelper.integrated.operation.Operator;
import gr.java_conf.tb.devhelper.integrated.scenario.Scenario;
import gr.java_conf.tb.devhelper.integrated.scenario.SearchTbpgScenarioFactory;
import gr.java_conf.tb.devhelper.integrated.util.Selenium;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;

/**
 * 結合テストシナリオ実行クラス.
 *
 * @author tbpgr
 *
 */
@RunWith(Theories.class)
public class IntegratedTest {
  private static Selenium DRIVER = null;

  @BeforeClass
  public static void classSetup() {
    DRIVER = new Selenium(new HtmlUnitDriver());
  }

  @Before
  public void setUp() {
  }

  @AfterClass
  public static void classTearDown() {
  }

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

  @DataPoints
  public static Scenario[] getScenario() {
    Scenario[] scenarios = {new SearchTbpgScenarioFactory().getScenario(DRIVER, 1, "シナリオタイトル"),};
    return scenarios;
  };

  @Theory
  public void testIntegrated(Scenario scenario) {
    try {
      for (Operator operator : scenario.getOperators()) {
        System.out.println(operator.getOperation());
        operator.operate();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
scenario

Scenario.java

package gr.java_conf.tb.devhelper.integrated.scenario;

import gr.java_conf.tb.devhelper.integrated.operation.Operator;

import java.util.List;

public class Scenario {
  private int caseNo;
  private String caseTitle;
  private List<Operator> operators;

  public Scenario() {

  }

  public int getCaseNo() {
    return caseNo;
  }

  public Scenario caseNo(int caseNo) {
    this.caseNo = caseNo;
    return this;
  }

  public String getCaseTitle() {
    return caseTitle;
  }

  public Scenario caseTitle(String caseTitle) {
    this.caseTitle = caseTitle;
    return this;
  }

  public List<Operator> getOperators() {
    return operators;
  }

  public Scenario operators(List<Operator> operators) {
    this.operators = operators;
    return this;
  }

  @Override
  public String toString() {
    return "Scenario [caseNo=" + caseNo + ", caseTitle=" + caseTitle + ", operators=" + operators + "]";
  }
}

ScenarioFactory.java

package gr.java_conf.tb.devhelper.integrated.scenario;

import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * シナリオ.
 *
 * <pre>
 * テストのシナリオ。
 * {@code getScenarioに1つのシナリオを表す{@code Scenario}を実装したクラスを返却する
 * </pre>
 *
 * @author tbpgr
 *
 */
public interface ScenarioFactory {
  Scenario getScenario(Selenium selenium, int caseNo, String caseTitle);
}

SearchTbpgScenarioFactory.java

package gr.java_conf.tb.devhelper.integrated.scenario;

import gr.java_conf.tb.devhelper.integrated.operation.GoogleTopPageInitialize;
import gr.java_conf.tb.devhelper.integrated.operation.MoveToResultSite;
import gr.java_conf.tb.devhelper.integrated.operation.SearchGoogle;
import gr.java_conf.tb.devhelper.integrated.util.Selenium;

import java.util.Arrays;

/**
 * GoogleでTbpgを検索するシナリオ.
 *
 * <pre>
 * ・トップページを開く
 * ・「tbpg programming memo」で検索を実行する
 * ・検索結果のリンクから「http://d.hatena.ne.jp/tbpg/」へ遷移する
 * </pre>
 *
 * @author tbpgr
 *
 */
public class SearchTbpgScenarioFactory implements ScenarioFactory {

  public Scenario getScenario(Selenium selenium, int caseNo, String caseTitle) {
    Scenario scenario = new Scenario()
      .caseNo(caseNo)
      .caseTitle(caseTitle)
      .operators(
        Arrays.asList(new GoogleTopPageInitialize(selenium), new SearchGoogle(selenium, "tbpg programming memo"),
          new MoveToResultSite(selenium, "programming memo", "http://d.hatena.ne.jp/tbpg")));
    return scenario;
  }
}
Selenium

Selenium.java

package gr.java_conf.tb.devhelper.integrated.util;

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

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

/**
 * Selenium Driverラッパー.
 *
 * @author tbpgr
 *
 */
public class Selenium {
  private WebDriver driver;

  /**
   * Webdriverを指定して初期化.
   *
   * @param driver Webdriver
   */
  public Selenium(WebDriver driver) {
    this.driver = driver;
  }

  /**
   * 指定{@code url}のページをリクエストする.
   *
   * @param url 任意のURL
   */
  public void requestPage(String url) {
    driver.get(url);
  }

  /**
   * 現在のページのURLを取得する.
   *
   */
  public String getUrl() {
    return driver.getCurrentUrl();
  }

  /**
   * IDでDOM要素を取得する.
   *
   * @param id ID
   * @return DOM要素
   */
  public WebElement getElementById(String id) {
    return driver.findElement(By.id(id));
  }

  /**
   * Class名でDOM要素を取得する.
   *
   * @param className Class名
   * @return DOM要素
   */
  public WebElement getElementByClassName(String className) {
    return driver.findElement(By.className(className));
  }

  /**
   * nameでDOM要素を取得する.
   *
   * @param name name
   * @return DOM要素
   */
  public WebElement getElementByName(String name) {
    return driver.findElement(By.name(name));
  }

  /**
   * 指定した{@code name}の要素にvalueをテキストする.
   *
   * @param name name
   * @param value 入力文字列
   */
  public void inputText(String name, String value) {
    WebElement element = driver.findElement(By.name(name));
    element.sendKeys(value);
  }

  /**
   * textの文字列を含むリンクを取得して押下する.
   *
   * @param text リンクに含まれる文字列
   */
  public void link(String text) {
    WebElement link = driver.findElement(By.partialLinkText(text));
    link.click();
  }

  /**
   * Webdriverを停止する.
   */
  public void quit() {
    driver.quit();
  }

  /**
   * 指定したURLと現在のURLが同一か検証する.
   *
   * @param expectedUrl 期待値となるURL
   */
  public void assertSameUrl(String expectedUrl) {
    assertThat(getUrl(), is(expectedUrl));
  }

  /**
   * 指定したURLが現在のURLに含まれるなら真を返す
   *
   * @param expectedUrl 期待値となるURL
   * @return 指定したURLが現在のURLに含まれるなら真,そうでないなら偽
   */
  private boolean isContainUrl(String expectedUrl) {
    return getUrl().contains(expectedUrl);
  }

  /**
   * 指定したURLが現在のURLに含まれるか検証する.
   *
   * @param expectedUrl 期待値となるURL
   */
  public void assertContainUrl(String expectedUrl) {
    assertThat(isContainUrl(expectedUrl), is(true));
  }

}
operation

Operator.java

package gr.java_conf.tb.devhelper.integrated.operation;

/**
 * テストシナリオに含めるオペレーション.
 *
 * @author tbpgr
 *
 */
public interface Operator {
  public String getOperation();

  public void operate();
}

GoogleTopPageInitialize.java

package gr.java_conf.tb.devhelper.integrated.operation;

import gr.java_conf.tb.devhelper.integrated.page.GoogleTopPage;
import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * Googleトップページ初期化のオペレーション.
 *
 * @author tbpgr
 *
 */
public class GoogleTopPageInitialize implements Operator {
  private static final String OPERATION_NAME = "Googleトップページの表示";
  String URL = "http://www.google.co.jp/webhp?hl=ja";
  private Selenium selenium;

  public GoogleTopPageInitialize(Selenium selenium) {
    this.selenium = selenium;
  }

  @Override
  public void operate() {
    GoogleTopPage top = new GoogleTopPage(selenium);
    top.openTop();
    selenium.assertSameUrl(URL);
  }

  @Override
  public String getOperation() {
    return OPERATION_NAME;
  }

}

SearchGoogle.java

package gr.java_conf.tb.devhelper.integrated.operation;

import gr.java_conf.tb.devhelper.integrated.page.GoogleTopPage;
import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * Googleトップページから検索オペレーション.
 *
 * @author tbpgr
 *
 */
public class SearchGoogle implements Operator {
  private static final String OPERATION_NAME = "任意のワードで検索の実行";
  private Selenium selenium;
  private String word;

  public SearchGoogle(Selenium selenium, String word) {
    this.selenium = selenium;
    this.word = word;
  }

  /**
   * Googleトップページで検索を行う.
   */
  @Override
  public void operate() {
    GoogleTopPage top = new GoogleTopPage(selenium);
    top.search(word);
  }

  @Override
  public String getOperation() {
    return OPERATION_NAME;
  }
}

MoveToResultSite.java

package gr.java_conf.tb.devhelper.integrated.operation;

import gr.java_conf.tb.devhelper.integrated.page.SearchPage;
import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * 指定リンク押下オペレーション.
 *
 * @author tbpgr
 *
 */
public class MoveToResultSite implements Operator {
  private static final String OPERATION_NAME = "検索結果の指定ページヘ遷移する";

  private Selenium selenium;
  private String linkText;
  private String expectedUrl;

  public MoveToResultSite(Selenium selenium, String linkText, String expectedUrl) {
    this.selenium = selenium;
    this.linkText = linkText;
    this.expectedUrl = expectedUrl;
  }

  /**
   * {@code linkText}を含むリンクを押下し、移動先が{@code expectedUrl}であることを検証する.
   */
  @Override
  public void operate() {
    SearchPage search = new SearchPage(selenium, linkText);
    search.moveToSite();
    selenium.assertContainUrl(expectedUrl);
  }

  @Override
  public String getOperation() {
    return OPERATION_NAME;
  }
}
page

GoogleTopPage.java

package gr.java_conf.tb.devhelper.integrated.page;

import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * Googleトップページ.
 *
 * <pre>
 * Googleトップページに関わるWeb操作をまとめるクラス。
 * </pre>
 *
 * @author tbpgr
 *
 */
public class GoogleTopPage {
  private static final String GOOGLE_JA_TOP = "http://www.google.co.jp/webhp?hl=ja";
  private static final String GOOGLE_SEARCH_BUTTION_NAME = "btnG";
  private static final String GOOGLE_SEARCH_INPUT_ID = "q";
  private Selenium DRIVER = null;

  public GoogleTopPage(Selenium driver) {
    super();
    DRIVER = driver;
  }

  /**
   * トップページを開く.
   */
  public void openTop() {
    DRIVER.requestPage(GOOGLE_JA_TOP);
  }

  /**
   * {@code word}で検索を行う.
   *
   * @param word 検索ワード
   */
  public void search(final String word) {
    DRIVER.inputText(GOOGLE_SEARCH_INPUT_ID, word);
    DRIVER.getElementByName(GOOGLE_SEARCH_BUTTION_NAME).click();
  }
}

SearchPage.java

package gr.java_conf.tb.devhelper.integrated.page;

import gr.java_conf.tb.devhelper.integrated.util.Selenium;

/**
 * Google検索ページ.
 *
 * <pre>
 * Google検索ページに関わるWeb操作をまとめるクラス。
 * </pre>
 *
 * @author tbpgr
 *
 */
public class SearchPage {
  private Selenium driver = null;
  private String linkText = null;

  public SearchPage(Selenium driver, String linkText) {
    this.driver = driver;
    this.linkText = linkText;
  }

  /**
   * 検索結果の中にあるリンクを押下する.
   */
  public void moveToSite() {
    driver.link(linkText);
  }
}

シーケンス図