单击更改的表的按钮时发生StaleElementReferenceException(Python> Selenium Webdriver)

时间:2015-08-20 14:33:28

标签: python selenium selenium-webdriver webdriver

当我尝试测试包含表格的网页时,我一直在设置StaleElementReferenceException。该表有点[以及其他一些信息]和两个单独的阻塞点的状态,每个阻塞点都有一个“是”和“否”的切换状态按钮。

在此特定代码中,过程为:

  1. 单击复选框以仅显示阻止点。
  2. 如果表格中没有阻止点,则表​​示您已完成。否则...
  3. 保存第一行中点的名称&检查第一个阻止状态。如果设置为“是”,请将其更改为“否”。
  4. 检查点是否仍然存在。如果是,请将第二个阻止状态更改为“否”,并确认该点已被删除。
  5. 我在代码中添加了注释,以帮助我遵循我的流程:

        # << Setup >>
        driver.get(url("/PointsTable/"))
        assertExpectedConditionTrue(driver, "By.XPATH", "//td")
    
        # < Confirm that the points blocking checkbox is enabled >
        if not driver.find_element_by_id("BlockedPoints").is_selected():
            assertExpectedConditionTrue(driver, "By.ID", "BlockedPoints")
            driver.find_element_by_id("BlockedPoints").click()
            assertCheckBoxEnabled(driver, "BlockedPoints")
    
        # < First check if any points have a blocking state >
        try:
            assertExpectedConditionTrue(driver, "By.XPATH", "//td[contains(text(), 'No data available in table')]", None, 3)
        except (NoSuchElementException):
            # < Until all the points are out of blocking state, begin changing blocking statuses
            #   to all the points >
            while True:
                # < Check if all the points are set to have no blocking statuses set to Yes >
                try:
                    assertExpectedConditionFalse(driver, "By.XPATH", "//td[contains(text(), 'No data available in table')]", None, 2)
                except (NoSuchElementException, TimeoutException):
                    break
    
                # < Save the name of the point
                # Check the first blocking status.  If it is blocking, set the block to No >
                assertExpectedConditionTrue(driver, "By.XPATH", "//td")
                myPointVal = driver.find_element_by_xpath("//td").text
    
                try:
                    assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[5]/div/button[@class='btn active btn-success btn-small point-button']", None, 2)
                except (NoSuchElementException):
                    assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[5]/div/button[@class='btn btn-small point-button']")
                    driver.find_element_by_xpath("//tbody/tr[1]/td[5]/div/button[@class='btn btn-small point-button']").click()
    
                # < Save the name of the point again.  Compare it to the original saved point
                #    If the name is the same, then the second blocking status needs to be set to No
                #    If the name is different, that means the point in question is no longer blocked >
                assertExpectedConditionTrue(driver, "By.XPATH", "//td")
                if myPointVal == driver.find_element_by_xpath("//td").text:
                    assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[6]/div/button[@class='btn btn-small point-button']")
                    driver.find_element_by_xpath("//tbody/tr[1]/td[6]/div/button[@class='btn btn-small point-button']").click()
                    assertExpectedConditionFalse(driver, "By.XPATH", "//td", myPointVal)
    

    当一个点已经删除了所有阻塞状态时,它实际上会从表中消失,这是我异常的原因。代码并不总是在同一行上失败,但是当它失败时,它总是在我尝试点击“是”或“否”按钮的行上,这很可能是因为在一个点之后表格发生变化已成功从表中删除。

    i.e. driver.find_element_by_xpath("//tbody/tr[1]/td[6]/div/button[@class='btn btn-small point-button']").click()
    

    它有时会超过代码的这一部分。在我试图点击某个按钮的不同部分失败了。(1)刷新页面,或者(2)导航到第2页,其中XPATH地址是相同的,但是对象在XPATH地址已更改。由于here列出的原因,我确实理解我遇到此问题的原因。我的问题似乎与“元素不再附加到DOM”一致。

    到目前为止,我已尝试在可能导致更改表的位置使用time.sleep()和driver.implicitly_wait(),但问题仍然存在。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

使用 inplicitly_wait(),如果时间设置得足够高,将解决StaleElementReferenceException问题。但是,隐式等待也导致测试用例需要很长时间才能运行。我使用我发现hereherehere的提示解决了这个问题。

出现此问题是因为在对表进行更改时,不再将正在引用的元素附加到DOM。因此,我专门为处理可能过时的元素创建了定义。

def waitForNonStaleElement(driver, type, element):
    strategy = {
            "id":           driver.find_element_by_id,
            "link_text":    driver.find_element_by_link_text,
            "name":         driver.find_element_by_name,
            "xpath":        driver.find_element_by_xpath
            }
    lhsType, rhsType = type.split(".", 1)
    find_element = strategy.get(rhsType.lower())

    try:
        find_element(element)
    except StaleElementReferenceException:
        waitForNonStaleElement(driver, type, element)
    except TypeError:
        raise TypeError("ERROR : CODE TO HANDLE \""+element+"\" TYPE NEEDS TO BE CREATED")


def waitForNonStaleElementClick(driver, type, element):
    strategy = {
            "id":           driver.find_element_by_id,
            "link_text":    driver.find_element_by_link_text,
            "name":         driver.find_element_by_name,
            "xpath":        driver.find_element_by_xpath
            }
    lhsType, rhsType = type.split(".", 1)
    find_element = strategy.get(rhsType.lower())

    try:
        waitForNonStaleElement(driver, type, element)
        find_element(element).click()
    except StaleElementReferenceException:
        waitForNonStaleElementClick(driver, type, element)
    except TypeError:
        raise TypeError("ERROR : CODE TO HANDLE \""+element+"\" TYPE NEEDS TO BE CREATED")


def waitForNonStaleElementText(driver, type, element):
    strategy = {
            "id":           driver.find_element_by_id,
            "link_text":    driver.find_element_by_link_text,
            "name":         driver.find_element_by_name,
            "xpath":        driver.find_element_by_xpath
            }
    lhsType, rhsType = type.split(".", 1)
    find_element = strategy.get(rhsType.lower())

    try:
        return find_element(element).text
    except StaleElementReferenceException:
        waitForNonStaleElementText(driver, type, element)
    except TypeError:
        raise TypeError("ERROR : CODE TO HANDLE \""+element+"\" TYPE NEEDS TO BE CREATED")

waitForNonStaleElement()用于确认元素不再陈旧。 waitForNonStaleElementClick()允许我点击可能过时的元素。 waitForNonStaleElementText()允许我从可能过时的元素中检索文本。

然后我使用这些方法重写了搜索代码:

# << Setup >>
driver.get(url("/PointsBlocking/"))
assertExpectedConditionTrue(driver, "By.XPATH", "//td")

if not driver.find_element_by_id("BlockedOnlyCheckbox").is_selected():
    assertExpectedConditionTrue(driver, "By.ID", "BlockedOnlyCheckbox")
    driver.find_element_by_id("BlockedOnlyCheckbox").click()
    assertCheckBoxEnabled(driver, "BlockedOnlyCheckbox")

waitForNonStaleElement(driver, "By.XPATH", "//td")

try:
    assertExpectedConditionTrue(driver, "By.XPATH", "//td", "No data available in table", 1)
except (TimeoutException):
    while True:
        try:
            assertExpectedConditionFalse(driver, "By.XPATH", "//td", "No data available in table", 1)
        except (TimeoutException):
            break

        assertExpectedConditionTrue(driver, "By.XPATH", "//td")
        pointName = waitForNonStaleElementText(driver, "By.XPATH", "//td")

        try:
            assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[5]/div/button[@class='btn active btn-success btn-small point-button']", None, 1)
        except NoSuchElementException:
            assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[5]/div/button[@class='btn btn-small point-button']")
            waitForNonStaleElementClick(driver, "By.XPATH", "//tbody/tr[1]/td[5]/div/button[@class='btn btn-small point-button']")

        tmp = waitForNonStaleElementText(driver, "By.XPATH", "//td")

        if pointName == tmp:
            assertExpectedConditionTrue(driver, "By.XPATH", "//tbody/tr[1]/td[6]/div/button[@class='btn btn-small point-button']")
            waitForNonStaleElementClick(driver, "By.XPATH", "//tbody/tr[1]/td[6]/div/button[@class='btn btn-small point-button']")
            waitForNonStaleElementClick(driver, "By.XPATH", "//td")

希望如果遇到与我相同的问题,这将有助于某人。

答案 1 :(得分:-1)

如果您的问题是您点击了一个尚不存在的元素,并且您想验证元素是否存在,您可以执行下一步:

  1. 使用搜索元素列表的方法查找元素(返回列表)
  2. 检查列表中是否有任何元素(count> 0)
  3. 如果count为0,则表示没有找到任何元素,因此它不存在
  4. 你也可以尝试使用try-catch,但它更复杂。