Selenium c # Webdriver: дождитесь появления элемента


185

Я хочу убедиться, что элемент присутствует, прежде чем веб-драйвер начнет делать вещи.

Я пытаюсь заставить что-то вроде этого работать:

WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0,0,5));
wait.Until(By.Id("login"));

Я в основном изо всех сил, как настроить любую функцию ..


3
К вашему сведению - так чище построить свой временной интервал TimeSpan.FromSeconds(5). Это делает это более ясным IMO
Каньон Колоб

Ответы:


159

В качестве альтернативы вы можете использовать неявное ожидание:

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

Неявное ожидание - указать WebDriver опросить DOM в течение определенного периода времени при попытке найти элемент или элементы, если они не доступны сразу. Значение по умолчанию равно 0. После установки устанавливается неявное ожидание в течение срока службы экземпляра объекта WebDriver.


5
спасибо, новый синтаксис: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Реда

20
@RedaBalkouch, синтаксис, который Майк использовал в своем ответе, правильный. Это C #
Diemo

3
Если вы решите использовать неявные ожидания, будьте осторожны, чтобы не использовать явные ожидания. Это может вызвать непредсказуемое поведение, приводящее к плохим результатам теста. Вообще говоря, я бы рекомендовал использовать явное ожидание вместо неявного.
mrfreester

7
Этот метод устарел, вместо этого следует использовать свойство ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Сэмюэль Рондо-Миллер

1
Я использовал предоставленный подход и обнаружил, что метод устарел, как указал Самуил. Проверка на наличие предмета теперь ожидает до указанного времени.
Джим Скотт

279

Использование решения, предоставленного Майком Кваном, может повлиять на общую производительность тестирования, поскольку неявное ожидание будет использоваться во всех вызовах FindElement. Много раз вы захотите, чтобы FindElement сразу завершился сбоем, когда элемент отсутствует (вы проверяете неправильно сформированную страницу, отсутствующие элементы и т. Д.). При неявном ожидании эти операции будут ожидать истечения всего времени ожидания, прежде чем выдать исключение. Неявное ожидание по умолчанию установлено на 0 секунд.

Я написал небольшой метод расширения для IWebDriver, который добавляет в FindElement()метод параметр времени ожидания (в секундах) . Это довольно очевидно:

public static class WebDriverExtensions
{
    public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => drv.FindElement(by));
        }
        return driver.FindElement(by);
    }
}

Я не кэшировал объект WebDriverWait, поскольку его создание очень дешево, это расширение может использоваться одновременно для разных объектов WebDriver, и я выполняю оптимизацию только в случае крайней необходимости.

Использование просто:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://localhost/mypage");
var btn = driver.FindElement(By.CssSelector("#login_button"));
btn.Click();
var employeeLabel = driver.FindElement(By.CssSelector("#VCC_VSL"), 10);
Assert.AreEqual("Employee", employeeLabel.Text);
driver.Close();

114
В случае, если кто-то задается вопросом, он WebDriverWaitиз OpenQA.Selenium.Support.UIпространства имен и поставляется в отдельном пакете, называемом Selenium WebDriver Support ClassesNuGet
Энди

5
@Ved я мог бы поцеловать тебя <3 искал его в другой DLL: D
Между

1
@Loudenvier Пожалуйста, выделите первую строку жирным шрифтом, чтобы она была более заметной. Тем более, что это не принятый ответ, хотя и является более точным и точным подходом.
Рик

5
Selenium WebDriver Support Classesтеперь появился на NuGet как «Selenium.Support» , текущая версия 3.4.0
Эрик Ф.

1
У меня все еще было много ошибок, пока я не использовал эту строку, return wait.Until(ExpectedConditions.ElementToBeClickable(by));и теперь она прекрасно работает. На голову, если кто-то еще получит случайные элементы, которые еще не найдены.
изыскатель

84

Вы также можете использовать

ExpectedConditions.ElementExists

Таким образом, вы будете искать доступность элемента, как это

new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut)).Until(ExpectedConditions.ElementExists((By.Id(login))));

Источник


1
Согласитесь, это гораздо полезнее, чем простой тайм-аут (в случаях, когда вы динамически загружаете объект).
keithl8041

5
Пока это работает. Теперь он помечен как устаревший, поэтому его следует избегать.
Адам Гарнер

3
Вот новый подход (не рекомендуется): stackoverflow.com/a/49867605/331281
Dejan

1
Обратите внимание, что в настоящее время DotNetSeleniumExtras.WaitHelpers(упомянутое @Dejan выше) «не поддерживается, проблемы не будут исправлены, PR не будут приняты». (источник: github.com/SeleniumHQ/selenium/issues/… ). Его издатель ищет сопровождающего, чтобы отобрать его у него.
Urig

