AbstractEventListener + EventFiringWebDriver + WebDriverEventListener の削除

このブログでは、前述のクラスを使用するコードを移行する方法の例をいくつか紹介します。

WebDriverListener および EventFiringDecorator へのアップグレード

WebDriver の装飾

new EventFiringWebDriver(driver); // Old approach
new EventFiringDecorator().decorate(driver); // New approach

メソッドラッパーの実装

装飾されたメソッド呼び出しの基盤として、独自のカスタム実装を使用する必要がある場合があります。例としては、Web 要素からメタデータを保存するために、独自の findElement 実装を使用したい場合があります。デコレータ(WebDriverDecorator などを拡張する)の深い迷路に迷い込む可能性がありますが、事を簡単にするために、すべてのリスナーイベントを処理する単一のデコレータが必要なため、EventFiringDecorator を拡張します。

public class WebDriverWrapper implements WebDriver {
    private final WebDriver driver;
    WebDriverWrapper(WebDriver driver) {
        this.driver = driver;
    }
    // custom implementation goes here
    @Override
    public WebElement findElement(final By by) {
        // custom implementation goes here
        return driver.findElement(by);
    }
}

public class testDecorator extends EventFiringDecorator<WebDriver> {

    @Override
    public Object call(Decorated<?> target, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if ("findElement".equals(methodName)) {
            WebDriverWrapper newDriver = new WebDriverWrapper((WebDriver) target.getOriginal());
            return newDriver.findElement((By) args[0]);
        }
        return super.call(target, method, args);
    }
}

上記の例に関する注意点として、「general」call メソッドのみをオーバーライドし、すべての呼び出しに対してメソッド名をチェックしています。デコレータについて深く掘り下げなくても、クラスインスタンスによる呼び出しをオーバーライドして、より的を絞ったアプローチを提供できます。さらにいくつかの機能を公開するために、例を変更してみましょう。子要素や WebDriver によって見つかった要素(WebDriver と WebElement はどちらも SearchContext を拡張します)が気になる可能性があるため、WebElement コンテキストを変更できます。

public class WebElementWrapper implements WebElement {
    private final WebElement element;
    WebElementWrapper(WebElement element) {
        this.element = element;
    }
    @Override
    public WebElement findElement(final By by) {
        // custom implementation goes here
        return element.findElement(by);
    }
}

public class WebElementDecorator extends EventFiringDecorator<WebDriver> {
    @Override
    public Decorated<WebElement> createDecorated(WebElement original) {
        return new DefaultDecorated<>(original, this) {
            @Override
            public Object call(Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                if ("findElement".equals(methodName)) {
                    // custom implementation goes here
                    WebElementWrapper element = new WebElementWrapper(getOriginal());
                    return element.findElement((By) args[0]);
                }
                return super.call(method, args);
            }
        };
    }
}

上記のサンプルでは、call メソッドをオーバーライドするという非常に似たアプローチを依然として行っていますが、現在は WebElement インスタンスもターゲットにしています。

リスナーの登録

new EventFiringWebDriver(driver).register(listener1).register(listener2); // Old approach
new EventFiringDecorator(listener1, listener2); // New approach

イベントのリスン

WebDriverListener クラスで特徴的なQoL(生活の質)の変更は、「default」の使用です。Java では、interface メソッドのコンテキストで使用される場合、default キーワードは、メソッドがデフォルト実装を持つことを示します。interface を実装するクラスがメソッドをオーバーライドしないことを選択した場合、デフォルト実装を継承します。この変更により、不要なメソッドや気にしないメソッドを実装する必要なく、リスナーを分割できます。

before/after メソッド呼び出しを使用した特定イベントのリスン

// Old approach
public class AlertListener implements WebDriverEventListener {
    @Override
    public void beforeAlertAccept(final WebDriver driver) {
        // custom implementation goes here
    }
// implement every method in interface
}

// New approach
public class AlertListener implements WebDriverListener {
    @Override
    public void beforeAccept(Alert alert) {
        // custom implementation goes here
    }
// does not need to implement every method in interface
}

ジェネリックイベントのリスン

もたらされた変更の1つは、ジェネリックイベントをリッスンする機能です。ユースケースの1つは、並列化されたテストスイートでの情報ロギングです。リスナーを作成し、すべてのメソッドをオーバーライドして簡単なログステートメントを追加するのではなく、1つのメソッド呼び出しをオーバーライドするというより簡単な代替手段ができました。以下では beforeAnyCall をオーバーライドしていますが、装飾されたメソッドの呼び出しの結果も持つ afterAnyCall も存在します。

public class Listener implements WebDriverEventListener {
    private static final Logger LOGGER = Logger.getLogger(Listener.class.getName());

    @Override
    public void beforeAnyCall(Object target, Method method, Object[] args) {
        logger.debug("Thread: " + Thread.currentThread().getName() +
                " | Method Name: " + method.getName() +
                " | Method Args: " + Arrays.toString(args));
    }
}

より具体的なジェネリックイベントをリッスンする追加機能もありました。ロギングの例に戻ると、beforeAnyCall はデバッグ情報やスレッドのアクションを追跡するための優れたメソッドですが、ノイズが多すぎる可能性があります。同じユースケースでは、WebDriver または WebElement の呼び出しのみを気にする場合があります。WebDriver および派生オブジェクト (WebElement、Alert など) のインスタンスを before/after イベント用にオーバーライドできます。

public class Listener implements WebDriverEventListener {
    private static final Logger LOGGER = Logger.getLogger(Listener.class.getName());

    @Override
    public void beforeAnyWebDriverCall(WebDriver driver, Method method, Object[] args) {
        logger.debug("Thread: " + Thread.currentThread().getName() +
                " | Method Name: " + method.getName() +
                " | Method Args: " + Arrays.toString(args));
    }

    @Override
    public void beforeAnyWebElementCall(WebElement element, Method method, Object[] args) {
        logger.debug("Thread: " + Thread.currentThread().getName() +
                " | Method Name: " + method.getName() +
                " | Method Args: " + Arrays.toString(args));
    }
}

以上が、コードを移行する方法に関する一般的な例です。ハッピーテスティング!