Для автоматизации тестирования специалисты компании «Аплана» довольно часто используют такой инструмент, как Selenium. Он бесплатный и свободный в использовании, совместим со всеми основными платформами и браузерами, поддерживает несколько языков программирования, включая Java, Python, C#, Ruby и Perl, и имеет ряд других преимуществ. Однако, из-за некоторых особенностей Selenium при программировании можно допустить досадные ошибки. О том, как их избежать рассказывает ведущий инженер-тестировщик «Апланы» и преподаватель корпоративного университета — Мария Байкова.
Почти два года я преподаю курс «Разработка автоматизированных тестов с использованием Selenium WebDriver» (Автоматизация функционального тестирования с помощью Selenium) в корпоративном университете «Апланы». В процессе работы мне приходится проверять большой объем домашних заданий. За это время я выделила несколько типичных ошибок, которые чаще всего встречаются у наших студентов — начинающих автоматизаторов. Давайте рассмотрим их на конкретных примерах.
Одна из наиболее распространённых ошибок возникает при проверке присутствия элемента на странице. Происходит это из-за неверного понимания работы метода — isDisplayed библиотеки Selenium WebDriver. Часто можно увидеть подобную реализацию:
public boolean isElementPresent(WebElement element){
return element.isDisplayed();
}
Вспомним, что возвращает нам метод element.isDisplayed();
Ø False, если элемент есть на странице, но он невидимый
Ø NoSuchElementException, если элемента на странице нет
Метод можно применять, только если элемент присутствует в коде страницы. Если же элемента нет, тест упадет с исключением — NoSuchElementException.
В качестве правильной реализации можно использовать следующие варианты:
public boolean isElementPresent(By locator){
try{
DriverManager.getWebDriver().manage().timeouts()
.implicitlyWait(0, TimeUnit.SECONDS);
return getWebDriver().findElement(locator).isDisplayed();
}catch (NoSuchElementException e){
return false;
}finally {
DriverManager.getWebDriver().manage().timeouts()
.implicitlyWait(30, TimeUnit.SECONDS);
}
}
В блоке try перед выполнением метода isDisplayed устанавливаем неявное ожидание в 0 секунд, чтобы тест не подвисал, в том случае, когда элемент будет отсутствовать на странице, а метод findElement будет искать его в течение времени, заданного в implicitlyWait.
Если элемент не найден, то перехватываем в блоке catch исключение NoSuchElementException и возвращаем false.
В блоке finally возвращаем неявное ожидание в значение по умолчанию для дальнейшего выполнения теста.
Есть еще один вариант реализации, в котором не придется использовать try catch и обрабатывать исключительную ситуацию:
public boolean isElementPresent(By locator){
getWebDriver().manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
List
getWebDriver().manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
if (elementList.size() > 0){
return elementList.get(0).isDisplayed()
}
return false;
}
В данном случае используется метод findElements, который возвращает список элементов, найденных по заданному локатору. Если подходящих веб-элементов нет, то возвращается пустой список.
Для этого варианта также необходимо менять неявное ожидание для метода findElements во избежание зависания теста.
Еще одной частой ошибкой является необдуманное использование конструкции try – catch.
Например, в блоке try реализуется клик по элементу. Если клик не выполнен и было выброшено исключение, то в блоке catch выполняется еще один клик по тому же элементу.
try {
clearCart.click();
} catch (StaleElementReferenceException e) {
clearCart.click();
}
В таких случаях удобно использовать явные ожидания:
Wait
.withTimeout(5, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(StaleElementReferenceException.class);
fluentWait.until(WebElement::isEnabled);
clearCart.click();
Пример проверки файла:
try {
utilScenarioSteps.isFileExists(fileFormat, filename);
} catch (Exception e) {
try {
utilScenarioSteps.isFileExists(fileFormat, filename);
} catch (AssertionError ex) {
utilScenarioSteps.isFileExists(fileFormat filename);
}
} finally {
utilScenarioSteps.deleteFile();
}
Этот вариант можно переписать с использованием FluentWait — класса из библиотеки Selenium WebDriver, который дальше будет рассмотрен более подробно:
Wait
wait.until(File::exists);
Использование циклов для синхронизации и ожидания веб-элементов также является не лучшим решением:
public void waitForSomeThing(){
int timeout = 30000;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start
String actualResult = new CalculatorResultsPage().getResult(fieldName);
if (actualResult.equalsIgnoreCase(expectedResult)){
return;
}else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Assert.fail("Timeout is over");
}
В данном примере проверяются значения в полях, которые обновляются автоматически. Иногда это обновление может происходить не сразу, поэтому проверка выполняется в цикле несколько раз, чтобы предусмотреть вариант, в котором поля обновились на несколько секунд позже.
Более правильным решением было бы использовать стандартные явные ожидания, которые уже входят в Selenium WebDiver. Например, ExpectedConditions.textToBePresentInElement
WebDriverWait wait = new WebDriverWait(DriverManager.getDriver(), 10);
wait.until(ExpectedConditions.textToBePresentInElement(new CalculatorResultsPage().getField(fieldName), expectedResult));
Если же стандартных ожиданий недостаточно для решения задачи, то можно реализовать свое ожидание:
WebDriverWait wait = new WebDriverWait(DriverManager.getWebDriver(), 30);
wait.until((ExpectedCondition
String actualResult = new CalculatorResultsPage().getResult(fieldName);
return actualResult.equalsIgnoreCase(expectedResult);
});
Рассмотрим подробнее ожидание FluentWait, которое:
Ожидание FluentWait поможет справиться с проблемами синхронизации, которые могут встретиться в тесте. Ожидание FluentWait ожидает определенного события или состояния в течение заданного времени и проверяет данное условие с определенной частотой.
Пример метода, который пытается найти элемент, игнорируя исключения NoSuchElementException и StaleElementReferenceException:
public WebElement findElementByLocator(By locator) {
Wait
.withTimeout(5, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class);
return wait.until(d -> d.findElement(locator));
}
Ожидание FluentWait можно применять не только с объектами класса WebDriver.Например, здесь мы ждем появления файла в директории:
public void waitUntilFileIsWritten(File file) {
Wait
.withTimeout(10, TimeUnit.SECONDS)
.pollingEvery(250, TimeUnit.MILLISECONDS);
wait.until(File::exists);
}
Методы класса FluentWait:
Ещё одна типичная ошибка при написании автотестов — это выбор локаторов. Чтобы её избежать, достаточно придерживаться основных правил:
Своим студентам я рекомендую использовать конвенции языка программирования, на котором разрабатываются тесты. В дальнейшем это упростит поддержку и сопровождение тестов, а также поможет новым коллегам быстрее влиться в проект.
Например, эти конвенции языка Java помогут сделать код хорошо читаемым и аккуратным:
Имена файлов, пакетов
Имена методов, переменных
Структурирование кода
В заключение хочу напомнить о необходимости периодически проводить код ревью проекта. Он помогает написать более аккуратный и хорошо документированный код, а также убедиться в том, что написанный код корректен.
И ещё. Не огорчайтесь, если вам знакомы ситуации, описанные выше. Помните, что делать ошибки — это важная часть развития!
Если у вас возникли вопросы, оставляйте комментарии под постом о типичных ошибках при разработке автоматизированных тестов в наших группах во ВКонтакте или на Facebook.