Selenium c # Webdriver: зачекайте, поки елемент буде присутній


185

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

Я намагаюся змусити щось подібне працювати:

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

Я в основному борюся, як налаштувати анонімну функцію ..


3
FYI - чистіше будувати такий часовий проміжок 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);
Samuel Rondeau-Millaire

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();

113
Якщо хтось замислиться, 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

Ви також можете використовувати

Очікувані умови.ElementExists

Таким чином, ви будете шукати доступність елемента

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

Джерело


1
Погоджено, це набагато корисніше, ніж простий тайм-аут (у випадках, коли ви динамічно завантажуєте об'єкт).
keithl8041

5
Поки це працює. Зараз вона позначена як застаріла, тому її слід уникати.
Адам Гарнер

3
Ось новий підхід (не застарілий): stackoverflow.com/a/49867605/331281
Десан

1
Зауважте, що DotNetSeleniumExtras.WaitHelpersнаразі (про який згадував @Dejan вище) "не підтримується, проблеми не виправляються, PR-адреси не приймаються". (джерело: github.com/SeleniumHQ/selenium/isissue/… ). Його видавець шукає технічного обслуговування, щоб взяти його у нього.
уріг

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 і повернути нуль у цьому випадку. Тоді ви можете створити метод розширення під назвою .exists, який повертає true, якщо 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. Я щось тут пропускаю?
уріг

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
Джефф Mergler

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 перед будь-якою дією, де ви посилаєтесь на об’єкт "За".

HTML-код:

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

C # / NN код:

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

2

Пітон:

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
Ви повинні пояснити, що ви зробили, і чому це вирішує проблему. І будь ласка, відформатуйте свій код.
hering

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

Ви не хочете занадто довго чекати, перш ніж елемент зміниться. У цьому коді webdriver чекає до 2 секунд, перш ніж він продовжить.

WebDriverWait wait = новий WebDriverWait (драйвер, TimeSpan.FromMilliseconds (2000));
wait.Until (очікуваніConditions.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;
        }
    });
}

Ласкаво просимо до переповнення стека, не публікуйте відповідей, що стосуються лише коду.
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працює, але я не намагався. Це офіційно, але було розбито на інший пакунок. Ви можете посилатися на 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
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));

ОчікуваніУмови застаріли
GELR

-1

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

Якщо у вас є та сама проблема

restart you visual studioі забезпечити це all the exceptions are handledналежним чином.


На даний момент ви повинні знати, що немає замовлення на відповіді в Stack Overflow, тому немає "першої відповіді"
Antti Haapala

-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.