【问题标题】:Wait for element - WebDriver - PageObject pattern等待元素 - WebDriver - PageObject 模式
【发布时间】:2013-09-21 12:31:11
【问题描述】:

只要我使用 PageObject 模式,我就想知道我应该在哪里等待动态页面上的元素。假设我们有测试方法和 pageObject 类。我应该做类似的事情(在测试方法中):

  1. 点击按钮
  2. 等待元素显示
  3. 验证元素(例如包含方法 isElementDisplayed())

或者也许还有其他好的做法来等待元素?也许我们应该等待 PageObject.class 中 isElementDisplayed 方法中的元素?

【问题讨论】:

    标签: java selenium webdriver selenium-webdriver pageobjects


    【解决方案1】:

    你应该在你的页面对象类中等待元素,而不是在测试类中,因为你的元素应该在页面对象类中定义,测试类不应该知道任何元素、选择器或类似的东西。恕我直言,测试应该只包含描述测试流程的方法调用链,与网站和底层 DOM 的所有交互都应该在 Page Object 类中进行。

    因此,等待某个元素出现的过于冗长的方法可能类似于:

    private final By yourElement = By.id("id");
    @Override
    public void isLoaded() throws Error {
        new FluentWait<WebDriver>(driver)
                .withTimeout(60, TimeUnit.SECONDS)
                .pollingEvery(1, TimeUnit.SECONDS)
                .ignoring(NoSuchElementException.class)
                .ignoring(StaleElementReferenceException.class)
                .until(new Function<WebDriver, Boolean>() {
                    @NotNull
                    @Override
                    public Boolean apply(WebDriver webDriver) {
                        WebElement element = driver.findElement(yourElement);
                        return element != null && element.isDisplayed();
                    }
                });
    }
    

    简单来说,该函数轮询 DOM 60 秒(每 1 秒)以查看元素是否存在于 DOM 中并且它是否可见(意味着高度和宽度大于 1px)。如果元素存在(并显示),则函数返回找到的元素并停止轮询(尽管isLoaded() 方法在这种特殊情况下不返回元素)。

    忽略NoSuchElementException 是有意义的,findElement 方法可以在找不到元素的情况下抛出它,而StaleElementException 表示对元素的引用现在是“陈旧的” - 元素没有更长的时间出现在页面的 DOM 上。这通常意味着,某些东西(最常见的是 JS)已经修改了 DOM,并且引用不再有效,因此 WebDriver 需要再次查找它。

    当然,更短的代码也可以解决问题,例如:

        new WebDriverWait(driver, 60)
                .until(ExpectedConditions.visibilityOf(someWebElement));
    

    documentation 在这方面其实很不错。

    编辑:回复评论:

    好的,明白了。但是如果点击某些元素后存在元素怎么办? 按钮等?

    假设您有一个场景,您有一个按钮,单击该按钮后会出现一个文本框,您想与它进行交互。

    public class PageObject extends LoadableComponent<PageObject>{
    
        public PageObject() throws Exception {
            driver = getWebDriver();
            PageFactory.initElements(driver, this);
            isLoaded();
        }
        private WebDriver driver = null;
    
        @FindBy(id = "yourButton")
        private WebElement button;
    
        @FindBy(id = "textBoxThatAppears")
        private WebElement txtBox;
    
        @Override
        public void isLoaded() throws Error {
            // Initial loading, called when creating the page object to make sure that the page is loaded to a state where it is ready to interact with us, in our case it means that button is present in DOM and visible.
            waitForVisibility(button);
        }
    
        private void waitForVisibility(WebElement element) throws Error{
               new WebDriverWait(driver, 60)
                    .until(ExpectedConditions.visibilityOf(element));
        }
    
        public void clickButton(){
            button.click();
    
        }
    
        public void interactWithTextbox(String text){
            // Wait for txtBox to be visible, then send text
            waitForVisibility(txtBox);
            txtBox.sendKeys(text);
    
           // EDIT 27.04.14: 
           // Actually you should not do the assertion here or anywhere in 
           // the pageObject, because when reusing the method in some other test, you might
           // not want to assert, you might wonder that why wouldn't you assert some 
           // specific condition every time, but I would throw that question right back 
           // to you and ask: What is the point of checking the exact same thing over and 
           // over again. There are 2 things, firstly the assertion takes resources (and
           // that can become important when test suite grows, secondly your tests can 
           // simply start failing at the same point when one little condition is not as
           // it should be. Also, having the asserts in the test, makes the test more
           // readable and understandable for others.
             // end edit 27.04.14
            // Next line is no longer recommended by this answer.
             // assert that something happened that you expected.
        }
    
    }
    

    现在你的测试类:

    public void TestClass {
    
         @Test
         public void testClickButtonAndInteractWithTextbox(){
             // Initiate the page object
             Pageobject po = new PageObject();
             po.clickButtonAndWaitForTextbox();
             po.interactWithTextbox("blabla");
             // edit 27.04.14
             assertSomethingGoodHappened();
         }
    }
    

    【讨论】:

    • 感谢您的回答。您的解决方案说我们等待每个元素。如果我想等待一个元素,例如 60 秒,而另一个是静态的,那么我只能等待 15 秒怎么办?我的问题是:如何定义这种等待测试?测试用例应该等待吗?还是应该隐藏在 PageObject 中?
    • 不,它不会等待每个元素,它会等待您指定的元素,例如第二段代码等待“someWebElement”(只有一个WebElement类型的项目,而不是列表所有定义的元素或任何东西)。如果你想在你的测试中等待,这意味着你必须在你的测试类中定义这个元素,这会超出页面对象的目的,所以我会说只在你的页面对象中等待,如果元素是在页面上动态创建,那么您应该每次都等待您想要与之交互的特定元素。
    • 好的,明白了。但是如果点击某个按钮等后出现元素怎么办?
    • clickButtonAndWaitForTextbox() - 我的主要问题是关于这个方法。好的,所以我们必须等待点击中的元素。那么,我们应该总是在与元素交互之前等待它,还是在交互之前检查它的存在?
    • 我不这样做 - 在与它交互之前检查每个元素的存在,因为我看不出这有什么意义。我只在必要时等待(即动态创建元素、发生数据绑定、一些异步 javascript 做某事等)。我的意思是,你自己知道什么时候会发生这样的事情,并编写代码来支持它。
    【解决方案2】:

    可以在此处使用selenium testing-frameworks - ISFW 之一的另一个有效的测试页面概念(自 selenium 1 以来)。它具有延迟加载元素、自定义组件功能和自动等待(不是降低性能的隐式等待)、带有元素的内置等待方法和其他对基于 ajax 的应用程序非常有用的功能。

    它为开发测试用例提供了以下构建块:

    1. 测试页
    2. 组件
    3. 测试步骤

    此外Reporting 也是描述性的。

    【讨论】:

      猜你喜欢
      • 2011-12-03
      • 1970-01-01
      • 2012-07-28
      • 2012-03-26
      • 1970-01-01
      • 2016-12-25
      • 1970-01-01
      • 1970-01-01
      • 2013-08-06
      相关资源
      最近更新 更多