30

Вот вариант решения @ Loudenvier, который также работает для получения нескольких элементов:

public static class WebDriverExtensions
{
    public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => drv.FindElement(by));
        }
        return driver.FindElement(by);
    }

    public static ReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => (drv.FindElements(by).Count > 0) ? drv.FindElements(by) : null);
        }
        return driver.FindElements(by);
    }
}

7
Ницца! Я только что добавил это в свою собственную библиотеку! В этом прелесть совместного использования кода !!!
Loudenvier

1
Я бы предложил одно дополнение к этому. Вы можете поймать решение NoSuchElement и вернуть null в этом случае. Затем вы можете создать метод расширения с именем .exists, который возвращает истину, если только IWebElement не равен нулю.
Брантли Бланчард

17

Вдохновленный решением Loudenvier, вот метод расширения, который работает для всех объектов ISearchContext, а не только для IWebDriver, который является специализацией первого. Этот метод также поддерживает ожидание, пока элемент не отобразится.

static class WebDriverExtensions
{
    /// <summary>
    /// Find an element, waiting until a timeout is reached if necessary.
    /// </summary>
    /// <param name="context">The search context.</param>
    /// <param name="by">Method to find elements.</param>
    /// <param name="timeout">How many seconds to wait.</param>
    /// <param name="displayed">Require the element to be displayed?</param>
    /// <returns>The found element.</returns>
    public static IWebElement FindElement(this ISearchContext context, By by, uint timeout, bool displayed=false)
    {
        var wait = new DefaultWait<ISearchContext>(context);
        wait.Timeout = TimeSpan.FromSeconds(timeout);
        wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
        return wait.Until(ctx => {
            var elem = ctx.FindElement(by);
            if (displayed && !elem.Displayed)
                return null;

            return elem;
        });
    }
}

Пример использования:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://localhost");
var main = driver.FindElement(By.Id("main"));
var btn = main.FindElement(By.Id("button"));
btn.Click();
var dialog = main.FindElement(By.Id("dialog"), 5, displayed: true);
Assert.AreEqual("My Dialog", dialog.Text);
driver.Close();

1
Если вы установили неявное ожидание, подобное _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);этому, все равно будет превышать значение тайм-аута, установленное здесь.
Howcheng

Это не работает для меня ...? Я добавил Stopwatchвокруг вызова в метод расширения и Console.WriteLine()внутри лямбда отправлено Until(). Секундомер измерял почти ровно 60 секунд, и только одно сообщение было записано Console. Я что-то здесь упускаю?
Urig

10

Я перепутал любую функцию с предикатом. Вот маленький вспомогательный метод:

   WebDriverWait wait;
    private void waitForById(string id) 
    {
        if (wait == null)            
            wait = new WebDriverWait(driver, new TimeSpan(0,0,5));

        //wait.Until(driver);
        wait.Until(d => d.FindElement(By.Id(id)));
    }

5

Вы можете узнать что-то подобное в C #.

Это то, что я использовал в JUnit - Selenium

WebDriverWait wait = new WebDriverWait(driver, 100);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));

Делать импорт связанных пакетов


1
Я попытался использовать это сегодня, и VS.net выдает мне предупреждения: класс OpenQA.Selenium.Support.UI.ExpectedConditions был помечен как «устаревший» и был «перенесен в репозиторий DotNetSeleniumExtras» на github.com/DotNetSeleniumTools
Джефф Мерглер,

3
//wait up to 5 seconds with no minimum for a UI element to be found
WebDriverWait wait = new WebDriverWait(_pagedriver, TimeSpan.FromSeconds(5));
IWebElement title = wait.Until<IWebElement>((d) =>
{
    return d.FindElement(By.ClassName("MainContentHeader"));
});

3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}

Приведенный выше код должен проверить, присутствует ли конкретный элемент или нет.
Мадху

2

Команда clickAndWait не преобразуется при выборе формата Webdriver в Selenium IDE. Вот обходной путь. Добавьте строку ожидания ниже. Реально, проблема была в щелчке или событии, которое произошло до этой строки 1 в моем коде C #. Но на самом деле, просто убедитесь, что у вас есть WaitForElement перед любым действием, когда вы ссылаетесь на объект «By».

HTML код:

<a href="http://www.google.com">xxxxx</a>

C # / NUnit код:

driver.FindElement(By.LinkText("z")).Click;
driver.WaitForElement(By.LinkText("xxxxx"));
driver.FindElement(By.LinkText("xxxxx")).Click();

2

Python:

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By

driver.find_element_by_id('someId').click()

WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.ID, 'someAnotherId'))

из ЕС вы можете выбрать и другие условия, попробуйте это: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions


Этот вопрос помечен C #, а не Python. Этот ответ не имеет значения.
Пользователь

2

Попробуйте этот код:

 New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)

4
Вы должны объяснить, что вы сделали и почему это решает проблему. И, пожалуйста, отформатируйте свой код.
Геринг

1

Явное ожидание

public static  WebDriverWait wait = new WebDriverWait(driver, 60);

Пример:

wait.until(ExpectedConditions.visibilityOfElementLocated(UiprofileCre.UiaddChangeUserLink));

1

Использовал Rn222 и Aknuds1 для использования ISearchContext, который возвращает либо один элемент, либо список. И минимальное количество элементов может быть указано:

public static class SearchContextExtensions
{
    /// <summary>
    ///     Method that finds an element based on the search parameters within a specified timeout.
    /// </summary>
    /// <param name="context">The context where this is searched. Required for extension methods</param>
    /// <param name="by">The search parameters that are used to identify the element</param>
    /// <param name="timeOutInSeconds">The time that the tool should wait before throwing an exception</param>
    /// <returns> The first element found that matches the condition specified</returns>
    public static IWebElement FindElement(this ISearchContext context, By by, uint timeOutInSeconds)
    {
        if (timeOutInSeconds > 0)
        {
            var wait = new DefaultWait<ISearchContext>(context);
            wait.Timeout = TimeSpan.FromSeconds(timeOutInSeconds);
            return wait.Until<IWebElement>(ctx => ctx.FindElement(by));
        }
        return context.FindElement(by);
    }
    /// <summary>
    ///     Method that finds a list of elements based on the search parameters within a specified timeout.
    /// </summary>
    /// <param name="context">The context where this is searched. Required for extension methods</param>
    /// <param name="by">The search parameters that are used to identify the element</param>
    /// <param name="timeoutInSeconds">The time that the tool should wait before throwing an exception</param>
    /// <returns>A list of all the web elements that match the condition specified</returns>
    public static IReadOnlyCollection<IWebElement> FindElements(this ISearchContext context, By by, uint timeoutInSeconds)
    {

        if (timeoutInSeconds > 0)
        {
            var wait = new DefaultWait<ISearchContext>(context);
            wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
            return wait.Until<IReadOnlyCollection<IWebElement>>(ctx => ctx.FindElements(by));
        }
        return context.FindElements(by);
    }
    /// <summary>
    ///     Method that finds a list of elements with the minimum amount specified based on the search parameters within a specified timeout.<br/>
    /// </summary>
    /// <param name="context">The context where this is searched. Required for extension methods</param>
    /// <param name="by">The search parameters that are used to identify the element</param>
    /// <param name="timeoutInSeconds">The time that the tool should wait before throwing an exception</param>
    /// <param name="minNumberOfElements">
    ///     The minimum number of elements that should meet the criteria before returning the list <para/>
    ///     If this number is not met, an exception will be thrown and no elements will be returned
    ///     even if some did meet the criteria
    /// </param>
    /// <returns>A list of all the web elements that match the condition specified</returns>
    public static IReadOnlyCollection<IWebElement> FindElements(this ISearchContext context, By by, uint timeoutInSeconds, int minNumberOfElements)
    {
        var wait = new DefaultWait<ISearchContext>(context);
        if (timeoutInSeconds > 0)
        {
            wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
        }

        // Wait until the current context found the minimum number of elements. If not found after timeout, an exception is thrown
        wait.Until<bool>(ctx => ctx.FindElements(by).Count >= minNumberOfElements);

        //If the elements were successfuly found, just return the list
        return context.FindElements(by);
    }

}

Пример использования:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://localhost");
var main = driver.FindElement(By.Id("main"));
// It can be now used to wait when using elements to search
var btn = main.FindElement(By.Id("button"),10);
btn.Click();
//This will wait up to 10 seconds until a button is found
var button = driver.FindElement(By.TagName("button"),10)
//This will wait up to 10 seconds until a button is found, and return all the buttons found
var buttonList = driver.FindElements(By.TagName("button"),10)
//This will wait for 10 seconds until we find at least 5 buttons
var buttonsMin= driver.FindElements(By.TagName("button"), 10, 5);
driver.Close();

1

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

WebDriverWait wait = new WebDriverWait (драйвер, TimeSpan.FromMilliseconds (2000));
wait.Until (ExpectedConditions.VisibilityOfAllElementsLocatedBy (By.Name ( "HTML-имя")));


