【问题标题】:How can I get Selenium Web Driver to wait for an element to be accessible, not just present?如何让 Selenium Web 驱动程序等待元素可访问,而不仅仅是存在?
【发布时间】:2012-02-28 00:37:30
【问题描述】:

我正在为 Web 应用程序编写测试。某些命令会拉出对话框,这些对话框具有可见但暂时不可用的控件。 (它们是灰色的,但 webdriver 仍然认为它们是可见的)。

我如何告诉 Selenium 等待元素实际可访问,而不仅仅是可见?

    try:
        print "about to look for element"
        element = WebDriverWait(driver, 10).until(lambda driver : driver.find_element_by_id("createFolderCreateBtn"))
        print "still looking?"
    finally: print 'yowp'

这是我尝试过的代码,但它在按钮可用之前“看到”按钮,并且基本上在假定的“等待”之后收费。

请注意,我可以在代码中填充 10 秒的睡眠时间,而不是这样,代码会正常工作,但这是丑陋、不可靠且效率低下的。但它确实证明了问题只是“点击”命令在控件的可用性之前竞争。

【问题讨论】:

  • 毫无疑问它工作正常。我正在尝试正确定义它应该等待什么。在这种情况下,一个特定的元素,当它可用时,不仅仅是可见的。
  • 是的,我不准确。我的意思是当我希望它暂停时,我的测试已经过去了。错误在于我作为程序员,而不是 selenium 作为工具。
  • 这个问题挽救了我的工作。
  • 澄清一下:我相信 Selenium 中 findElement 的默认行为也是检查可见性。这与在 DOM 上检查“存在”不同,即使某些东西不可见,它也可能返回 true。

标签: python selenium webdriver


【解决方案1】:

我假设事件时间线是这样的:

  1. 页面上没有需要的元素。
  2. 需要的元素出现,但被禁用:
    <input type="button" id="createFolderCreateBtn" disabled="disabled" />
  3. 需要的元素已启用:
    <input type="button" id="createFolderCreateBtn" />

目前您正在按 id 搜索元素,并且您在第 2 步中找到了一个,这比您需要的要早。你需要做的是通过xpath搜索它:

//input[@id="createFolderCreateBtn" and not(@disabled)]

这就是区别:

from lxml import etree


html = """
<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />
"""

tree = etree.fromstring(html, parser=etree.HTMLParser())

tree.xpath('//input[@id="createFolderCreateBtn"]')
# returns both elements:
# [<Element input at 102a73680>, <Element input at 102a73578>]


tree.xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
# returns single element:
# [<Element input at 102a73578>]

总结一下,这里是你的固定代码:

try:
    print "about to look for element"
    element_xpath = '//input[@id="createFolderCreateBtn" and not(@disabled)]'
    element = WebDriverWait(driver, 10).until(
            lambda driver : driver.find_element_by_xpath(element_xpath)
    )
    print "still looking?"
finally: 
    print 'yowp'

更新:
使用实际的 webdriver 重新粘贴相同的内容。
这是example.html 页面代码:

<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />

这是 ipython 会话:

In [1]: from selenium.webdriver import Firefox

In [2]: browser = Firefox()

In [3]: browser.get('file:///tmp/example.html')

In [4]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn"]')
Out[4]: 
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75110>,
 <selenium.webdriver.remote.webelement.WebElement at 0x103f75150>]

In [5]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
Out[5]: 
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75290>]

更新 2:

它也适用于此:

<input type="button" id="createFolderCreateBtn" disabled />

【讨论】:

  • 我认为这为我指明了正确的方向,但似乎“禁用”不是正确的属性。我想我需要在它不可用的时候用萤火虫来捕捉该死的东西。我希望我的视频游戏技能能够应对速度挑战。
  • @SkipHuffman,它也很容易成为一个 css 类。要捕获所需的页面状态: 1. 在找到元素后立即将 browser.page_source 值存储在某个变量中。 2. 将该变量存储在.html 文件中。 3.用chrome/firefox打开,用firebug/developer console检查。
  • css 看起来可以做到。 'type="button" disabled="disabled" id="createFolderCreateBtn"' 在禁用时似乎是该控件独有的。当按钮处于活动状态时,它会完全失去“禁用”属性。 (有道理。)现在我只需要修改 css 选择器来进行“这个,但不是那个”排序。
  • (解决这个问题。也许我的思考过程会帮助其他人)。我可以找到带有 element_css = 'button[type="button"][id="createFolderCreateBtn"]' 的按钮,所以它可以搜索参数列表(一个按钮的东西,它的类型为“button”,id 为“createFolderCreateBtn”)好。但是现在我需要添加一个否定搜索,如果属性包含'disabled="disabled"',它应该会失败。现在我想我可以通过编程方式完成它,但我怀疑如果我对搜索字符串更聪明一点,我会得到我需要的。
  • @SkipHuffman 仔细阅读我最初的回答。我给了你有效的 xpath 选择器,它匹配未禁用的按钮://input[@id="createFolderCreateBtn" and not(@disabled)],注意not(@disabled) 部分。
【解决方案2】:
    print time.time()
    try:
        print "about to look for element"
        def find(driver):
            e = driver.find_element_by_id("createFolderCreateBtn")
            if (e.get_attribute("disabled")=='true'):
                return False
            return e
        element = WebDriverWait(driver, 10).until(find)
        print "still looking?"
    finally: print 'yowp'
    print "ok, left the loop"
    print time.time()

这就是我们最终的结果。 (感谢 lukeis 和 RossPatterson。)请注意,我们必须按 id 查找所有项目,然后按“禁用”过滤。我更喜欢单一的搜索模式,但你能做什么?

【讨论】:

    【解决方案3】:

    我认为这些方面的东西也应该起作用:

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions
    
    browser = webdriver.Firefox()
    wait = WebDriverWait(browser, 30)
    wait.until(expected_conditions.presence_of_element_located((By.XPATH, "//*[@id='createFolderCreateBrn' and not(@disabled)]")))
    

    【讨论】:

      【解决方案4】:

      这里已经发布了一些很棒的答案,但我想我会添加我的解决方案。显式等待等是用于硒测试的重要功能。但是,显式等待仅执行您只能设置一次的Thread.Sleep() 的功能。下面的功能是我用来“剃掉”几分钟的功能。它一直等到元素“可访问”。

          //ALTERNATIVE FOR THREAD.SLEEP
      public static class Wait
      {  
          //public static void wait(this IWebDriver driver, List<IWebElement> IWebElementLIst)
          public static void wait(this IWebDriver driver, By bylocator)
          {
              bool elementPresent = IsPresent.isPresent(driver, bylocator);
              while (elementPresent != true)
              {
                  Thread.Sleep(1000);
      
                  elementPresent = IsPresent.isPresent(driver, bylocator); 
      
              }
      
          }
      
      }
      

      它在 C# 中,但适应它并不难。希望这可以帮助。

      【讨论】:

      • 你需要小心使用'isPresent',因为当item在DOM上但不可见时它会返回true。在此线程中提出的问题的上下文中,您应该改用“isVisible”。此外,如果您的 Selenium 绑定允许,使用 ExpectedCondition 也是一个好主意。事实上, findElement 的默认行为是(我认为) .isVisible 而不是 .isPresent 。
      • 如果元素在 1010 毫秒或更长时间后启用怎么办?你的测试会失败。如果元素在 300 毫秒到 500 毫秒之间加载,那么您的测试就是在浪费时间。时间不多,但是当你使用这个等待几个元素时,它可以加起来。
      猜你喜欢
      • 2015-09-15
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      • 2019-11-02
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多