簡體   English   中英

Selenium C# WebDriver:等到元素出現

[英]Selenium C# WebDriver: Wait until element is present

我想確保在 webdriver 開始做事之前存在一個元素。

我正在嘗試讓這樣的東西起作用:

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

我主要是在努力設置匿名 function...

使用Mike Kwan 提供解決方案可能會對整體測試性能產生影響,因為在所有 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();

或者,您可以使用隱式等待:

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

隱式等待是告訴 WebDriver 在嘗試查找一個或多個元素(如果它們不是立即可用)時輪詢 DOM 一段時間。 默認設置為 0。設置后,將在 WebDriver 對象實例的生命周期內設置隱式等待。

你也可以使用

預期條件.ElementExists

因此,您將搜索這樣的元素可用性

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

來源

這是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);
    }
}

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

混淆了一個匿名函數和一個謂詞。 這是一個小助手方法:

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

你可以在 C# 中找到類似的東西。

這是我在JUnit 中使用的 - Selenium

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

導入相關包。

public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}
// 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"));
});

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'))

ECexpected_conditions 的導入),您也可以選擇其他條件。 試試這個: 預期條件支持

試試這個代碼:

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

當您在 Selenium IDE 中選擇 Webdriver 格式時,clickAndWait 命令不會被轉換。 這是解決方法。 添加下面的等待線。 實際上,問題是在我的 C# 代碼中的第 1 行之前發生的點擊或事件。 但實際上,只要確保在引用“By”對象的任何操作之前有一個 WaitForElement。

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

顯式等待

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

例子:

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

您不想在元素更改之前等待太久。 在此代碼中,webdriver 在繼續之前最多等待 2 秒。

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromMilliseconds(2000));
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Name("html-name")));

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

由於我使用已經找到的 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;
        }
    };
}

這是使用顯式 wait 等待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;
        }
    });
}

您可以使用以下內容:

使用命名空間:

using SeleniumExtras.WaitHelpers;

在代碼中:

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

我看到多個解決方案已經發布,效果很好! 但是,以防萬一有人需要其他東西,我想我會發布兩個我個人在 Selenium 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;
    }
}

我們可以這樣實現:

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;
    }
}

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 'ExpectedConditions is obsolete'

我使用FindElements並檢查Count == 0 如果為 true,請使用await Task.Delay 確實效率不高。

您可以使用以下

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

使用C#擴展方法:我們可以解決等待元素可見的問題。
特定元素的最大 reties 為 100。

public static bool WaitForElementToBeVisible(IWebDriver browser, By by)
        {
            int attemptToFindElement = 0;
            bool elementFound = false;
            IWebElement elementIdentifier = null;
            do
            {
                attemptToFindElement++;
                try
                {
                    elementIdentifier = browser.FindWebElement(by);
                    elementFound = (elementIdentifier.Displayed && elementIdentifier.Enabled) ? true : false;
                }
                catch (Exception)
                {
                    elementFound = false;
                }

            }
            while (elementFound == false && attemptToFindElement < 100);

            return elementFound;
        }

我正在使用它並且效果很好:

public static bool elexists(By by, WebDriver driver)
    {
        try
        {
            driver.FindElement(by);
            return true;
        }
        catch (NoSuchElementException)
        {
            return false;
        }
    }
    public static void waitforelement(WebDriver driver, By by)
    {
        for (int i = 0; i < 30; i++)
        {
            System.Threading.Thread.Sleep(1000);
            if (elexists(by, driver))
            {
                break;
            }


        }

    }

當然,您可以添加 30 多次嘗試並將檢查時間縮短到 1 秒以上。

用法:

waitforelement(driver, By.Id("login"));
IWebElement login= driver.FindElement(By.Id("login"));
login.Click();
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));

第一個答案很好,但我的問題是未處理的異常沒有正確關閉 Web 驅動程序,並且它保持了我使用的第一個值,即 1 秒。

如果遇到同樣的問題,請重新啟動 Visual Studio並確保正確處理所有異常

以下是如何在 Selenium 中等待條件:

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

ReadCell(row, col) != ""可以是任何條件。 喜歡這種方式是因為:

  • 這是我的
  • 允許內聯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM