移除 AbstractEventListener + EventFiringWebDriver + WebDriverEventListener

本博客将介绍一些关于如何转换使用上述类的代码的示例。

升级到 WebDriverListener 和 EventFiringDecorator

装饰 WebDriver

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

实现方法包装器

人们可能会发现需要使用他们自己的自定义实现来处理底层装饰方法调用。一个例子可能是想要使用自己的 findElement 实现来存储来自 Web 元素的元数据。人们可以深入到装饰器的兔子洞中(扩展 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);
    }
}

关于上述示例的一些说明,我们只重写了“通用”调用方法,并针对每次调用检查方法名称。在不深入装饰器的情况下,还可以重写类实例的调用,以提供更有针对性的方法。为了公开更多的功能,让我们修改我们的示例。我们可以修改 WebElement 上下文,因为我们可能关心子元素和 WebDriver 找到的元素(WebDriver 和 WebElement 都扩展了 SearchContext)。

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 类中一个具有生活质量的更改是使用“default”。在 Java 中,当 default 关键字在接口方法的上下文中时,表示该方法具有默认实现。如果实现该接口的类选择不重写该方法,它将继承默认实现。此更改允许拆分监听器,而无需实现您不需要或不关心的不必要的方法。

使用 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
}

监听通用事件

带来的一个更改是能够监听通用事件。一个用例是在并行化的测试套件中记录信息。现在不再需要创建一个监听器并重写每个方法来添加一个简单的日志语句,而是可以通过重写一个方法调用来更简单地实现。下面我重写了 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));
    }
}

这就是关于如何转换代码的一些通用示例!祝你测试愉快!