こんにちは、kureyです。


今回は私がSeleniumとJavaを使ってWebアプリケーションの画面テストをする際に気を付けたことを3つご紹介します。



Seleniumとは?


Seleniumとは、ブラウザのテストで用いられるフレームワークです。


  1. ブラウザに表示されているテキストなどの要素が正しく表示されているか?

  2. ボタンをクリックすることで遷移する先のURLが正しいか?

などのサイト上の動作を様々な言語1で記述されたプログラムによりテストする事ができます。


これは世に出ている様々なブラウザ上で自分のサイトが正しく動作しているか、 定期的にテストするためによく使われます。




1. Seleniumでテストする範囲を決める


1つ目に気を付けるべき点はSeleniumでテストする範囲を決めることです。


最初から全ての画面の全ての操作をSeleniumで自動化しようとすると、 テストコードを書くコスト物凄くかかる上、メンテナンスも大変となります。


私がSeleniumでテストを行った時は各画面で必ず存在するようなヘッダーやフッターの表示、 メニューなど共通の画面で利用できるもの、ブラウザリロードやブラウザバックした時の動作、 それぞれが想定通りの動作となっているかという点に観点を絞り、テストの範囲を最小限にしました。


このように自動化する範囲を各画面で必ず行うような操作に限定する事でテストコードは汎用的にしやすく、 プログラムに落とし込むコストは大幅に削減でき、最小限のコストで最大限のパフォーマンスを発揮する事ができます。




2. テストコードを書く上でルールを決める


2つ目はテストコードを書く上でもコーディングルールを決めることです。


複数人でテストを書く場合、ルールを決めておかなければそれぞれが好き勝手書いてしまい、 せっかくテストする範囲を限定したにも関わらず煩雑なテストコードが生まれてしまいます。


煩雑なテストコードとは、FirefoxのSelenium IDEを利用して画面上の操作をキャプチャし、 それをそのままテストコードとして利用する場合が該当します。


FirefoxのSelenium IDE自体は画面上の操作を記録してテストコードを生成してくれるため、 テストコードを作る上ではとても早いですが、テストコードの再利用が出来ないことや、 画面変更が発生した際にSelenium IDEで操作を記録してテストコードを生成する必要が出てきてしまいます。


通常のプログラム同様、ある一定のルールを用いて開発を進めないと 保守性の低いアプリケーションが完成しますよね?

たかがテストコード、されどテストコード。

アプリケーションの品質を高い保つためにはテストコードもクリーンにすることをオススメします。



ページオブジェクトパターンというデザインパターン


ここでご紹介するのはページオブジェクトパターンというデザインパターンです。


ページオブジェクトパターンとは、テストコードを記述する上でWebアプリケーション上の画面1つを オブジェクトとして捉えるデザインパターンの1種です。


Seleniumの公式でも推奨されている保守性の高いテストコードの書き方となります。


以下、公式に記載されているPageObjectの原則

・The public methods represent the services that the page offers(publicメソッドは、ページが提供するサービスを表す)

・Try not to expose the the internals of the page (ページの内部を公開しないこと)

・Generally don’t make assertions (原則としてassertionを行わないこと)

・Methods return other PageObjects (メソッドは他のPageObjectsを返す)

・Need not represent an entire page (ページ全体を表す必要はない)

・Different results for the same action are modelled as different as different methods (同じアクションに対して異なる結果となる場合には異なるメソッドとしてモデル化する)


このデザインパターンで書かれたテストコードの利点は画面変更が発生した場合にテストコードの修正が最小限で済むことにあります。




3. テストすべき内容をインターフェースとして定義


3つ目は各画面で共通して必ずテストするものをインターフェースとして定義することです。


デザインパターンである程度テストコードの統一性は生まれますが、チェックすべきテストを書く・書かないはその人次第になってしまう事があります。

私はそれを防ぐために必要なチェック項目をインターフェースとして定義する事でテストの漏れをできる限り少なくなるようにしました。

public interface MyTestCaseInterface {
    public void testCheckUrl();          // URLのチェック
    public void testCheckPageTitle();    // ページタイトルのチェック
    etc...
}




サンプルコード


以下は参考までに私がテストを行ったときのパッケージ構成とテストコードのサンプルとなります。


パッケージ構成


jp.co.isoroot.sample.page        // ページオブジェクトクラス
jp.co.isoroot.sample.scenario    // シナリオクラス
jp.co.isoroot.sample.fixture     // データクラス


ページオブジェクトクラス


画面1つ1つをクラスとして定義します。


また各画面で共通の部分がある場合、ベースクラスを定義して各画面で継承させると良いでしょう。 ただし、なんでもかんでも共通化しようとしてベースクラスが肥大化しすぎないように注意が必要です!


以下はログインページとトップページのサンプルコードです。

public class PageBase {
    protected WebDriver driver;
    public PageBase (WebDriver _driver) {
        this.driver = _driver;
    }
    /**
     * <title>要素のテキスト取得
     */
    public String getPageTitle() {
        return driver.findElement(By.tagName("title")).getText();
    }
    /**
     * URL取得
     */
    public String getUrl() {
        return driver.getCurrentUrl();
    }
}
public class LoginPage extends PageBase {
    public LoginPage(WebDriver _driver) {
        super(_driver);
    }

    /**
     * ログインページにURLアクセス
     */
    public HogePage access() {
        driver.get(ログインページURL);
        return this;
    }

    /**
     * ログインボタンクリック
     */
    public TopPage clickLogin(String _id, String _pass) {
        driver.findElement(ログインID入力フォーム).sendKeys(_id);
        driver.findElement(ログインパスワード入力フォーム).sendKeys(_pass);
        driver.findElement(ログインボタン).submit();
        return new TopPage(driver);
    }
}
public class TopPage extends PageBase {
    public TopPage(WebDriver _driver) {
        super(_driver);
    }

    /**
     * トップページにURLアクセス
     */
    public TopPage access() {
        driver.get(トップページURL);
        return this;
    }
    /**
     * ログアウトボタンクリック
     */
    public LoginPage clickLogout() {
        driver.findElement(ログアウトボタン).click();
        return new LoginPage(driver);
    }
 }


シナリオクラス


このクラスではページオブジェクトクラスを利用してテストするシナリオを定義します。


以下はログインページのURLとページタイトルをチェックするサンプルコードです。

public LoginPageTest implements MyOperationTestCaseInterface {
    WebDriver driver;
    public LoginPageTest() {
        driver = new ChromeDriver();
    }
    @Test
    @Override
    public void testCheckUrl() {
        LoginPage _login = new LoginPage(driver).access();
        assertTrue(_login.getUrl().equals(ログインページURL));
    }
    @Test
    @Override
    public void testCheckPageTitle() {
        LoginPage _login = new LoginPage(driver).access();
        assertTrue(_login.getPageTitle().equals("ログインページ"));
    }
}

補足:WebDriverの取得もFactoryクラスを作って切り替えられるようにするとより汎用的になります。




最後に


今回はWebアプリケーションの画面テストをする際に注意すべきこととして

  1. テスト自動化の範囲

  2. テストコードの記述ルール(デザインパターン)

  3. テストケースの抽象化(インターフェース定義)することによるテスト漏れの防止

をご紹介しました。


テストコードを書くのはとてもコストがかかりますが、Seleniumのようなテストツールを上手く活用する事でテストにかける時間を少なくし、 産性を最大限に引き出す事ができるきっかけの一つとなりますので、ぜひ活用してみてはいかがでしょうか。


  1. Selenium WebDriverはC#、Java、Perl、PHP、Python、Ruby、Node.jsに対応。