Como posso pedir ao Selenium-WebDriver para esperar alguns segundos em Java?

100

Estou trabalhando em um Java Selenium-WebDriver. Eu adicionei

driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);

e

WebElement textbox = driver.findElement(By.id("textbox"));

porque meus aplicativos levam alguns segundos para carregar a interface do usuário. Portanto, defino 2 segundos implícito e espera. mas não consegui localizar a caixa de texto do elemento

Então eu adiciono Thread.sleep(2000);

Agora funciona bem. Qual é a melhor maneira?

Principe
fonte

Respostas:

123

Bem, existem dois tipos de espera: espera explícita e implícita. A ideia de espera explícita é

WebDriverWait.until(condition-that-finds-the-element);

O conceito de espera implícita é

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

Você pode obter a diferença nos detalhes aqui .

Em tais situações, prefiro usar espera explícita ( fluentWaitem particular):

public WebElement fluentWait(final By locator) {
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(30, TimeUnit.SECONDS)
            .pollingEvery(5, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class);

    WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return driver.findElement(locator);
        }
    });

    return  foo;
};

fluentWaitfunção retorna seu elemento da web encontrado. Da documentação em diante fluentWait: Uma implementação da interface Wait que pode ter seu tempo limite e intervalo de pesquisa configurados em tempo real. Cada instância FluentWait define a quantidade máxima de tempo de espera por uma condição, bem como a frequência com a qual verificar a condição. Além disso, o usuário pode configurar a espera para ignorar tipos específicos de exceções enquanto espera, como NoSuchElementExceptions ao pesquisar um elemento na página. Detalhes você pode obter aqui

O uso de, fluentWaitno seu caso, seja o seguinte:

WebElement textbox = fluentWait(By.id("textbox"));

Esta abordagem IMHO é melhor, pois você não sabe exatamente quanto tempo esperar e no intervalo de pesquisa, você pode definir o valor de tempo arbitrário por meio do qual a presença do elemento será verificada. Saudações.

eugene.polschikov
fonte
3
Isso import com.google.common.base.Function;, nãoimport java.util.function.Function;
verifiquei
não verificado, na verdade usando intelij IDEA para desenvolvimento, está ajudando com importações automáticas (ao trabalhar em um projeto baseado em maven)
eugene.polschikov
1
Eu estava apenas chamando isso para outras pessoas que podem estar se perguntando o que Functionestava sendo usado. Não foi óbvio para mim no início. Obrigado pela resposta.
verificado
1
Ou você pode simplesmente usar uma expressão lambda: driver -> driver.findElement(locator). Ao usar isso, você não precisará de uma instrução de importação.
Thibstars
2
Agora é: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))em vez de: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro
16

Este tópico é um pouco mais antigo, mas pensei em postar o que faço atualmente (trabalho em andamento).

Embora eu ainda esteja encontrando situações em que o sistema está sob carga pesada e quando clico em um botão de envio (por exemplo, login.jsp), todas as três condições (veja abaixo) retornam, truemas a próxima página (por exemplo, home.jsp) não tem Ainda não comecei a carregar.

Este é um método de espera genérico que obtém uma lista de ExpectedConditions.

public boolean waitForPageLoad(int waitTimeInSec, ExpectedCondition<Boolean>... conditions) {
    boolean isLoaded = false;
    Wait<WebDriver> wait = new FluentWait<>(driver)
            .withTimeout(waitTimeInSec, TimeUnit.SECONDS)
            .ignoring(StaleElementReferenceException.class)
            .pollingEvery(2, TimeUnit.SECONDS);
    for (ExpectedCondition<Boolean> condition : conditions) {
        isLoaded = wait.until(condition);
        if (isLoaded == false) {
            //Stop checking on first condition returning false.
            break;
        }
    }
    return isLoaded;
}

Eu defini várias ExpectedConditions reutilizáveis ​​(três estão abaixo). Neste exemplo, as três condições esperadas incluem document.readyState = 'complete', nenhum "wait_dialog" presente e nenhum 'spinners' (elementos indicando que dados assíncronos estão sendo solicitados).

Apenas o primeiro pode ser aplicado genericamente a todas as páginas da web.

/**
 * Returns 'true' if the value of the 'window.document.readyState' via
 * JavaScript is 'complete'
 */
public static final ExpectedCondition<Boolean> EXPECT_DOC_READY_STATE = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        String script = "if (typeof window != 'undefined' && window.document) { return window.document.readyState; } else { return 'notready'; }";
        Boolean result;
        try {
            result = ((JavascriptExecutor) driver).executeScript(script).equals("complete");
        } catch (Exception ex) {
            result = Boolean.FALSE;
        }
        return result;
    }
};
/**
 * Returns 'true' if there is no 'wait_dialog' element present on the page.
 */
