SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

「日本Seleniumユーザーコミュニティ」のエキスパートが教えるSelenium最新事情

Seleniumで挑む、SPAのE2Eテスト自動化

  • X ポスト
  • このエントリーをはてなブックマークに追加

不安定性への対処

 非同期リクエストとそれに伴う動的なページ書き換えが増えると、E2Eの自動テストは不安定になります。例えば、要素取得を行うときに、ページ書き換えが終わっていなくてまだ取得する予定の要素が表示されていないというような、いわゆるタイミング問題が発生するからです。

 この問題に対処するためには、人間がほぼ無意識のうちに行っている「要素が表示されるまで待つ」という処理をコードで実装する必要があります。例えば、要素取得であれば、以下のようなラッパーメソッドの作成が考えられます。

WebElement findElement(WebDriver driver, By by) {
    long timeOutInSeconds = 10;
    WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
    wait.until(ExpectedConditions.visibilityOfElementLocated(by));
    return driver.findElement(by);
}

 WebDriverWaitは、Explicit Waitと呼ばれている明示的な待ち処理を実現するためのクラスです。untilメソッドを呼び出すと以下のような処理が行われます。

  1. 渡された条件を満たすかチェック(今回の場合、要素が存在して見えること)
  2. 満たしていれば次の処理へ進む(今回の場合、要素の取得)
  3. 満たしていなければ0.5秒待機して再び1.へ戻る
  4. 最大待機時間(今回の場合、10秒)を過ぎても条件を満たさなければタイムアウトエラー

 ExpectedConditionsは、Seleniumテストでよく使われる待ち条件を集めたクラスです。今回のvisibilityOfElementLocatedメソッドでは、byで取得できる要素がDOM上に存在して、視認可能であることをチェックする条件に指定しています。

 Selenideのような一部のSeleniumラッパーライブラリの中には、上記で書いたような待ち処理を内部で自動で行ってくれるものも存在します。上記のような処理を自分で記述して自作フレームワークを構築していくのは手間なので、可能であればそういったライブラリの導入を検討したほうが楽です。

 しかし、言語やライブラリの選択によってはそういった機能を得られない場合もあります。ライブラリが提供する機能だけではカバーしてくれない待ち処理も存在するので、上記のような明示的な待ち処理の実装方法は把握しておくことをおすすめします。

 ここまで不安定なテストを安定させる方法を書きましたが、どれだけがんばっても不安定なテストを完全になくすことは簡単ではありません。そして、不安定なテストが原因による失敗が常態化していると、本当のバグでテストが落ちてるときも無視されるようになってしまう恐れがあります。この問題を改善するためには、テストが失敗したときに一定回数のリトライを認め、その回数内で成功したらOKとする方法があります。

 JUnit 4の場合、次のようなTestRuleを実装したRetryRuleクラスを作成すれば実現可能です。

package com.example.project;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class RetryRule implements TestRule {
    private static final Logger LOG = LogManager.getLogger(RetryRule.class.getName());
    private final int maxRetryCount;

    public RetryRule(int maxRetryCount) {
        this.maxRetryCount = maxRetryCount;
    }

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                Throwable caughtThrowable = null;

                for (int i = 0; i < maxRetryCount; i++) {
                    try {
                        base.evaluate();
                        return;
                    } catch (Throwable t) {
                        caughtThrowable = t;
                        LOG.error(description.getDisplayName() + ": run " + (i + 1) + " failed");
                    }
                }
                LOG.error(description.getDisplayName() + ": giving up after " + maxRetryCount + " failures");
                throw caughtThrowable;
            }
        };
    }
}
package com.example.project;

import org.junit.Rule;
import org.junit.Test;

import java.util.Random;

public class SampleTest {
    @Rule
    public RetryRule retryRule = new RetryRule(3);

    @Test
    public void flakyTest() {
        // 不安定なテスト
    }
}

 上記のSampleTestクラスの場合だと、クラス内のすべてのテストメソッドに対してそれぞれ最大3回までリトライするようになり、それまでに1回でも通ればテストが成功したと判定されます。

 このように不安定なテストを許容する方法はとても効果的ですが、かといって不安定なテストを放置してしまうと徐々にリトライ回数の上限を超えるようなテストが出てくる恐れがあります。なので、リトライしたテストを後から追跡できるようにしておき、継続的に改善することをおすすめします。

次のページ
SPAとページオブジェクトパターン

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
「日本Seleniumユーザーコミュニティ」のエキスパートが教えるSelenium最新事情連載記事一覧

もっと読む

この記事の著者

宮田 淳平(ミヤタ ジュンペイ)

 2009年にサイボウズへ入社。大規模向けグループウェア『ガルーン』やビジネスアプリ作成プラットフォーム『kintone』の開発を経験した後、生産性向上チームを立ち上げ、組織横断で開発基盤の整備と自動化の推進を行っている。 Twitter:@miyajan GitHub:https://github.com/miyajan

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10591 2017/12/18 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング