Разделы портала

Онлайн-тренинги

.
Избавляемся от ExpectedConditions в тестах C# Selenium WebDriver
13.10.2020 00:00

Автор: Баз Дейкстра (Bas Djikstra)
Оригинал статьи
Перевод: Ольга Алифанова

Если вы пишете тесты Selenium на C#, то, возможно, заметили, что начиная с версии 3.11 ряд часто используемых штук помечен как устаревший – в частности, PageFactory и класс ExpectedConditions. Для тех, кто не знает, почему это произошло – вот объяснение Джима Эванса, основного разработчика связки Selenium C#.

Все это перемещено в новое место и доступно в форме отдельных пакетов NuGet (DotNetSeleniumExtras.PageObjects и DotNetSeleniumExtras.WaitHelpers, соответственно). Когда я писал эту статью, этот репозиторий никто не поддерживал – а следовательно, поддержка недоступна, и будущее его туманно.



Пока с этим нет проблем – вы все еще можете использовать устаревшие классы или пользоваться пакетами DotNetSeleniumExtras. Однако я считаю, что тут есть риск положиться на код, который по большей части не поддерживается.

В моем случае с PageFactory нет проблем – я, как правило, все равно этим не пользуюсь. Другое дело с ExpectedConditions. Когда я пишу автоматизацию на Selenium и обучаю ей, я в основном следую подходу, который описывал несколько лет назад здесь. Он все еще работает, он читабелен, его легко поддерживать и объяснить, и он был эффективен в множестве различных проектов, с которыми я работал.

Однако этот подход полагается на использование ExpectedConditions, и это все чаще и чаще меня беспокоит.

Поэтому, готовясь к тренингу, я начал искать альтернативу своим оберточным методам на C#. Оказалось, что C# предлагает для этого элегантный способ прямо из коробки в форме лямбда-выражений.

Лямбда-выражения (также известные как анонимные функции) – это функции, не привязанные к идентификатору. Их можно передавать как аргумент в функцию более высокого порядка. Концепция зародилась из функционального программирования (которым я, кстати, давненько не занимался), но уже давно является частью C#. Между прочим, лямбда-выражения доступны и в Java, начиная с Java8, если вы не работаете с C#.

Посмотрим на примере. Вот так я бы реализовал свой детализированный оберточный метод через стандартный метод SendKeys() Selinium, который учитывает синхронизацию и работу с исключениями, используя ExpectedConditions:

public void SendKeys(By by, string valueToType)
{
try
{
new WebDriverWait(_driver, TimeSpan.FromSeconds(Constants.DEFAULT_TIMEOUT)).Until(ExpectedConditions.ElementToBeClickable(by));
_driver.FindElement(by).Clear();
_driver.FindElement(by).SendKeys(valueToType);
}
catch (Exception ex) when (ex is NoSuchElementException || ex is WebDriverTimeoutException)
{
Assert.Fail($"Exception occurred in SeleniumHelper.SendKeys(): element located by {by.ToString()} could not be located within {Constants.DEFAULT_TIMEOUT} seconds.");
}
}

Та же самая обертка теперь передает наше собственное лямбда-выражение как аргумент метода Until() вместо метода в ExpectedConditions:

public void SendKeys(By by, string valueToType)
{
WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(Constants.DEFAULT_TIMEOUT));
try
{
IWebElement myElement = wait.Until<IWebElement>(driver =>
{
IWebElement tempElement = _driver.FindElement(by);
return (tempElement.Displayed &amp;&amp; tempElement.Enabled) ? tempElement : null;
}
);
myElement.Clear();
myElement.SendKeys(valueToType);
}
catch (WebDriverTimeoutException)
{
Assert.Fail($"Exception in SendKeys(): element located by {by.ToString()} not visible and enabled within {Constants.DEFAULT_TIMEOUT} seconds.");
}
}

Документация Selenium гласит, что метод Until() регулярно (с изменяемым интервалом, по умолчанию 500 миллисекунд) оценивает лямбда-выражение, пока не будет применимо одно из условий:

  • Выражение не возвращает ни null, ни ложь
  • Функция выдает исключение, не входящее в список игнорируемых
  • Истек указанный таймаут.

Поэтому я создал лямбда-выражение именно таким. Прежде чем кликнуть по элементу, я хочу дождаться, пока он станет кликабельным. Кликабельность я определяю как видимость (выраженную через свойство Displayed) и доступность (свойство Enabled). Если оба они верны, я возвращаю элемент, в прочих случаях – null. Если это происходит до таймаута, я очищаю текстовое поле и использую стандартный метод SendKeys() для ввода значения. Если происходит WebDriverTimeOutException, я учитываю это соответственно – в этом случае падением теста с внятным сообщением.

Да, такое внедрение моих вспомогательных методов немного перегружает мой код, но это небольшая плата за то, чтобы не рассчитывать на библиотеку, которая больше не поддерживается, и чье будущее туманно. К тому же я определяю этих помощников в едином классе, и поэтому это затрагивает только одну часть моего кода. Page Objects и тестовый код остаются без изменений.

В этом репозитории находится несколько рабочих примеров тестов, которые написаны для демо-приложения ParaBank. Это обновленная версия решения, описанного здесь.

Обсудить в форуме