Selenium c # Webdriver: Aguarde até que o elemento esteja presente

185

Quero garantir que um elemento esteja presente antes que o webdriver comece a fazer coisas.

Estou tentando fazer algo assim funcionar:

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

Eu estou lutando principalmente para configurar a função anônima ..

AyKarsi
fonte
3
Para sua informação, é mais fácil criar seu período de tempo como este TimeSpan.FromSeconds(5). Isso torna mais claro o IMO #
Kolob Canyon

Respostas:

159

Como alternativa, você pode usar a espera implícita:

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

Uma espera implícita é dizer ao WebDriver para pesquisar o DOM por um determinado período de tempo ao tentar encontrar um elemento ou elementos, se eles não estiverem disponíveis imediatamente. A configuração padrão é 0. Depois de definida, a espera implícita é definida para a vida útil da instância do objeto WebDriver.

Mike Kwan
fonte
5
obrigado, a nova sintaxe é: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda
20
@RedaBalkouch, a sintaxe usada por Mike em sua resposta está correta. É C #
Diemo
3
Se você optar por usar esperas implícitas, tome cuidado para não usar esperas explícitas. Isso pode causar algum comportamento imprevisível, levando a resultados ruins de teste. De um modo geral, eu recomendaria o uso de esperas explícitas sobre esperadas implícitas.
mrfreester
7
Agora, esse método foi descontinuado, você deve usar a propriedade ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire
1
Usei a abordagem fornecida e constatei que o método estava obsoleto, como apontado por Samuel. A verificação da existência de um item agora aguarda o tempo especificado.
21475 Jim
279

O uso da solução fornecida por Mike Kwan pode afetar o desempenho geral dos testes, pois a espera implícita será usada em todas as chamadas FindElement. Muitas vezes, você deseja que o FindElement falhe imediatamente quando um elemento não está presente (você está testando uma página malformada, elementos ausentes etc.). Com a espera implícita, essas operações esperariam o tempo limite expirar antes de lançar a exceção. A espera implícita padrão é definida como 0 segundos.

Eu escrevi um pequeno método de extensão no IWebDriver que adiciona um parâmetro de tempo limite (em segundos) ao FindElement()método. É bastante auto-explicativo:

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

Não armazenei em cache o objeto WebDriverWait, pois sua criação é muito barata, essa extensão pode ser usada simultaneamente para diferentes objetos do WebDriver e só otimizo quando necessário.

O uso é direto:

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();
Loudenvier
fonte
114
No caso de alguém admira, WebDriverWaitsão do OpenQA.Selenium.Support.UInamespace e vem em um pacote separado chamado Selenium WebDriver Support Classesem NuGet
Andy
5
@Ved eu poderia beijar você <3 procurando por ele em uma dll diferente: D
#
1
@Loudenvier Por favor, deixe a primeira linha em negrito para que fique mais perceptível. Especialmente porque não é a resposta aceita, embora seja uma abordagem melhor e mais precisa.
Rick
5
Selenium WebDriver Support Classesagora aparece no NuGet como "Selenium.Support" , a versão atual é 3.4.0
Eric F.
1
Eu ainda tinha muitos erros até usar essa linha return wait.Until(ExpectedConditions.ElementToBeClickable(by));e ela funciona muito bem agora. Atenção, caso alguém obtenha elementos aleatórios ainda não encontrados.
prospector
84

Você também pode usar

ExpectedConditions.ElementExists

Então, você procurará por uma disponibilidade de elemento assim

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

Fonte

Zain Ali
fonte
1
Concordado, isso é muito mais útil do que um mero tempo limite (nos casos em que você está carregando dinamicamente um objeto).
precisa saber é o seguinte
5
Enquanto isso funciona. Agora está marcado como obsoleto, portanto deve ser evitado.
Adam Garner
3
Aqui está a nova abordagem (não obsoleta): stackoverflow.com/a/49867605/331281
Dejan
1
Observe que, no momento, o DotNetSeleniumExtras.WaitHelpers(referido por @Dejan acima) "não é mantido, os problemas não serão corrigidos, os PRs não serão aceitos". (fonte: github.com/SeleniumHQ/selenium/issues/… ). Seu editor está procurando um mantenedor para substituí-lo.
urig
30

Aqui está uma variação da solução da @ Loudenvier que também funciona para obter vários elementos:

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);
    }
}
Rn222
fonte
7
Agradável! Acabei de adicionar isso à minha própria biblioteca! Essa é a beleza de compartilhar código !!!
Loudenvier
1
Eu sugeriria uma adição a isso. Você pode pegar a solução NoSuchElement e retornar nulo nessa instância. Em seguida, você pode criar um método de extensão chamado .exists que retorne true, a menos que o IWebElement seja nulo.
Brantley Blanchard
17

Inspirado na solução de Loudenvier, aqui está um método de extensão que funciona para todos os objetos ISearchContext, não apenas para o IWebDriver, que é uma especialização do primeiro. Este método também suporta aguardar até que o elemento seja exibido.

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