public static final ExpectedCondition<Boolean> EXPECT_NOT_WAITING = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
            WebElement wait = driver.findElement(By.id("F"));
            if (wait.isDisplayed()) {
                loaded = false;
            }
        } catch (StaleElementReferenceException serex) {
            loaded = false;
        } catch (NoSuchElementException nseex) {
            loaded = true;
        } catch (Exception ex) {
            loaded = false;
            System.out.println("EXPECTED_NOT_WAITING: UNEXPECTED EXCEPTION: " + ex.getMessage());
        }
        return loaded;
    }
};
/**
 * Returns true if there are no elements with the 'spinner' class name.
 */
public static final ExpectedCondition<Boolean> EXPECT_NO_SPINNERS = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
        List<WebElement> spinners = driver.findElements(By.className("spinner"));
        for (WebElement spinner : spinners) {
            if (spinner.isDisplayed()) {
                loaded = false;
                break;
            }
        }
        }catch (Exception ex) {
            loaded = false;
        }
        return loaded;
    }
};

Dependendo da página, posso usar um ou todos eles:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Existem também ExpectedConditions predefinidos na seguinte classe: org.openqa.selenium.support.ui.ExpectedConditions

Jeff Vincent
fonte
1
Ótima resposta! Nunca vi alguém passar um item ExpectedCondition por meio do construtor do método. Que grande idéia. +1
djangofan
Jeff Vincent, você pode me dizer se isso funcionará para a página esperar 5 minutos. Se sim, por favor, sugira qual parâmetro de local precisa ser enviado?
khanam de
Esta é uma ótima resposta. Tenho uma função semelhante a essa, no entanto, tenho que chamá-la em todas as funções para que meu script espere até o carregamento da página (spinner). Existe uma maneira de conectar essa função waitForSpinner silenciosamente em ImplicitWait para que eu não precise chamá-la todas as vezes em minha função? Como definir a função, conecte-a ao driver uma vez e bum.
Bhuvanesh Mani
13

Se estiver usando webdriverJs (node.js),

driver.findElement(webdriver.By.name('btnCalculate')).click().then(function() {
    driver.sleep(5000);
});

O código acima faz o navegador esperar 5 segundos após clicar no botão.

Anup
fonte
18
Por que você postaria isso quando a questão é especificamente sobre Java, não node / JavaScript? É tão fora do assunto quanto responder como fazê-lo em rubi.
Thor84no,
1
recomendação para usar o sono definitivamente não é uma boa solução
Kiril S.
@ Thor84no Principalmente porque alguns de nós que buscam a solução da web encontram esta resposta
Kitanga Nday
10

Usar Thread.sleep(2000);é uma espera incondicional. Se o seu teste carregar mais rápido, você ainda terá que esperar. Portanto, em princípio, usar implicitlyWaité a melhor solução.

No entanto, não vejo por implicitlyWaitque não funciona no seu caso. Você mediu se findElementrealmente leva dois segundos antes de lançar uma exceção. Em caso afirmativo, você pode tentar usar a espera condicional do WebDriver conforme descrito nesta resposta?

Valentin
fonte
3

Gosto de usar condições personalizadas. Aqui estão alguns códigos em Python:

def conditions(driver):
    flag = True
    ticker = driver.find_elements_by_id("textbox")
    if not ticker:
        flag = False
    return flag

... click something to load ...
self.wait = WebDriverWait(driver, timeout)
self.wait.until(conditions)

Sempre que precisar esperar, você pode fazê-lo explicitamente verificando a existência de um determinado elemento (tal elemento pode variar de página para página). find_elements_by_idlista de retornos - vazia ou não, basta verificar.

simno
fonte
2

clique parece estar bloqueando? - aqui está outra maneira de esperar se você estiver usando WebDriverJS:

driver.findElement(webdriver.By.name('mybutton')).click().then(function(){
  driver.getPageSource().then(function(source) {
    console.log(source);
  });
});

O código acima aguarda depois que o botão é clicado para a próxima página carregar e então pega o código-fonte da próxima página.

Dion
fonte
1
O que faz o código aguardar o carregamento da próxima página? Acontece apenas que a primeira chamada no retorno de chamada é getPageSource?
Isócrono
1

Implicitamente esperar e Thread.sleep Ambos são usados ​​para sincronização apenas ... mas a diferença é que podemos usar Implicitamente esperar por todo o programa, mas Thread.sleep funcionará apenas para aquele único código ... Aqui minha sugestão é usar Espere implicitamente uma vez no programa quando toda vez que sua página da Web for atualizada significa usar Thread.sleep naquele momento ... será muito melhor :)

Aqui está o meu código:

package beckyOwnProjects;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;

public class Flip {

    public static void main(String[] args) throws InterruptedException {
        WebDriver driver=new FirefoxDriver();
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.MINUTES);
        driver.get("https://www.flipkart.com");
    WebElement ele=driver.findElement(By.cssSelector(".menu-text.fk-inline-block"));
    Actions act=new Actions(driver);
    Thread.sleep(5000);
    act.moveToElement(ele).perform();
    }

}
Beckham Vinoth
fonte
0

Às vezes, a espera implícita parece ser substituída e o tempo de espera é encurtado. [@ eugene.polschikov] tinha uma boa documentação sobre os porquês. Descobri em meus testes e codificação com Selenium 2 que as esperas implícitas são boas, mas ocasionalmente você tem que esperar explicitamente.

