На мысль навел просмотр видео, в котором Сергей Король справедливо напомнил, что многие из нас «упираются» собственно в средства Селениума и не используют всю силу благословенной Java. Уже вышла 9 версия, а многие еще недостаточно освоили 8, часто ли вы используете в проекте лямбды, стримы, функциональные интерфейсы?
Покажу небольшой пример применения функциональных интерфейсов для ожиданий, который делает код кратким, понятным и, главное, вполне в стиле 8 Java. Идею я честно взял из вышеуказанного видео, немного поправив для своих нужд.
Сначала пишем небольшой enum, в котором используем все наиболее часто применяемые ожидания. Вот весь код, далее обсудим:
import org.openqa.selenium.By; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import java.util.function.Function; public enum WaitConditions { visible (ExpectedConditions::visibilityOfElementLocated), exist (ExpectedConditions::presenceOfAllElementsLocatedBy), clickable (ExpectedConditions::elementToBeClickable), invisible (ExpectedConditions::invisibilityOfElementLocated); WaitConditions(Function<By, ExpectedCondition> type) { this.type = type; } public Function<By, ExpectedCondition> getType() { return type; } private final Function<By, ExpectedCondition> type; }
Тип нашего enum это функциональный интерфейс Function, который получает на вход By (наш локатор) и возвращает ExpectedCondition то есть те самые условия ожидания.
visible (ExpectedConditions::visibilityOfElementLocated), exist (ExpectedConditions::presenceOfAllElementsLocatedBy), clickable (ExpectedConditions::elementToBeClickable), invisible (ExpectedConditions::invisibilityOfElementLocated);
Тут мы собственно перечисляем нужные значения и те методы, на которые их нужно завязать.
private final Function<By, ExpectedCondition> type;
Обратите внимание на ? — это необходимо для использования invisibilityOfElementLocated, так как он в отличие от остальных методов возвращает ExpectedCondition.
С enum разобрались, теперь посмотрим на метод, который будет использовать его. Сразу оговорюсь, я использую возвращаемое значение boolean, а не кидаю исключение выше, но это мой стиль работы, вы можете модифицировать метод:
public boolean waitFor(By locator, WaitConditions conditions) { WebDriverWait wait = new WebDriverWait(driver, defaulTime); try { wait.until(conditions.getType().apply(locator)); return true; } catch (TimeoutException ex) { //делаем скриншот, логируем и т.д. return false; } }
defaultTime — это переменная int, сколько конкретно секунд ждать по умолчанию, при желании ее можно менять, driver это конечно же наш WebDriver. Вот тут все происходит wait.until(conditions.getType().apply(locator)); — наш локатор используется для ожидания соответствующего типа.
Не так много кода мы написали, но все это просто и лаконично позволит нам в коде писать вызовы наших ожиданий в стиле
waitFor(By.id("loginButton"), visible); или waitFor(submitButton, clickable);
По-моему достаточно просто использовать и легко читается, даже тому, кто впервые увидит ваши тесты.