Exemplo de uso:

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();
aknuds1
fonte
1
Se você definiu uma espera implícita como _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);essa, ainda superará o valor do tempo limite definido aqui.
howcheng
Isso não parece funcionar para mim ...? Eu adicionei um Stopwatchao redor da chamada ao método de extensão e um Console.WriteLine()dentro do lambda enviado para Until(). O cronômetro mediu quase exatamente 60 segundos e apenas uma mensagem foi gravada Console. Estou faltando alguma coisa aqui?
urig 28/10/19
10

Confundi função anômala com predicado. Heres um pequeno método auxiliar:

   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)));
    }
AyKarsi
fonte
5

Você pode descobrir algo assim em C #.

Isto é o que eu usei no JUnit - Selenium

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

Importar pacotes relacionados

Aditi
fonte
1
Tentei usar isso hoje e o VS.net está me dando avisos: a classe OpenQA.Selenium.Support.UI.ExpectedConditions foi marcada como "obsoleta" e foi "migrada para o repositório DotNetSeleniumExtras" no repositório github.com/DotNetSeleniumTools
Jeff 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"));
});
Brian121212
fonte
3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}
Madhu
fonte
O código acima é para verificar se um elemento específico está presente ou não.
Madhu
2

O comando clickAndWait não é convertido quando você escolhe o formato Webdriver no Selenium IDE. Aqui está a solução alternativa. Adicione a linha de espera abaixo. Realisticamente, o problema foi o clique ou evento que ocorreu antes desta linha 1 no meu código C #. Mas, na verdade, verifique se você tem um WaitForElement antes de qualquer ação em que você está fazendo referência a um objeto "Por".

Código HTML:

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

Código da unidade:

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

Pitão:

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

da CE, você também pode escolher outras condições: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions

Nazmul Haque Sarker
fonte
Esta pergunta está etiquetada com C #, não Python. Esta resposta é irrelevante.
Usuário
2

Tente este código:

 New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
Ammar Ben Hadj Amor
fonte
4
Você deve explicar o que fez e por que isso resolve o problema. E formate seu código.
23717
1

Espera explícita

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

Exemplo:

wait.until(ExpectedConditions.visibilityOfElementLocated(UiprofileCre.UiaddChangeUserLink));
Pavan T
fonte
1

Utilizou Rn222 e Aknuds1 para usar um ISearchContext que retorna um único elemento ou uma lista. E um número mínimo de elementos pode ser especificado:

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

}

Exemplo de uso:

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();
havan
fonte
1

Você não deseja esperar muito antes que o elemento seja alterado. Nesse código, o webdriver aguarda até 2 segundos antes de continuar.

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

user3607478
fonte
1

Como estou separando as definições dos elementos da página e os cenários de teste da página usando o IWebElement já encontrado para obter visibilidade, isso pode ser feito da seguinte maneira:

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;
        }
    };
}
Angel_D
fonte
1

Essa é a função reutilizável para aguardar um elemento presente no DOM usando a Explicit Wait.

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;
        }
    });
}
Balakrishna
fonte
Bem-vindo ao Stack Overflow, não poste respostas somente de código.
JJ for Transparency e Monica
0

Podemos conseguir isso assim:

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;
    }
}
Krunal
fonte
0

WebDriverWait não terá efeito.

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

Isso geraria uma exceção imediatamente quando a página fosse "interativa". Não sei por que, mas o tempo limite age como se não existisse.

Talvez SeleniumExtras.WaitHelpersfuncione, mas não tentei. É oficial, mas foi dividido em outro pacote de pepitas. Você pode consultar o C # Selenium 'ExpectedConditions is obsolete' .

Eu mesmo estou usando FindElementse verifique se Count == 0, se for verdade, use await Task.Delay. Realmente não é tão eficiente.

imba-tjd
fonte
0

Você pode usar o seguinte

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

Vejo várias soluções já postadas que funcionam muito bem! No entanto, caso alguém precise de algo mais, pensei em publicar duas soluções que eu pessoalmente usei no selênio C # para testar se um elemento está presente! Espero que ajude, felicidades!

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

}

Aqui é o segundo

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

}
newITguy
fonte
-1
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));
david
fonte
ExpectedConditions está obsoleto
GELR 8/08/18
-1

A primeira resposta é boa, meu problema era que as exceções não tratadas não fechavam o driver da web corretamente e mantinha o mesmo primeiro valor que eu usei, que foi de 1 segundo.

Se você tiver o mesmo problema

restart you visual studioe garantir isso all the exceptions are handledcorretamente.

Pete Kozak
fonte
Até agora você deve saber que não há nenhuma ordenação de respostas em estouro de pilha, por isso não há "primeira resposta"
Antti Haapala
-2

Estava pesquisando como esperar no Selenium pela condição, desembarcou nesse segmento e aqui está o que eu uso agora:

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

ReadCell(row, col) != ""pode ser qualquer condição. Assim, porque:

  • é meu
  • permite inlining
Mars Robertson
fonte