Я пытаюсь использовать Selenium для задач тестирования автоматизации веб-сайтов, и я новичок в среде тестирования Selenium. Ситуация, с которой я столкнулся, заключается в том, чтобы дождаться загрузки компонентов веб-сайта, а затем выполнить соответствующие операции с целевыми компонентами. В WaitFor
метод с xpath
а также waitingTimes
входные параметры разработаны, как показано ниже.
Экспериментальная реализация
private static bool WaitFor(IWebDriver driver, string xpath, uint waitingTimes = 100)
{
log.Info("Wait for " + xpath);
uint count = 0;
while (IsElementExists(driver, xpath) == false)
{
if (count >= waitingTimes)
{
return false;
}
Thread.Sleep(1000);
count++;
}
return true;
}
private static bool IsElementExists(IWebDriver driver, string xpath)
{
try
{
var element = driver.FindElement(By.XPath(xpath));
return true;
}
catch (Exception ex)
{
log.Info(ex.Message);
}
return false;
}
Использование WaitFor
метод:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Repository.Hierarchy;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
namespace WaitForMethodSeleniumWebDriver
{
class Program
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static BackgroundWorker backgroundWorkerForLogging = new BackgroundWorker();
static void Main(string[] args)
{
LogHandler();
log.Info("Main method");
IWebDriver driver = new ChromeDriver(@".");
driver.Url = "https://codereview.stackexchange.com/";
WaitFor(driver, ".//*[@id='content']");
// perform next step operation
// ...
driver.Close();
driver.Quit();
}
// Reference: https://stackoverflow.com/a/4172968/6667035
private static void LogHandler()
{
Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(System.Reflection.Assembly.GetEntryAssembly());
XmlConfigurator.Configure(hierarchy, new FileInfo("log4net.config"));
FileAppender fileAppender = new FileAppender();
fileAppender.AppendToFile = true;
fileAppender.LockingModel = new FileAppender.MinimalLock();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder = stringBuilder.Append("log").Append(DateTime.UtcNow.ToString("_MM-dd-yyyy_hh_mm_ss")).Append(".txt");
fileAppender.File = stringBuilder.ToString();
log4net.Layout.PatternLayout pl = new log4net.Layout.PatternLayout();
pl.ConversionPattern = "%d [%2%t] %-5p [%-10c] %m%n";
pl.ActivateOptions();
fileAppender.Layout = pl;
fileAppender.ActivateOptions();
BasicConfigurator.Configure(fileAppender);
// Reference: https://stackoverflow.com/a/2077767/6667035
backgroundWorkerForLogging.DoWork += (sender, e) =>
{
UDPListener();
};
// Start BackgroundWorker
backgroundWorkerForLogging.RunWorkerAsync();
}
// launch this in a background thread
private static void UDPListener()
{
System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
var udpClient = new System.Net.Sockets.UdpClient(10001);
while (true)
{
var buffer = udpClient.Receive(ref remoteEndPoint);
var loggingEvent = System.Text.Encoding.Unicode.GetString(buffer);
Console.WriteLine(loggingEvent);
}
}
private static bool WaitFor(IWebDriver driver, string xpath, uint waitingTimes = 100)
{
log.Info("Wait for " + xpath);
uint count = 0;
while (IsElementExists(driver, xpath) == false)
{
if (count >= waitingTimes)
{
return false;
}
Thread.Sleep(1000);
count++;
}
return true;
}
private static bool IsElementExists(IWebDriver driver, string xpath)
{
try
{
var element = driver.FindElement(By.XPath(xpath));
return true;
}
catch (Exception ex)
{
log.Info(ex.Message);
}
return false;
}
}
}
Все предложения приветствуются.
2 ответа
WaitFor
- Для меня это название метода довольно странное.
- Если бы я был потребителем вашего API, я бы исключил из этого наименования, что он возвращался бы с
IWebElement
экземпляр (илиnull
), а неboolean
ценить.
- Если бы я был потребителем вашего API, я бы исключил из этого наименования, что он возвращался бы с
- Но если вы хотите придерживаться этого API проверки существования, я бы посоветовал:
static bool CheckElementExistence(this IWebDriver driver, string elementXPath, byte waitUntilInSeconds = 10)
- Я переименовал твой
xpath
параметр на более значимый - В
waitingTimes
на мой взгляд действительно плохой параметр- Вы должны знать некоторые детали реализации (
Thread.Sleep(1000)
), чтобы можно было определить значение этого параметра - Вот почему я бы предложил использовать другую концепцию, например
waitUntil
- Обычно рекомендуется включать в имя единицу времени, чтобы иметь возможность использовать API без тщательного изучения документации (будь то миллисекунды или секунды).
- Вы должны знать некоторые детали реализации (
IsElementExists(driver, xpath) == false
: Я знаю, что некоторым людям не нравится использование оператор логического отрицания потому что его легко контролировать, но вы можете переписать свой цикл, чтобы избежать использования== false
private static bool CheckElementExistence(this IWebDriver driver, string elementXPath, byte waitUntilInSeconds = 10)
{
log.Info("Checking existence of " + elementXPath);
byte remainingSeconds = waitUntilInSeconds;
while (remainingSeconds > 0)
{
if (DoesElementExist(driver, elementXPath)) return true;
Thread.Sleep(1000);
remainingSeconds--;
}
return false;
}
- Возможно, имеет смысл сделать ваш метод асинхронным и использовать
Task.Delay
скорее, чемThread.Sleep
IsElementExists
- Я бы предложил переименовать ваш метод в
DoesElementExist
- Я бы также рекомендовал обрабатывать только
NoSuchElementException
а не какой-либоException
.- Например, если предоставленный xpath неверен или равен нулю,
FindElement
может броситьArgumentException
(это всего лишь предположение, не знаю). Вы можете справиться с этим делом по-другому
- Например, если предоставленный xpath неверен или равен нулю,
var element
: если вы не используете эту переменную, вы можете просто использовать оператор сброса
private static bool DoesElementExist(IWebDriver driver, string elementXPath)
{
try
{
_ = driver.FindElement(By.XPath(elementXPath));
return true;
}
catch (NoSuchElementException ex)
{
log.Info(ex.Message);
return false;
}
}
Это немного переосмысливает, но я думаю, что это поможет.
У Selenium на самом деле есть пакет помощника для умного ожидания, который называется DotNetSeleniumExtras.WaitHelpers
.
С его помощью вы можете значительно упростить свой код, чтобы просто:
private void WaitFor(IWebDriver driver, By by, TimeSpan wait)
{
var smartWait = new WebDriverWait(driver, wait);
smartWait.Until(ExpectedConditions.ElementExists(by));
}
Использование:
WaitFor(driver, By.XPath(".//*[@id='content']"), TimeSpan.FromSeconds(10));
Таким образом, вы можете сохранить все параметры настолько открытыми, насколько вам нужно. Драйвер нужен всегда. Критерии поиска по-прежнему позволяют использовать любые доступные шаблоны поиска — например, по классу CSS или по идентификатору — а явный временной интервал позволяет сразу понять время задержки.
от твоего
DoesElementExist
метод есть небольшая ловушка,FindElement
использует время ожидания поиска драйверов и сидит там, пока не истечет время ожидания или не найдет его. если ваш тайм-аут выражен в минутах, а тайм-аут вызывающих функций может быть в предполагаемых секундах. ноremainingSeconds
в вашем примере это просто счетчик количества попыток перепроверки. как есть …. если вы установитеwaitUntilInSeconds
до 20 тайм-аут драйвера составляет 30 секунд, затем, если мы никогда не находим элемент управления, мы сидим здесь 620 секунд— Майкл Ригер
Откровенно говоря, я не использовал библиотеку .net. Я использовал js lib несколько лет назад. Я предположил, что FindElement мгновенный, он не ждет определенного времени. Имея это в виду, в моем дизайне есть проблема, которую вы описали. Спасибо, что указали. Есть ли метод FindElementAsync, который получает cancellationToken?
— Питер Чала
самое близкое к немедленным результатам — это
FindElements
который возвращает коллекцию. затем проверьте счет.— Майкл Ригер