简体   繁体   English

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

[英]StaleElementReferenceException occurs when clicking on the buttons of a changed table (Python > Selenium Webdriver)

I keep geting a StaleElementReferenceException when I'm trying to test a webpage that contains a table. 当我尝试测试包含表的网页时,我不断收到StaleElementReferenceException。 The table has points [as well as some other info] and the status of two separate Blocking points, each of which has a toggle state button for 'Yes' and 'No'. 该表包含点(以及一些其他信息)以及两个单独的阻止点的状态,每个阻止点的状态都为“是”和“否”的切换状态按钮。

In this particular code, the process is: 在此特定代码中,过程为:

  1. Click the checkbox to reveal only the blocking points. 单击复选框仅显示阻塞点。
  2. If there are no blocking point in the table, you're DONE. 如果表中没有阻塞点,则说明您完成了。 Otherwise... 除此以外...
  3. Save the name of the point in the first row & check the first blocking status. 将点的名称保存在第一行并检查第一个阻止状态。 If set to 'Yes', change it to 'No.' 如果设置为“是”,请将其更改为“否”。
  4. Check to see if the point still remains. 检查该点是否仍然存在。 if so, change the second blocking status to 'No' and confirm the point has been deleted. 如果是这样,请将第二个阻止状态更改为“否”,并确认该点已被删除。

I added comments in the code to help follow my process: 我在代码中添加了注释,以帮助遵循我的流程:

    # << 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)

When a point has had all its Blocking states removed, it literally disappears from the table, which is the cause of my exception. 当某个点的所有“阻塞”状态都已删除时,它实际上从表中消失了,这是导致我异常的原因。 The code doesn't always fail on the same line, but when it fails, it's ALWAYS on a line where I attempt to click on the 'Yes' or 'No' button, more than likely due to the table changing after a point has been successfully removed from the table. 代码并非总是在同一行上失败,但是当它失败时,它总是在我试图单击“是”或“否”按钮的行上,这很可能是由于在一个点之后表发生了变化已成功从表中删除。

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

It sometimes makes it past this portion of the code & fails in a different portion where I'm attempting to click on a button either after I've.. (1) refreshed the page, or (2) have navigated to page two, where the XPATH addresses are the same, but the objects in the XPATH address have changed. 有时,它会超出代码的这一部分,并在我尝试单击按钮后的另一部分中失败。.(1)刷新页面,或(2)导航到第二页, XPATH地址相同,但XPATH地址中的对象已更改。 I do understand the reason I'm having this issue for the reasons listed here . 我确实了解由于此处列出的原因而遇到此问题的原因。 My issue seems to be consistent with, "The element is no longer attached to the DOM." 我的问题似乎与“元素不再附加到DOM”相一致。

Up to this point, I've tried using both time.sleep() and driver.implicitly_wait() in locations that could cause change to the table, but the issue still remains. 到目前为止,我尝试在可能导致表更改的位置中同时使用time.sleep()和driver.implicitly_wait(),但问题仍然存在。 How can I resolve this issue? 我该如何解决这个问题?

Using an inplicitly_wait() , if the time is set high enough, will resolve StaleElementReferenceException issue. 使用inplicitly_wait() ,如果时间设置得足够长,将解决StaleElementReferenceException问题。 However, the implicit wait also caused the test case to take a very long time to run. 但是,隐式等待也导致测试用例需要很长时间才能运行。 I resolved this issue using ideas I found here , here , and here . 我使用在这里这里这里找到的想法解决了这个问题。

The issue occurred because elements that were being referenced were no longer be attached to the DOM when changes were made to the table. 发生此问题的原因是,对表进行更改时,被引用的元素不再附加到DOM。 Therefore, I created definitions specifically for dealing with elements that may be stale. 因此,我专门为处理过时的元素创建了定义。

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() is used for confirming the an element is no longer stale. waitForNonStaleElement()用于确认元素不再陈旧。 waitForNonStaleElementClick() allows me click on an element that could be stale. waitForNonStaleElementClick()允许我单击可能是过时的元素。 waitForNonStaleElementText() allows me to retrieve text from an element that may be stale. waitForNonStaleElementText()允许我从可能过时的元素中检索文本。

I then rewrote the search code using these methods: 然后,我使用以下方法重写了搜索代码:

# << 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")

Hopefully, this will help someone if they run across the same issue I did. 希望这对某人遇到我遇到的相同问题会有所帮助。

If your problem is that you click on an element that doesn't already exist and you want to verify if element is present you can do the next: 如果您的问题是您单击了一个不存在的元素,并且想要验证是否存在该元素,则可以执行下一个操作:

  1. Find element using method that search for list of elements (returns also list) 使用搜索元素列表的方法查找元素(还返回列表)
  2. Check if there is any elements in the list (count > 0) 检查列表中是否有任何元素(计数> 0)
  3. If count is 0 then there was no elements found so it doesn't exist 如果count为0,则找不到元素,因此不存在

Also you can try using try-catch but it's more complicated. 您也可以尝试使用try-catch,但是它更复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM