WebDriver Wait '.until(ExpectedConditions.elementToBeClickable' 仅在我将 Thread.sleep(500) 添加到代码时才有效?

Posted

技术标签:

【中文标题】WebDriver Wait \'.until(ExpectedConditions.elementToBeClickable\' 仅在我将 Thread.sleep(500) 添加到代码时才有效?【英文标题】:WebDriver Wait '.until(ExpectedConditions.elementToBeClickable' only works when i add Thread.sleep(500) to the Code?WebDriver Wait '.until(ExpectedConditions.elementToBeClickable' 仅在我将 Thread.sleep(500) 添加到代码时才有效? 【发布时间】:2016-11-26 12:05:00 【问题描述】:

    我正在尝试使用 webdriver wait 单击页面上可见的按钮,但 webdriver 只有在我将 Thread.sleep 添加到代码后才能单击该按钮。

    在执行我的代码之前,我还检查了按钮是否可见 (True),而它又是 returns = true

//按钮可见性检查:

List<WebElement> signOutList = driver.findElements(.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button"));
Assert.assertTrue(signOutList.size() > 0);

//下面的代码没有点击按钮

By timeDropdownButton = By.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.elementToBeClickable(timeDropdownButton));
myDynamicElement.click();

//下面的代码是否点击了按钮:

Thread.sleep(500);
By timeDropdownButton = By.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.elementToBeClickable(timeDropdownButton));
myDynamicElement.click();

请注意,我还尝试使用 JS 代码和 WebDriver 操作等单击按钮

我不知道为什么“Wait Clickable”只有在与“Thread.sleep”结合使用时才有效?

The button I'm trying to click

【问题讨论】:

您是否尝试过将时间从 10 秒增加到更多..??? 嗨 Saurabh,我尝试了以下代码,但仍然无法正常工作(从 10 更改为 1000):WebElement myDynamicElement = (new WebDriverWait(driver, 1000)) 尝试使用ExpectedConditions.visibilityOfElementLocated..告诉我.. 感谢您帮助 Saurabh,恐怕即使尝试 ExpectedConditions.visibilityOfElementLocated 它仍然不起作用 你能分享一下发生了什么异常吗?? 【参考方案1】:

您希望在测试中尽可能避免Thread.sleep。仅仅通过几个测试它可能看起来并不那么重要,但它们的使用存在很多固有的问题。首先,如果你有一堆测试,测试套件的运行时间可能会变得难以管理。其次,它们有时还不够。例如,对于一般的生产机器,等待 500 毫秒可能就足够了,但如果 Web 服务器处于高负载或测试环境中,则可能需要 750 毫秒才能准备好。然后,您正在处理非确定性故障。最好使用WebDriverWait 之类的构造,并给它们一个合理(但过于慷慨)的最大值,这样您就不必等待比必要的时间更长,但如果它失败了,则意味着下面的环境存在严重问题测试。

这里的必胜客网站也大量使用异步 java 脚本和浮动面板等。所有这些都需要在使用 Selenium 时更加小心,因为元素需要在与它们交互之前准备好,但它们通常是已经在 DOM 中了。这意味着默认情况下,Selenium 可能会在 javascript 完成之前快速找到它们,并且它们实际上并未处于准备使用状态。您将希望像您已经尝试做的那样使用 ExpectedConditions。您遇到问题的按钮需要等待 JavaScript 完成,这是已经建议的,但它不需要在页面加载时,而是在单击按钮之前。在我的机器上运行的示例:

@Test
public void foo() 
    WebDriver driver = new FirefoxDriver();
    driver.manage()
          .window()
          .maximize();
    // Move through the page to the point you are interested in
    driver.get("https://www.pizzahut.co.uk/");
    waitForElement(driver, By.cssSelector(".hidden-xs [title='Pizza']")).click();
    waitForElement(driver, By.cssSelector("form[action='/menu/startyourorder']")).submit();
    WebElement postElement = waitForElement(driver, By.id("ajax-postcode-txt"));
    postElement.sendKeys("TS1 4AG" + Keys.ENTER);
    // Wait for the java script to finish executing
    waitForJavaScript(driver);
    // Finally you should be able to click on the link
    waitForElement(driver, By.partialLinkText("Start Your Order")).click();
    // continue on ... then quit the driver
    driver.quit();


private WebElement waitForElement(WebDriver driver, By locator) 
    return new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(locator));


private void waitForJavaScript(WebDriver driver) 
    new WebDriverWait(driver, 10).until(new Predicate<WebDriver>() 
                                            public boolean apply(WebDriver driver) 
                                                return ((JavascriptExecutor) driver).executeScript("return document.readyState")
                                                                                    .equals("complete");
                                            
                                        
    );

