Seleniumとは?Javaでの自動テストを始める前に
ブラウザ自動化ツールSeleniumの基本概念を理解しよう
Seleniumは、Webアプリケーションのテスト自動化を実現する強力なツールセットです。2004年に誕生して以来、Webアプリケーションテストの事実上の標準として世界中で使用されています。
Seleniumの基本アーキテクチャ
Seleniumは主に以下のコンポーネントで構成されています:
- Selenium WebDriver
- ブラウザを直接制御するための中核コンポーネント
- 各ブラウザ専用のドライバーを通じてブラウザを操作
- クロスブラウザテストを可能にする統一的なインターフェース
- Selenium Grid
- 複数のマシンでの並列テスト実行を実現
- クラウド環境でのテスト実行もサポート
- Selenium IDE
- ブラウザの操作を記録・再生できるChrome/Firefox拡張機能
- テストケース作成の初期段階で活用可能
なぜJavaでSeleniumを使うべきなのか? 3つの決定的な理由
1. 豊富なエコシステムとライブラリ
- TestNG/JUnitとの連携
- 強力なテストフレームワークとの統合が容易
- データ駆動テストの実装が簡単
- テスト実行の並列化がサポート
- Build/依存関係管理
- MavenやGradleによる依存関係の管理が容易
- CIツールとの連携が充実
2. 安定性と保守性
- 静的型付け言語の利点
- コンパイル時のエラーチェック
- IDEのサポートによる高い開発効率
- リファクタリングの容易さ
- デザインパターンの適用
- Page Object Modelの実装が直感的
- テストコードの再利用性が高い
3. エンタープライズでの実績
- 大規模プロジェクトでの採用実績
- 多くの企業での採用実績
- 豊富な技術情報とコミュニティサポート
- セキュリティとコンプライアンス
- エンタープライズ環境での実績
- セキュリティ要件への対応が容易
Javaを使用した実装例
// WebDriverの初期化例
WebDriver driver = new ChromeDriver();
// ページ遷移と要素操作の基本例
driver.get("https://example.com");
WebElement searchBox = driver.findElement(By.id("search"));
searchBox.sendKeys("test automation");
searchBox.submit();
// 明示的な待機処理の例
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement result = wait.until(
ExpectedConditions.presenceOfElementLocated(By.className("result"))
);
このコード例からも分かるように、Javaを使用したSeleniumの実装は直感的で理解しやすいものとなっています。
導入時の検討ポイント
- プロジェクトの規模と性質
- 中〜大規模プロジェクトであればJava + Seleniumの組み合わせが最適
- チームの技術スタックとの整合性を確認
- メンテナンス性の考慮
- 長期的な保守性を重視する場合、Javaの型安全性は大きな利点
- チーム内でのコード品質維持が容易
- 実行環境の要件
- CI/CD環境との親和性
- クロスブラウザテストの必要性
開発環境のセットアップ手順
JavaプロジェクトへのSelenium導入:Maven/Gradleの設定方法
Mavenでの設定
まず、pom.xmlに必要な依存関係を追加します:
<dependencies>
<!-- Selenium WebDriver -->
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.1</version>
</dependency>
<!-- TestNG for testing -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.9.0</version>
<scope>test</scope>
</dependency>
<!-- WebDriverManager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
</dependencies>
Gradleでの設定
build.gradleへの依存関係の追加:
dependencies {
// Selenium WebDriver
implementation 'org.seleniumhq.selenium:selenium-java:4.18.1'
// TestNG
testImplementation 'org.testng:testng:7.9.0'
// WebDriverManager
implementation 'io.github.bonigarcia:webdrivermanager:5.7.0'
}
WebDriver の準備:ChromeDriver/FirefoxDriver の設定
ChromeDriverのセットアップ
WebDriverManagerを使用した最新の方法:
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class ChromeDriverSetup {
public static WebDriver setupChromeDriver() {
// WebDriverManagerがChromeDriverを自動でセットアップ
WebDriverManager.chromedriver().setup();
// Chrome オプションの設定
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized"); // ブラウザを最大化
options.addArguments("--disable-notifications"); // 通知を無効化
// HeadlessモードのためのOptional設定
// options.addArguments("--headless");
// ChromeDriverのインスタンス作成
return new ChromeDriver(options);
}
}
FirefoxDriverのセットアップ
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
public class FirefoxDriverSetup {
public static WebDriver setupFirefoxDriver() {
// WebDriverManagerがGeckoDriverを自動でセットアップ
WebDriverManager.firefoxdriver().setup();
// Firefox オプションの設定
FirefoxOptions options = new FirefoxOptions();
options.addArguments("--start-maximized");
// FirefoxDriverのインスタンス作成
return new FirefoxDriver(options);
}
}
共通のベース設定クラス
テストクラスで共通して使用するベース設定:
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
public class BaseTest {
protected WebDriver driver;
@BeforeClass
public void setUp() {
// Chrome/Firefoxの選択
driver = ChromeDriverSetup.setupChromeDriver();
// または
// driver = FirefoxDriverSetup.setupFirefoxDriver();
// 暗黙的な待機時間の設定
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@AfterClass
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
動作確認用の簡単なテストケース
import org.testng.Assert;
import org.testng.annotations.Test;
public class SampleTest extends BaseTest {
@Test
public void testGoogleSearch() {
// Googleにアクセス
driver.get("https://www.google.com");
// タイトルの確認
String title = driver.getTitle();
Assert.assertTrue(title.contains("Google"));
System.out.println("セットアップ成功!基本的なテストが実行できました。");
}
}
セットアップ時の注意点
- JDKバージョンの確認
- Java 11以上を推奨
- JAVA_HOME環境変数の適切な設定
- ブラウザバージョンとDriverの互換性
- WebDriverManagerが自動的に適切なバージョンを選択
- 手動でDriverをダウンロードする場合は互換性に注意
- プロキシ設定
- 社内ネットワークでの利用時は適切なプロキシ設定が必要
- WebDriverManagerのプロキシ設定方法
- メモリ設定
- テスト実行時のヒープサイズ設定
- パラレル実行時のリソース考慮
Selenium の基本操作をマスターしよう
ブラウザの起動・終了とページ移動の制御方法
基本的なブラウザ操作
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
public class BrowserOperations {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
// ブラウザウィンドウの操作
driver.manage().window().maximize(); // ウィンドウ最大化
driver.manage().window().setSize(new Dimension(1024, 768)); // サイズ指定
driver.manage().window().setPosition(new Point(0, 0)); // 位置指定
// ページ移動
driver.get("https://www.example.com"); // URLに直接アクセス
driver.navigate().to("https://www.example.com"); // 履歴を保持してアクセス
// ブラウザの戻る・進む・更新
driver.navigate().back(); // 戻る
driver.navigate().forward(); // 進む
driver.navigate().refresh(); // 更新
// ブラウザの終了
driver.close(); // 現在のウィンドウを閉じる
driver.quit(); // すべてのウィンドウを閉じてドライバーを終了
}
}
要素の検索と操作:8つの主要なロケーター活用術
1. ID による要素の特定
// 最も推奨される方法:高速で一意
WebElement element = driver.findElement(By.id("login-button"));
2. CSS セレクタによる要素の特定
// 複雑な要素の特定に有用
WebElement element = driver.findElement(By.cssSelector(".login-form input[type='submit']"));
3. XPath による要素の特定
// 動的な要素や複雑な構造の要素を特定
WebElement element = driver.findElement(By.xpath("//button[contains(text(), 'ログイン')]"));
4. クラス名による要素の特定
// 同じクラスの要素をまとめて取得
List<WebElement> elements = driver.findElements(By.className("menu-item"));
5. 要素の操作方法
public class ElementOperations {
public void demonstrateElementOperations(WebDriver driver) {
// テキスト入力
WebElement inputField = driver.findElement(By.id("username"));
inputField.sendKeys("testuser"); // テキスト入力
inputField.clear(); // テキストクリア
// クリック操作
WebElement button = driver.findElement(By.name("login"));
button.click();
// 要素の状態確認
boolean isDisplayed = button.isDisplayed(); // 表示状態
boolean isEnabled = button.isEnabled(); // 有効状態
boolean isSelected = button.isSelected(); // 選択状態
// 要素の属性・テキスト取得
String value = inputField.getAttribute("value");
String text = button.getText();
}
}
待機処理の実装:暗黙的待機と明示的待機の使い分け
1. 暗黙的待機(Implicit Wait)
// ドライバー生成後に一度設定 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
2. 明示的待機(Explicit Wait)
public class WaitOperations {
public void demonstrateWaitOperations(WebDriver driver) {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// 要素が表示されるまで待機
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("dynamic-content"))
);
// 要素がクリック可能になるまで待機
WebElement button = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit-button"))
);
// カスタム条件での待機
wait.until(driver -> {
WebElement elem = driver.findElement(By.id("status"));
return elem.getText().contains("完了");
});
}
}
3. Fluent Wait の活用
// より柔軟な待機設定
Wait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofSeconds(5))
.ignoring(NoSuchElementException.class);
WebElement element = wait.until(driver -> {
return driver.findElement(By.id("dynamic-element"));
});
待機処理の使い分けガイド
| 待機タイプ | 使用シーン | メリット | デメリット |
|---|---|---|---|
| 暗黙的待機 | 基本的な要素待機 | 設定が簡単 | 柔軟性に欠ける |
| 明示的待機 | 特定条件での待機 | 細かい制御が可能 | コードが冗長 |
| Fluent Wait | 複雑な待機条件 | 高度なカスタマイズ | 実装が複雑 |
ベストプラクティス
- 要素の特定
- IDを優先的に使用
- 動的な要素にはXPathを活用
- CSSセレクタで階層構造を指定
- 待機処理
- 暗黙的待機はグローバルに設定
- 特定の要素には明示的待機を使用
- タイムアウト値は適切に設定
- エラーハンドリング
- 要素が見つからない場合の例外処理
- タイムアウト時の適切なエラーメッセージ
実践的なテストケース実装ガイド
ログインフォームのテスト自動化:完全サンプルコード付き
ページオブジェクトの実装
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
private WebDriver driver;
// ページ要素の定義
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(id = "login-button")
private WebElement loginButton;
@FindBy(css = ".error-message")
private WebElement errorMessage;
// コンストラクタ
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
// ページ操作メソッド
public void enterUsername(String username) {
usernameInput.clear();
usernameInput.sendKeys(username);
}
public void enterPassword(String password) {
passwordInput.clear();
passwordInput.sendKeys(password);
}
public void clickLoginButton() {
loginButton.click();
}
public String getErrorMessage() {
return errorMessage.getText();
}
// 統合メソッド
public void login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLoginButton();
}
}
テストケースの実装
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.io.File;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.apache.commons.io.FileUtils;
public class LoginTest extends BaseTest {
private LoginPage loginPage;
@BeforeMethod
public void setUp() {
super.setUp();
driver.get("https://example.com/login");
loginPage = new LoginPage(driver);
}
@Test(description = "正常系:有効なクレデンシャルでログイン")
public void testSuccessfulLogin() {
loginPage.login("validUser", "validPass");
assertTrue(driver.getCurrentUrl().contains("/dashboard"));
}
@Test(description = "異常系:無効なクレデンシャルでログイン")
public void testFailedLogin() {
loginPage.login("invalidUser", "invalidPass");
assertEquals(loginPage.getErrorMessage(), "Invalid credentials");
}
// スクリーンショット撮影用のユーティリティメソッド
private void captureScreenshot(String fileName) {
try {
TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(source, new File("./screenshots/" + fileName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
データ駆動テストの実装:TestNG を活用した効率的なテスト設計
テストデータの準備(testng.xml)
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Login Test Suite">
<test name="Login Tests">
<classes>
<class name="com.example.tests.LoginDataDrivenTest"/>
</classes>
</test>
</suite>
データプロバイダーの実装
public class LoginDataProvider {
@DataProvider(name = "loginData")
public Object[][] getLoginData() {
return new Object[][] {
{"user1", "pass1", true, "Valid user login"},
{"user2", "wrong", false, "Invalid password"},
{"", "pass1", false, "Empty username"},
{"user1", "", false, "Empty password"}
};
}
}
データ駆動テストの実装
public class LoginDataDrivenTest extends BaseTest {
private LoginPage loginPage;
@BeforeMethod
public void setUp() {
super.setUp();
loginPage = new LoginPage(driver);
}
@Test(dataProvider = "loginData", dataProviderClass = LoginDataProvider.class)
public void testLogin(String username, String password,
boolean expectedSuccess, String testCase) {
// テスト実行前のログ
logger.info("実行中のテストケース: " + testCase);
try {
loginPage.login(username, password);
if (expectedSuccess) {
assertTrue(driver.getCurrentUrl().contains("/dashboard"));
} else {
assertTrue(loginPage.getErrorMessage().length() > 0);
}
} catch (Exception e) {
// スクリーンショット撮影
captureScreenshot(testCase.replaceAll("\\s+", "_") + ".png");
throw e;
}
}
}
スクリーンショット取得と実行結果レポートの作成方法
カスタムテストリスナーの実装
import org.testng.ITestListener;
import org.testng.ITestResult;
public class TestListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
// テスト失敗時のスクリーンショット
Object currentClass = result.getInstance();
WebDriver driver = ((BaseTest) currentClass).getDriver();
String testMethodName = result.getMethod().getMethodName();
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = testMethodName + "_" + timestamp + ".png";
captureScreenshot(driver, fileName);
}
private void captureScreenshot(WebDriver driver, String fileName) {
try {
TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);
File destination = new File("test-output/screenshots/" + fileName);
FileUtils.copyFile(source, destination);
} catch (Exception e) {
e.printStackTrace();
}
}
}
レポート設定(pom.xml)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
テスト実行のベストプラクティス
- テストの独立性確保
- 各テストは他のテストに依存しない
- テストデータは事前に用意
- テスト終了時に状態をクリーンアップ
- 効率的なデータ管理
- テストデータはExcelやCSVで外部管理
- 環境依存の設定は設定ファイルで管理
- 機密情報は暗号化して管理
- 実行結果の管理
- スクリーンショットは日時付きで保存
- ログは詳細に記録
- テストレポートは定期的にアーカイブ
メンテナンス性を高めるテストコード設計
ページオブジェクトモデルパターンを活用した構造化の実践
基本構造の実装
// 基底ページクラス
public abstract class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
PageFactory.initElements(driver, this);
}
// 共通の待機処理
protected WebElement waitForElementVisible(By locator) {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
}
protected WebElement waitForElementClickable(By locator) {
return wait.until(ExpectedConditions.elementToBeClickable(locator));
}
// 共通のページ操作
protected void click(WebElement element) {
waitForElementClickable(element);
element.click();
}
protected void type(WebElement element, String text) {
waitForElementVisible(element);
element.clear();
element.sendKeys(text);
}
}
// ヘッダーコンポーネント
public class HeaderComponent {
@FindBy(id = "search")
private WebElement searchBox;
@FindBy(id = "user-menu")
private WebElement userMenu;
public void search(String keyword) {
searchBox.sendKeys(keyword);
searchBox.submit();
}
public void openUserMenu() {
userMenu.click();
}
}
// 特定のページクラス
public class DashboardPage extends BasePage {
@FindBy(css = ".dashboard-stats")
private WebElement statsSection;
private HeaderComponent header;
public DashboardPage(WebDriver driver) {
super(driver);
this.header = new HeaderComponent();
PageFactory.initElements(driver, header);
}
public boolean isStatsDisplayed() {
return statsSection.isDisplayed();
}
}
共通処理の抽象化:カスタムメソッド作成のベストプラクティス
要素操作の抽象化
// カスタム要素操作クラス
public class ElementOperations {
private WebDriver driver;
private WebDriverWait wait;
public ElementOperations(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public void safeClick(WebElement element) {
try {
wait.until(ExpectedConditions.elementToBeClickable(element));
element.click();
} catch (ElementClickInterceptedException e) {
((JavascriptExecutor) driver).executeScript("arguments[0].click();", element);
}
}
public void scrollIntoView(WebElement element) {
((JavascriptExecutor) driver).executeScript(
"arguments[0].scrollIntoView({ behavior: 'smooth', block: 'center' });",
element
);
}
}
// カスタムアサーションクラス
public class CustomAssertions {
private WebDriver driver;
private WebDriverWait wait;
public void assertElementPresent(By locator, String message) {
try {
wait.until(ExpectedConditions.presenceOfElementLocated(locator));
} catch (TimeoutException e) {
throw new AssertionError(message);
}
}
public void assertTextPresent(WebElement element, String expectedText) {
wait.until(ExpectedConditions.textToBePresentInElement(element, expectedText));
}
}
データ処理の抽象化
// テストデータ管理クラス
public class TestDataManager {
private Properties testData;
public TestDataManager() {
loadTestData();
}
private void loadTestData() {
testData = new Properties();
try (InputStream input = getClass().getClassLoader()
.getResourceAsStream("testdata.properties")) {
testData.load(input);
} catch (IOException e) {
throw new RuntimeException("テストデータの読み込みに失敗しました", e);
}
}
public String getData(String key) {
return testData.getProperty(key);
}
}
設計のベストプラクティス
- コンポーネントの分離
- ページオブジェクトは単一責任の原則に従う
- 共通のヘッダー/フッターは個別コンポーネントとして実装
- テストデータとテストロジックを分離
- 抽象化のレベル
- 基本操作は BasePage クラスに実装
- 共通のユーティリティメソッドは専用クラスに分離
- 環境固有の設定は設定ファイルで管理
- エラーハンドリング
- 適切な例外処理の実装
- わかりやすいエラーメッセージ
- リトライロジックの実装
- メンテナンス性の向上
- 定数の外部化
- ログ出力の標準化
- コードの自動フォーマット化
- テストの安定性
- 適切な待機処理の実装
- 要素の状態確認
- 環境依存の最小化
このような設計アプローチにより、テストコードの保守性が大幅に向上し、長期的なメンテナンスコストを削減できます。
高度な自動化手法
JavaScript実行とアラート処理の実装方法
JavaScriptExecutorの活用
public class JavaScriptOperations {
private WebDriver driver;
private JavascriptExecutor js;
public JavaScriptOperations(WebDriver driver) {
this.driver = driver;
this.js = (JavascriptExecutor) driver;
}
// 要素の可視性変更
public void makeElementVisible(WebElement element) {
js.executeScript(
"arguments[0].style.display='block';",
element
);
}
// スクロール操作
public void smoothScrollToElement(WebElement element) {
js.executeScript(
"arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});",
element
);
}
// 動的コンテンツの操作
public void modifyDOMContent(String elementId, String newContent) {
js.executeScript(
"document.getElementById('" + elementId + "').innerHTML='" + newContent + "';"
);
}
// 非表示要素のクリック
public void clickHiddenElement(WebElement element) {
js.executeScript("arguments[0].click();", element);
}
}
アラート処理の実装
public class AlertHandler {
private WebDriver driver;
private Alert alert;
public AlertHandler(WebDriver driver) {
this.driver = driver;
}
// 基本的なアラート処理
public void handleBasicAlert() {
alert = driver.switchTo().alert();
alert.accept();
}
// 確認ダイアログの処理
public void handleConfirmAlert(boolean accept) {
alert = driver.switchTo().alert();
if (accept) {
alert.accept();
} else {
alert.dismiss();
}
}
// プロンプトアラートの処理
public void handlePromptAlert(String input) {
alert = driver.switchTo().alert();
alert.sendKeys(input);
alert.accept();
}
// アラートテキストの取得
public String getAlertText() {
alert = driver.switchTo().alert();
return alert.getText();
}
}
複数ブラウザでのマルチブラウザテスト実行テクニック
ブラウザ設定ファイル(browsers.xml)
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Multi Browser Test Suite" parallel="tests" thread-count="3">
<test name="Chrome Tests">
<parameter name="browser" value="chrome"/>
<classes>
<class name="com.example.tests.CrossBrowserTest"/>
</classes>
</test>
<test name="Firefox Tests">
<parameter name="browser" value="firefox"/>
<classes>
<class name="com.example.tests.CrossBrowserTest"/>
</classes>
</test>
<test name="Edge Tests">
<parameter name="browser" value="edge"/>
<classes>
<class name="com.example.tests.CrossBrowserTest"/>
</classes>
</test>
</suite>
マルチブラウザテストの実装
public class CrossBrowserTest {
private WebDriver driver;
private BrowserFactory browserFactory;
@BeforeMethod
@Parameters("browser")
public void setUp(String browser) {
browserFactory = new BrowserFactory();
driver = browserFactory.createDriver(browser);
}
@Test
public void testAcrossMultipleBrowsers() {
driver.get("https://example.com");
// テスト実装
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
// ブラウザファクトリークラス
public class BrowserFactory {
public WebDriver createDriver(String browserType) {
switch (browserType.toLowerCase()) {
case "chrome":
WebDriverManager.chromedriver().setup();
return new ChromeDriver();
case "firefox":
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver();
case "edge":
WebDriverManager.edgedriver().setup();
return new EdgeDriver();
default:
throw new IllegalArgumentException("Unsupported browser type: " + browserType);
}
}
}
並列実行の設定(pom.xml)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>browsers.xml</suiteXmlFile>
</suiteXmlFiles>
<properties>
<property>
<name>parallel</name>
<value>methods</value>
</property>
<property>
<name>threadCount</name>
<value>3</value>
</property>
</properties>
</configuration>
</plugin>
</plugins>
</build>
高度な自動化のベストプラクティス
- JavaScript実行
- 標準的なSeleniumメソッドを優先
- JavaScriptは必要な場合のみ使用
- エラーハンドリングを適切に実装
- アラート処理
- タイムアウトを適切に設定
- 例外処理を確実に実装
- アラートの存在確認を行う
- マルチブラウザテスト
- ブラウザ固有の挙動を考慮
- 並列実行時のリソース管理
- テスト結果の適切な集約
トラブルシューティングとベストプラクティス
よくあるエラーとその解決方法:実例ベースで解説
1. 要素特定の問題
public class ElementTroubleshooting {
private WebDriver driver;
private WebDriverWait wait;
// StaleElementReferenceException対策
public void handleStaleElement(By locator, Consumer<WebElement> action) {
int maxAttempts = 3;
int attempts = 0;
while (attempts < maxAttempts) {
try {
WebElement element = wait.until(
ExpectedConditions.presenceOfElementLocated(locator)
);
action.accept(element);
break;
} catch (StaleElementReferenceException e) {
attempts++;
if (attempts == maxAttempts) {
throw e;
}
}
}
}
// ElementClickInterceptedException対策
public void safeClick(WebElement element) {
try {
element.click();
} catch (ElementClickInterceptedException e) {
// スクロールして要素を表示
((JavascriptExecutor) driver).executeScript(
"arguments[0].scrollIntoView(true);",
element
);
// 少し待機
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
// JavaScript経由でクリック
((JavascriptExecutor) driver).executeScript(
"arguments[0].click();",
element
);
}
}
}
2. タイミング関連の問題
public class TimingHandler {
private WebDriver driver;
private WebDriverWait wait;
// 動的要素の待機
public WebElement waitForDynamicElement(By locator) {
return wait.until(ExpectedConditions.refreshed(
ExpectedConditions.presenceOfElementLocated(locator)
));
}
// AJAX完了の待機
public void waitForAjaxComplete() {
wait.until(driver -> {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (Boolean) js.executeScript(
"return jQuery.active == 0"
);
});
}
// ページロード完了の待機
public void waitForPageLoad() {
wait.until(driver -> ((JavascriptExecutor) driver)
.executeScript("return document.readyState")
.equals("complete"));
}
}
テスト実行の安定性を高める7つのテクニック
1. リトライメカニズムの実装
public class RetryMechanism implements IRetryAnalyzer {
private int retryCount = 0;
private static final int MAX_RETRY_COUNT = 3;
@Override
public boolean retry(ITestResult result) {
if (retryCount < MAX_RETRY_COUNT) {
retryCount++;
return true;
}
return false;
}
}
// テストクラスでの使用例
@Test(retryAnalyzer = RetryMechanism.class)
public void unstableTest() {
// テスト実装
}
2. カスタムウェイト処理
public class CustomWaits {
private WebDriver driver;
public void waitForCondition(Function<WebDriver, Boolean> condition,
String message) {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.withMessage(message)
.pollingEvery(Duration.ofMillis(500))
.ignoring(StaleElementReferenceException.class)
.until(condition);
}
// カスタム条件の例
public void waitForElementToBeStable(WebElement element) {
Point initialLocation = element.getLocation();
waitForCondition(driver -> {
Point currentLocation = element.getLocation();
return currentLocation.equals(initialLocation);
}, "Element location is not stable");
}
}
3. スクリーンショット管理
public class ScreenshotManager {
private WebDriver driver;
public void captureScreenshot(String testName, String status) {
try {
TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
String fileName = String.format(
"screenshot_%s_%s_%s.png",
testName,
status,
timestamp
);
FileUtils.copyFile(
source,
new File("test-output/screenshots/" + fileName)
);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. ログ管理の強化
public class TestLogger {
private static final Logger logger = LogManager.getLogger(TestLogger.class);
public static void logTestStep(String step) {
logger.info("実行ステップ: " + step);
}
public static void logError(String message, Exception e) {
logger.error("エラー発生: " + message, e);
}
public static void logTestResult(String testName, String result) {
logger.info(String.format(
"テスト結果 - %s: %s",
testName,
result
));
}
}
安定性向上のためのベストプラクティス
- テスト環境の標準化
- ブラウザバージョンの統一
- スクリーン解像度の固定
- テストデータの独立性確保
- エラーハンドリングの強化
- 例外の適切なキャッチ
- リトライロジックの実装
- エラーログの詳細化
- パフォーマンス最適化
- 不要な待機時間の削減
- リソース使用量の監視
- 並列実行の最適化
- メンテナンス性の向上
- コードの定期的なリファクタリング
- 共通処理の抽象化
- 設定の外部化