【2024年保存版】SeleniumとJavaで始める自動テスト入門 〜設定から実装まで完全解説

Seleniumとは?Javaでの自動テストを始める前に

目次

目次へ

ブラウザ自動化ツールSeleniumの基本概念を理解しよう

Seleniumは、Webアプリケーションのテスト自動化を実現する強力なツールセットです。2004年に誕生して以来、Webアプリケーションテストの事実上の標準として世界中で使用されています。

Seleniumの基本アーキテクチャ

Seleniumは主に以下のコンポーネントで構成されています:

  1. Selenium WebDriver
  • ブラウザを直接制御するための中核コンポーネント
  • 各ブラウザ専用のドライバーを通じてブラウザを操作
  • クロスブラウザテストを可能にする統一的なインターフェース
  1. Selenium Grid
  • 複数のマシンでの並列テスト実行を実現
  • クラウド環境でのテスト実行もサポート
  1. 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の実装は直感的で理解しやすいものとなっています。

導入時の検討ポイント

  1. プロジェクトの規模と性質
  • 中〜大規模プロジェクトであればJava + Seleniumの組み合わせが最適
  • チームの技術スタックとの整合性を確認
  1. メンテナンス性の考慮
  • 長期的な保守性を重視する場合、Javaの型安全性は大きな利点
  • チーム内でのコード品質維持が容易
  1. 実行環境の要件
  • 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("セットアップ成功!基本的なテストが実行できました。");
    }
}

セットアップ時の注意点

  1. JDKバージョンの確認
  • Java 11以上を推奨
  • JAVA_HOME環境変数の適切な設定
  1. ブラウザバージョンとDriverの互換性
  • WebDriverManagerが自動的に適切なバージョンを選択
  • 手動でDriverをダウンロードする場合は互換性に注意
  1. プロキシ設定
  • 社内ネットワークでの利用時は適切なプロキシ設定が必要
  • WebDriverManagerのプロキシ設定方法
  1. メモリ設定
  • テスト実行時のヒープサイズ設定
  • パラレル実行時のリソース考慮

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複雑な待機条件高度なカスタマイズ実装が複雑

ベストプラクティス

  1. 要素の特定
  • IDを優先的に使用
  • 動的な要素にはXPathを活用
  • CSSセレクタで階層構造を指定
  1. 待機処理
  • 暗黙的待機はグローバルに設定
  • 特定の要素には明示的待機を使用
  • タイムアウト値は適切に設定
  1. エラーハンドリング
  • 要素が見つからない場合の例外処理
  • タイムアウト時の適切なエラーメッセージ

実践的なテストケース実装ガイド

ログインフォームのテスト自動化:完全サンプルコード付き

ページオブジェクトの実装

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>

テスト実行のベストプラクティス

  1. テストの独立性確保
  • 各テストは他のテストに依存しない
  • テストデータは事前に用意
  • テスト終了時に状態をクリーンアップ
  1. 効率的なデータ管理
  • テストデータはExcelやCSVで外部管理
  • 環境依存の設定は設定ファイルで管理
  • 機密情報は暗号化して管理
  1. 実行結果の管理
  • スクリーンショットは日時付きで保存
  • ログは詳細に記録
  • テストレポートは定期的にアーカイブ

メンテナンス性を高めるテストコード設計

ページオブジェクトモデルパターンを活用した構造化の実践

基本構造の実装

// 基底ページクラス
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);
    }
}

設計のベストプラクティス

  1. コンポーネントの分離
  • ページオブジェクトは単一責任の原則に従う
  • 共通のヘッダー/フッターは個別コンポーネントとして実装
  • テストデータとテストロジックを分離
  1. 抽象化のレベル
  • 基本操作は BasePage クラスに実装
  • 共通のユーティリティメソッドは専用クラスに分離
  • 環境固有の設定は設定ファイルで管理
  1. エラーハンドリング
  • 適切な例外処理の実装
  • わかりやすいエラーメッセージ
  • リトライロジックの実装
  1. メンテナンス性の向上
  • 定数の外部化
  • ログ出力の標準化
  • コードの自動フォーマット化
  1. テストの安定性
  • 適切な待機処理の実装
  • 要素の状態確認
  • 環境依存の最小化

このような設計アプローチにより、テストコードの保守性が大幅に向上し、長期的なメンテナンスコストを削減できます。

高度な自動化手法

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>

高度な自動化のベストプラクティス

  1. JavaScript実行
  • 標準的なSeleniumメソッドを優先
  • JavaScriptは必要な場合のみ使用
  • エラーハンドリングを適切に実装
  1. アラート処理
  • タイムアウトを適切に設定
  • 例外処理を確実に実装
  • アラートの存在確認を行う
  1. マルチブラウザテスト
  • ブラウザ固有の挙動を考慮
  • 並列実行時のリソース管理
  • テスト結果の適切な集約

トラブルシューティングとベストプラクティス

よくあるエラーとその解決方法:実例ベースで解説

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
        ));
    }
}

安定性向上のためのベストプラクティス

  1. テスト環境の標準化
  • ブラウザバージョンの統一
  • スクリーン解像度の固定
  • テストデータの独立性確保
  1. エラーハンドリングの強化
  • 例外の適切なキャッチ
  • リトライロジックの実装
  • エラーログの詳細化
  1. パフォーマンス最適化
  • 不要な待機時間の削減
  • リソース使用量の監視
  • 並列実行の最適化
  1. メンテナンス性の向上
  • コードの定期的なリファクタリング
  • 共通処理の抽象化
  • 設定の外部化