1

Поскольку я разделяю определения элементов страницы и сценарии тестирования страницы, используя уже найденный IWebElement для наглядности, это можно сделать так:

public static void WaitForElementToBecomeVisibleWithinTimeout(IWebDriver driver, IWebElement element, int timeout)
{
    new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ElementIsVisible(element));
}

private static Func<IWebDriver, bool> ElementIsVisible(IWebElement element)
{
    return driver => {
        try
        {
            return element.Displayed;              
        }
        catch(Exception)
        {
            // If element is null, stale or if it cannot be located
            return false;
        }
    };
}

1

Это повторно используемая функция для ожидания присутствия элемента в DOM с использованием явного ожидания.

public void WaitForElement(IWebElement element, int timeout = 2)
{
    WebDriverWait wait = new WebDriverWait(webDriver, TimeSpan.FromMinutes(timeout));
    wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
    wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException));
    wait.Until<bool>(driver =>
    {
        try
        {
            return element.Displayed;
        }
        catch (Exception)
        {
            return false;
        }
    });
}

Добро пожаловать в Stack Overflow, не публикуйте ответы только по коду.
JJ за прозрачность и Монику

0

Мы можем достичь этого следующим образом:

public static IWebElement WaitForObject(IWebDriver DriverObj, By by, int TimeOut = 30)
{
    try
    {
        WebDriverWait Wait1 = new WebDriverWait(DriverObj, TimeSpan.FromSeconds(TimeOut));
        var WaitS = Wait1.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.PresenceOfAllElementsLocatedBy(by));
        return WaitS[0];
    }
    catch (NoSuchElementException)
    {
        Reports.TestStep("Wait for Element(s) with xPath was failed in current context page.");
        throw;
    }
}

0

WebDriverWait не вступит в силу.

var driver = new FirefoxDriver(
    new FirefoxOptions().PageLoadStrategy = PageLoadStrategy.Eager
);
driver.Navigate().GoToUrl("xxx");
new WebDriverWait(driver, TimeSpan.FromSeconds(60))
    .Until(d => d.FindElement(By.Id("xxx"))); // a tag that close to the end

Это сразу вызовет исключение, как только страница станет «интерактивной». Я не знаю почему, но тайм-аут действует так, как будто его не существует.

Возможно, SeleniumExtras.WaitHelpersработает, но я не пробовал. Это официально, но было разделено на другой пакет nuget. Вы можете обратиться к C # Selenium «Ожидаемые условия устарели» .

Сам пользуюсь FindElementsи проверь Count == 0, если правда, пользуюсь await Task.Delay. Это действительно не совсем эффективно.


0

Вы можете использовать следующее

WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0,0,5));
wait.Until(ExpectedConditions.ElementToBeClickable((By.Id("login")));

-1

Я вижу несколько уже опубликованных решений, которые прекрасно работают! Однако на случай, если кому-то понадобится что-то еще, я решил опубликовать два решения, которые я лично использовал в селене C #, чтобы проверить, присутствует ли элемент! Надеюсь, это поможет, ура!

public static class IsPresent
{
    public static bool isPresent(this IWebDriver driver, By bylocator)
    {

        bool variable = false;
        try
        {
            IWebElement element = driver.FindElement(bylocator);
            variable = element != null;
        }
       catch (NoSuchElementException){

       }
        return variable; 
    }

}

Вот второй

    public static class IsPresent2
{
    public static bool isPresent2(this IWebDriver driver, By bylocator)
    {
        bool variable = true; 
        try
        {
            IWebElement element = driver.FindElement(bylocator);

        }
        catch (NoSuchElementException)
        {
            variable = false; 
        }
        return variable; 
    }

}


-1

Первый ответ хороший, моя проблема заключалась в том, что необработанные исключения не закрывали веб-драйвер должным образом, и он сохранял то же первое значение, которое я использовал, которое составляло 1 секунду.

Если вы получаете ту же проблему

restart you visual studioи убедитесь, что all the exceptions are handledправильно.


К настоящему времени вы должны знать, что в Переполнении стека нет упорядочения ответов, поэтому нет «первого ответа»
Антти Хаапала

-2

Искал, как ждать в Selenium для состояния, приземлился в этой теме, и вот что я использую сейчас:

    WebDriverWait wait = new WebDriverWait(m_driver, TimeSpan.FromSeconds(10));
    wait.Until(d => ReadCell(row, col) != "");

ReadCell(row, col) != ""может быть любое условие. Вот так, потому что:

  • это мое
  • позволяет встраивать
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.