Метод WaitFor для тестирования автоматизации веб-сайтов с помощью Selenium WebDriver

Я пытаюсь использовать 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 ответа
2

WaitFor

  • Для меня это название метода довольно странное.
    • Если бы я был потребителем вашего API, я бы исключил из этого наименования, что он возвращался бы с IWebElement экземпляр (или null), а не boolean ценить.
  • Но если вы хотите придерживаться этого 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 (это всего лишь предположение, не знаю). Вы можете справиться с этим делом по-другому
  • 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;
    } 
}

  • 1

    от твоего DoesElementExist метод есть небольшая ловушка, FindElement использует время ожидания поиска драйверов и сидит там, пока не истечет время ожидания или не найдет его. если ваш тайм-аут выражен в минутах, а тайм-аут вызывающих функций может быть в предполагаемых секундах. но remainingSeconds в вашем примере это просто счетчик количества попыток перепроверки. как есть …. если вы установите waitUntilInSeconds до 20 тайм-аут драйвера составляет 30 секунд, затем, если мы никогда не находим элемент управления, мы сидим здесь 620 секунд

    — Майкл Ригер

  • Откровенно говоря, я не использовал библиотеку .net. Я использовал js lib несколько лет назад. Я предположил, что FindElement мгновенный, он не ждет определенного времени. Имея это в виду, в моем дизайне есть проблема, которую вы описали. Спасибо, что указали. Есть ли метод FindElementAsync, который получает cancellationToken?

    — Питер Чала

  • самое близкое к немедленным результатам — это FindElements который возвращает коллекцию. затем проверьте счет.

    — Майкл Ригер

Это немного переосмысливает, но я думаю, что это поможет.

У 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 или по идентификатору — а явный временной интервал позволяет сразу понять время задержки.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *