Selenium-这样混合隐式等待和显式等待可以吗? [英] Selenium - Is it okay to mix implicit wait and explicit wait like this?

查看:95
本文介绍了Selenium-这样混合隐式等待和显式等待可以吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是 udemy课程(摘自"Lets Kode它"),以使用Selenium和Java开发Web自动化框架. 但是,这不是Java问题.您只需要了解以下任何一种语言中的硒-javascript,python,ruby,c#& Java.

讲师已经开发了一个CustomDriver类,该类具有以下给出的方法/功能.该方法等待元素可单击,而不必在我们的代码中到处编写WebDriverWait语句.首先将隐式等待设置为零,然后进行显式等待,然后将隐式等待设置为框架中使用的原始值.

这种方法对我来说似乎还可以,但是我不确定.这样的隐式和显式等待混合会引起任何问题吗?

更新(2020年3月24日)-我已经知道,混合使用隐式和显式等待被认为是一种不好的做法,因为它可能导致无法预测的等待时间.我并不是在问不可预测的等待时间,因为已经有很多问题和文章了.

我要问的是,如果在进行显式等待之前每次将隐式等待设置为零,那可以吗?那还会引起不可预测的等待问题吗?会引起其他问题吗?

/*
 Click element when element is clickable
 @param locator - locator strategy, id=>example, name=>example, css=>#example,
                       tag=>example, xpath=>//example, link=>example
 @param timeout - Duration to try before timeout
 */
public void clickWhenReady(By locator, int timeout) {
    try {
        driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
        WebElement element = null;
        System.out.println("Waiting for max:: " + timeout + " seconds for element to be clickable");

        WebDriverWait wait = new WebDriverWait(driver, 15);
        element = wait.until(
                ExpectedConditions.elementToBeClickable(locator));
        element.click();
        System.out.println("Element clicked on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    } catch (Exception e) {
        System.out.println("Element not appeared on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }
}

解决方案

使用隐式等待和显式等待的问题归结为Selenium源代码中如何实现ExpectedConditions的缺陷.

让我通过分析以下代码来说明问题:

WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
WebElement element =  new WebDriverWait(driver, 5, 1000).until(ExpectedConditions.elementToBeClickable(By.id("some_id")));

  1. 我们初始化驱动程序并将隐式等待时间设置为10秒.这意味着driver.findElement()将等待10秒,直到找到元素为止,然后才抛出NoSuchElementException. 这是一个非常重要的要点.
  2. 然后,我们设置了一个明确的等待时间,持续时间为5秒,在轮询1000毫秒(1秒)之间的睡眠时间.这意味着WebDriverWait将每1秒到5秒轮询一次ExpectedConditions为true.如果ExpectedConditions返回true,则轮询将停止并返回ExpectedConditions中指定的对象.在上面的示例代码中,返回的对象是WebElement.如果5秒钟后ExpectedConditions为假,则会抛出TimeoutException(我们如此期望).现在该看看ExpectedConditions中发生了什么.
  3. ExpectedConditions.elementToBeClickable()代码具有以下语法.

    public static ExpectedCondition<WebElement> elementToBeClickable(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                WebElement element = visibilityOfElementLocated(locator).apply(driver);
                try {
                    if (element != null && element.isEnabled()) {
                        return element;
                    }
                    return null;
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    

  4. 上面的elementToBeClickable依次调用visibilityOfElementLocated()方法以确认元素是否可见.

    public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                try {
                    return elementIfVisible(driver.findElement(locator));
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    

    5.注意上面在visibilityOfElementLocated()方法中如何调用driver.findElement(locator).如果找不到该元素,则将隐式等待10秒.因此,驱动程序将等待10秒,直到它抛出NoSuchElementException.

但是请稍等(双关语不是故意的)!在elementToBeClickable()条件下,我们的显式等待时间不是设置为5秒超时吗?是的,但是隐式等待将首先应用. WebDriverWait将捕获NoSuchElementException并在10秒后而不是设置的显式等待5秒后抛出TimeoutException.这就是问题中的解决方案试图解决的问题.该解决方案尝试将隐式等待设置为0秒,以便正确执行显式等待条件,然后重置隐式等待.

问题中提供的实现确实完成了工作,但没有一个细节.隐式等待被硬编码为3秒,这是不理想的.您如何将隐式等待作为全局通用常量来提供是特定于案例的.我们在驱动程序上设置了隐式等待,我们可以期望像'driver'driver.manage().timeouts().getImplicitWait()这样的隐式等待.尽管很理想,但是不幸的是这不可能直接实现.有解决方法,@ forresthopkinsa在创建扩展版时有一个漂亮的有趣的解决方案获得隐式等待的驱动程序

更新(2020年3月24日)

问:这种方法对我来说似乎还可以,但我不确定.这样的隐式和显式等待混合会引起任何问题吗?

我要问的是,如果在进行显式等待之前每次将隐式等待设置为零,那可以吗?那还会引起不可预测的等待问题吗?会引起其他问题吗?

据我了解,将隐式等待设置为0,然后执行显式等待,然后再切换回去应该没有任何问题,因为在测试执行期间的任何时间点都没有设置隐式等待的限制. >

此外,如果考虑一下,从代码执行的角度来看,您实际上并没有在解决方案中混合使用隐式等待和显式等待.实际上,您在做相反的事情!如果您将隐式等待设置为某个非零值,然后执行显式等待,那么这就是真正的混合发生的地方.如果适用,则首先执行隐式,然后执行显式等待,从而导致不一致.如果将隐式等待设置为0,则可以从超时方程中删除隐式等待!

OP中的解决方案不会发生无法预测的等待的问题.

Here is a udemy course (from "Lets Kode It") to develop a web automation framework with selenium and Java. But, this is not a java question. You only need to know selenium in any of these languages - javascript, python, ruby, c# & java.

The instructor has developed a CustomDriver class which has the method/function given below. The method waits for an element to be clickable, without having to write WebDriverWait statements everywhere in our code. It first sets the implicit wait to zero, does an explicit wait and then sets the implicit wait to the original value which was being used in the framework.

This approach seems okay to me, but I am not sure about it. Could mixing implicit and explicit waits like this cause any problems?

UPDATE (March 24, 2020) - I already know that mixing implicit and explicit waits is considered a bad practice because it can lead to unpredictable wait times. I am not asking about the unpredictable wait times because there are plenty of questions and articles on that already.

Instead, I am asking that if the implicit wait is set to zero every time before doing an explicit wait, then is that okay? Will that still cause the problems of unpredictable waits? Will it cause other problems ?

/*
 Click element when element is clickable
 @param locator - locator strategy, id=>example, name=>example, css=>#example,
                       tag=>example, xpath=>//example, link=>example
 @param timeout - Duration to try before timeout
 */
public void clickWhenReady(By locator, int timeout) {
    try {
        driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
        WebElement element = null;
        System.out.println("Waiting for max:: " + timeout + " seconds for element to be clickable");

        WebDriverWait wait = new WebDriverWait(driver, 15);
        element = wait.until(
                ExpectedConditions.elementToBeClickable(locator));
        element.click();
        System.out.println("Element clicked on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    } catch (Exception e) {
        System.out.println("Element not appeared on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }
}

解决方案

The problem of using implicit wait and explicit wait boils down to the flaws in how ExpectedConditions are implemented in Selenium source code.

Let me explain the problem by analyzing the below code:

WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
WebElement element =  new WebDriverWait(driver, 5, 1000).until(ExpectedConditions.elementToBeClickable(By.id("some_id")));

  1. We initialize the driver and set implicit wait to 10 seconds. This means that the driver.findElement() will wait for 10 seconds untill element is located before throwing NoSuchElementException. This is a very important point to note.
  2. Then we set up an explicit wait with 5 seconds duration and sleep time between polling of 1000 milliseconds (1 second). This means that the WebDriverWait will poll the ExpectedConditions to be true every 1 second till 5 second. If ExpectedConditions return true, the polling will stop and the Object specified in ExpectedConditions is returned. In the example code above returned object is a WebElement. In case ExpectedConditions is false after 5 seconds, a TimeoutException is thrown (OR SO WE EXPECT). Now time to see what happens in ExpectedConditions.
  3. The ExpectedConditions.elementToBeClickable() code has the below syntax.

    public static ExpectedCondition<WebElement> elementToBeClickable(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                WebElement element = visibilityOfElementLocated(locator).apply(driver);
                try {
                    if (element != null && element.isEnabled()) {
                        return element;
                    }
                    return null;
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    

  4. The elementToBeClickable above in turn calls visibilityOfElementLocated() method to confirm if element is visible.

    public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                try {
                    return elementIfVisible(driver.findElement(locator));
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    

    5.Notice how driver.findElement(locator) is invoked above in the visibilityOfElementLocated() method. In case the element is not found the implicit wait of 10 seconds will apply. So driver will wait for 10 seconds untill it throws NoSuchElementException.

But wait (pun not intentional)! isn't our explicit wait set to timeout at 5 seconds on elementToBeClickable() condition? Yes it is, but the implicit wait will be applied first. The WebDriverWait will catch the NoSuchElementException and throw a TimeoutException after 10 seconds instead of the set explicit wait of 5 seconds. Therein lies the problem which the solution in the question is trying to address. The solution attempts to set the implicit wait to 0 seconds so the explicit wait condition is executed correctly and then resets the implicit wait.

The implementation provided in the Question does get the job done sans one detail. The implicit wait is hard coded to 3 seconds which is not ideal. How you provide the implicit wait as a global universal constant is very case specific. We are setting implicit wait on driver and we can expect the implicit wait like 'driver' driver.manage().timeouts().getImplicitWait(). Though ideal, unfortunately this is NOT possible directly. There are workarounds, @forresthopkinsa has a pretty interesting solution on creating extended drivers for getting implicit waits

UPDATE (March 24, 2020)

Q: This approach seems okay to me, but I am not sure about it. Could mixing implicit and explicit waits like this cause any problems?

I am asking that if the implicit wait is set to zero every time before doing an explicit wait, then is that okay? Will that still cause the problems of unpredictable waits? Will it cause other problems ?

As far as I understand, there should not be any problems with setting implicit wait to 0 and then performing explicit wait and then switching back as there is no restriction on setting implicit wait at any point in time during test execution.

Also, if you think about it you are really not mixing implicit wait and explicit wait in the solution from code execution point of view. In fact you are doing the opposite! If you have set implicit wait to a certain non-zero value and then performed explicit wait, that is where the real mixing happens. First implicit is executed if applicable and then explicit wait executes leading to inconsistencies. If you set implicit wait to 0, you take out the implicit wait from the timeout equation!

The problem of unpredictable waits would not occur with the solution in OP.

这篇关于Selenium-这样混合隐式等待和显式等待可以吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