在 WebDriverWait 中,给定的 int 参数是它将继续尝试给定检查的秒数。如果达到设定的数量(上例中为10 秒),它将抛出TimeoutException

【讨论】:

【参考方案2】:

页面上可能仍在执行活动的 Javascript。在第一瞬间,它可能会以ExpectedCondition 无法分辨的方式对其进行修改。当我遇到意外行为问题时,我发现等待页面加载(包括 JavaScript 脚本)通常可以解决大部分问题。

要等待页面加载,您可以调用您的一些 Javascript 来检查文档的readyState。尝试等待页面加载,然后等待元素可点击。

快速搜索返回此 (code taken from here):

wait.until( new Predicate<WebDriver>() 
            public boolean apply(WebDriver driver) 
                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
            
        
    );

【讨论】:

感谢@Parker 的帮助,我在上面的代码中试过了,但还是不行 【参考方案3】:

以下方法似乎可以正常工作:

public void waitForPageLoad() 
    ExpectedCondition<Boolean> expectation = new
            ExpectedCondition<Boolean>() 
                public Boolean apply(WebDriver driver) 
                    return ((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete");
                
            ;
    try 
        Thread.sleep(1000);
        WebDriverWait wait = new WebDriverWait(driver, 30);
        wait.until(expectation);
     catch (Throwable error) 
        Assert.fail("Timeout waiting for Page Load Request to complete.");
    

【讨论】:

【参考方案4】:

尝试使用 PageFactory 实现,看看 selenium API 是否为您解决了问题。

public class OrderMyPizzaPage 
        @FindBy(css="form[action='/menu/startyourorder']")
        WebElement startMyOrder;

        public void startOrder() 
            startMyOrder.click();
        
    

太好了,现在如果我们假设其他所有事情都已成功完成,并且我们可以点击“开始您的订单”

 public void myExecutingMethod(WebDriver driver) 
        //previous steps occur and we've just performed interactions which should cause the 'start your order' button to be on the dom

        OrderMyPizzaPage orderPage = PageFactory.initElements(driver, OrderMyPizzaPage.class);
        orderPage.startOrder();

        //And now maybe it worked?
    

如果这不起作用,请尝试更强力的方法。为您的 WebDriverWait 创建一个自定义谓词,在发布前检查点击后条件是否为真。如果不正确,则让谓词重新执行点击!

由于时区问题,我无法查找 成功的点击后内容,因此在 sn-p。它被标记为 FIXME

public void startOrder(WebDriver driver) 
    WebDriverWait wait = new WebDriverWait(driver, 30);
    //Throttle the polling!
    wait.pollingEvery(1500, TimeUnit.MILLISECONDS);
    wait.until(new Predicate<WebDriver>() 
        public boolean apply(WebDriver input) 
            boolean successfulClick = false;
            //FIXME:  Need to check for something that should occur after the click happens successfully
            By lookupAfterClick = By.cssSelector("");
            WebElement nextElement = ExpectedConditions.visibilityOfElementLocated(lookupAfterClick).apply(input);
            if (nextElement == null) 
                System.out.println("Not there yet, Keep trying!");
                By startOrderLookup = By.cssSelector("form[action='/menu/startyourorder']");
                WebElement startOrder =ExpectedConditions.visibilityOfElementLocated(startOrderLookup).apply(input);
                if (startOrder != null) 
                    System.out.println("Clicking Start Order!");
                    startOrder.click();                        
                 else 
                    System.out.println("Start Order isn't visible!");
                
             else 
                System.out.println("Hey, That worked!");
                successfulClick = true;
            
            return successfulClick;
        
    );        

【讨论】:

【参考方案5】:

您是否尝试过使用 urlContains ?我遇到了你问的同样的问题,不知何故线程睡眠正常

详情如下

(new WebDriverWait(driver, 50)).until(ExpectedConditions.urlContains("/completing-profile?step=skin_type"));

【讨论】:

以上是关于WebDriver Wait '.until(ExpectedConditions.elementToBeClickable' 仅在我将 Thread.sleep(500) 添加到代码时才有效?的主要内容,如果未能解决你的问题,请参考以下文章

webdriver保存验证码截图

Selenium C# WebDriver:等到元素出现

Selenium webdriver 轮询时间

Golang之wait.Until 简单测试用例

C# Selenium:使用 wait.Until 时无法捕获异常

如何使用selenium webdriver来判断一个网页加载完毕