É melhor evitar chamar diretamente um tópico para dormir, mas às vezes não há uma boa maneira de contornar isso. No entanto, existem outras opções de espera fornecidas pelo Selenium que ajudam. waitForPageToLoad e waitForFrameToLoad provaram ser especialmente úteis.

BadgerAndK
fonte
0

Espera implícita: durante a espera implícita, se o driver da Web não puder localizá-lo imediatamente devido à sua disponibilidade, o WebDriver aguardará o tempo mencionado e não tentará encontrar o elemento novamente durante o período de tempo especificado. Assim que o tempo especificado terminar, ele tentará pesquisar o elemento mais uma vez pela última vez antes de lançar a exceção. A configuração padrão é zero. Depois de definir um tempo, o driver da Web aguarda o período da instância do objeto WebDriver.

Espera explícita: pode haver uma instância em que um elemento específico leva mais de um minuto para carregar. Nesse caso, você definitivamente não gosta de definir um tempo enorme para espera implícita, pois se você fizer isso, seu navegador irá esperar o mesmo tempo para cada elemento. Para evitar essa situação, você pode simplesmente colocar um horário separado apenas no elemento necessário. Seguindo isso, o tempo de espera implícito do seu navegador seria curto para cada elemento e seria grande para um elemento específico.

Abhishek Singh
fonte
0

Às vezes, a espera implícita falha, dizendo que um elemento existe, mas realmente não existe.

A solução é evitar o uso de driver.findElement e substituí-lo por um método personalizado que usa uma Espera Explícita implicitamente. Por exemplo:

import org.openqa.selenium.NoSuchElementException;


public WebElement element(By locator){
    Integer timeoutLimitSeconds = 20;
    WebDriverWait wait = new WebDriverWait(driver, timeoutLimitSeconds);
    try {
        wait.until(ExpectedConditions.presenceOfElementLocated(locator));
    }
    catch(TimeoutException e){
        throw new NoSuchElementException(locator.toString());
    }
    WebElement element = driver.findElement(locator);
    return element;
}

Existem razões adicionais para evitar a espera implícita, além de falhas esporádicas e ocasionais (consulte este link ).

Você pode usar este método de "elemento" da mesma maneira que driver.findElement. Por exemplo:

    driver.get("http://yoursite.html");
    element(By.cssSelector("h1.logo")).click();

Se você realmente deseja apenas aguardar alguns segundos para a solução de problemas ou alguma outra ocasião rara, pode criar um método de pausa semelhante ao que o selênio IDE oferece:

    public void pause(Integer milliseconds){
    try {
        TimeUnit.MILLISECONDS.sleep(milliseconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
JohnP2
fonte
0

Resposta : aguarde alguns segundos antes que a visibilidade do elemento usando Selenium WebDriver passe pelos métodos abaixo.

implicitlyWait () : a instância do WebDriver espera até o carregamento completo da página. Você deve usar 30 a 60 segundos para aguardar o carregamento completo da página.

driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

ExplicitlyWait WebDriverWait () : a instância do WebDriver espera até o carregamento completo da página.

WebDriverWait wait = new WebDriverWait(driver, 60);

wait.until(ExpectedConditions.visibilityOf(textbox));

driver.findElement(By.id("Year")).sendKeys(allKeys);

Observação : use ExplicitlyWait WebDriverWait () para lidar com qualquer WebElement específico.

Sandeep Shewale
fonte
0

Ações de uso -

A API voltada para o usuário para emular gestos complexos do usuário.

Consulte o método Actions # pause .

Dzmitry Sankouski
fonte
-1

Prefiro que o código a seguir espere 2 segundos.

for(int i=0; i<2 && driver.findElements(By.id("textbox")).size()==0 ; i++){
   Thread.sleep(1000);
}
Gangadhar
fonte
-1
Thread.sleep(1000);

é o pior: sendo uma espera estática, tornará o script de teste mais lento.

driver.manage().timeouts.implicitlyWait(10,TimeUnit.SECONDS);

esta é uma espera dinâmica

  • é válido até a existência do webdriver ou tem um escopo até a vida do driver
  • podemos esperar implícita também.

Finalmente, o que sugiro é

WebDriverWait wait = new WebDriverWait(driver,20);
wait.until(ExpectedConditions.<different canned or predefined conditions are there>);

com algumas condições predefinidas:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • É também uma espera dinâmica
  • neste a espera será apenas em segundos
  • temos que usar a espera explícita por um elemento da web em particular no qual queremos usar.
Shobhit
fonte
-4
Thread.Sleep(5000);

Isso me ajudou, mas a exceção InterruptedException precisa ser resolvida. Portanto, é melhor envolvê-lo com try and catch:

try {
    Thread.Sleep(5000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

OU

Adicionar declaração throws:

public class myClass {
    public static void main(String[] args) throws InterruptedException
    { ... }

Eu preferiria o segundo, pois pode-se usar sleep()quantas vezes quiser e evitar a repetição trye o catchbloqueio sempre sleep()que for usado.

Prasad Hajare
fonte