【问题标题】:Find Element doesn't always succeed. Can this be improved?查找元素并不总是成功。这可以改进吗?
【发布时间】:2017-07-13 10:06:36
【问题描述】:

我有一个特定的脚本,它引用了我测试的 UI 中的某些元素。一些元素通过它们的 ID 定位,一些通过它们的 Xpath 定位。此脚本在特定场景后从我的 UI 收集一些信息。我反复调整此脚本以找到特定的结果。

大多数时候所有的元素都被找到了。碰巧在大约 100 个(有时是几十个周期之后)循环之后,脚本停止并且大多数时间它与未找到的元素有关。 我知道这种自动化故障很常见,我还假设有时它可能与我的 UI(甚至与 UI 管理的设备)相关的某个问题有关。

是否可以/建议以更能容忍某些情况的方式设计我的自动化(顺便说一句,我确实使用了内置的隐式等待)?

是否建议在这些测试之王中创建重试机制?

这是脚本:

from selenium import webdriver
from datetime import datetime
import time


def main():
    i = 0
    while True:
        i = i +1
        execute_code(i)

        if i == 500:
            break

def Cold_Restart(browser):

    browser.switch_to.default_content()

    browser.switch_to.frame('box_menu')
    browser.switch_to.frame('box_menu')
    SysBtn = browser.find_element_by_id('System')
    SysBtn.click()

    browser.switch_to.default_content()

    browser.switch_to.frame('main_menu')
    Mainten_Btn = browser.find_element_by_id('Maintenance')
    Mainten_Btn.click()

    browser.switch_to.default_content()

    browser.switch_to.frame('main_body')
    Mntn_Rst_Tab = browser.find_element_by_id('tab_restart')
    Mntn_Rst_Tab.click()

    browser.switch_to.frame('maint_sys')
    Cold_Rst_Btn = browser.find_element_by_id('cold_restart')
    Cold_Rst_Btn.click()

    #In order to confirm the Alert Message I first need to switch to the alert pop-up message and then accept it
    alertDialog = browser.switch_to_alert()
    alertDialog.accept()

    time.sleep(205)

    return


def Port_Admin_Down(browser):

    browser.switch_to.default_content()

    browser.switch_to.frame('box_menu')
    browser.switch_to.frame('box_menu')
    Port_19 = browser.find_element_by_id('Port-19')
    Port_19.click()


    browser.switch_to.default_content()

    # Show the Port Configuration TAB
    browser.switch_to.frame('main_body')
    CFP2_Info = browser.find_element_by_id('tab_general')
    CFP2_Info.click()

    browser.switch_to.frame('config_port') # Move to the inner frame that holds the configuration fields and buttons

    Admin_Down_Btn = browser.find_element_by_id('red_button')
    Admin_Down_Btn.click()

    #In order to confirm the Alert Message I first need to switch to the alert pop-up message and then accept it
    alertDialog = browser.switch_to_alert()
    alertDialog.accept()

    time.sleep(5)
    return


def Port_Admin_Up(browser):

    browser.switch_to.default_content()

    browser.switch_to.default_content()

    browser.switch_to.frame('box_menu')
    browser.switch_to.frame('box_menu')
    Port_19 = browser.find_element_by_id('Port-19')
    Port_19.click()
    browser.switch_to.default_content()

    # Show the Port Configuration TAB
    browser.switch_to.frame('main_body')
    CFP2_Info = browser.find_element_by_id('tab_general')
    CFP2_Info.click()

    browser.switch_to.frame('config_port') # Move to the inner frame that holds the configuration fields and buttons

    Admin_Down_Btn = browser.find_element_by_id('green_button')
    Admin_Down_Btn.click()

    #In order to confirm the Alert Message I first need to switch to the alert pop-up message and then accept it
    alertDialog = browser.switch_to_alert()
    alertDialog.accept()    

    time.sleep(5)
    return

def Gui_Login(browser, URL):
    browser.get(URL)
#    browser.implicitly_wait(20) # Implicit wait


    browser.maximize_window()

    # Find the User Name text box and fill the User name
    user_name_box = browser.find_element_by_id('u_name_box')
    user_name_box.click()
    user_name_box.send_keys('admin')

    # Find the Password text box and fill the Password
    user_pass_box = browser.find_element_by_id('u_pass_box')
    user_pass_box.click()
    user_pass_box.send_keys('admin')

    #webdriver.ActionChains(driver).send_keys(Keys.ESCAPE).perform()

    login_button = browser.find_element_by_id('login_but')
    login_button.click()

    return

def Gui_Logout(browser):

    browser.switch_to.default_content() # Get back to the default starting point


    # In order to click the Logout button I needed to pass through two frames
    browser.switch_to.frame('box_menu')
    browser.switch_to.frame('box_menu')
    logout_btn = browser.find_element_by_id('Logout')
    logout_btn.click()

    return

def Sample_Uplink(browser):

    browser.switch_to.default_content()

    # Go to the Configuration Pane (main_menu)
    browser.switch_to.frame('main_menu')
    Configuration_Btn = browser.find_element_by_id('Configuration')
    Configuration_Btn.click()

    browser.switch_to.default_content()

    # Go to the Uplink 1 CFP2 information and take the Rx Pwr
    browser.switch_to.frame('box_menu')
    browser.switch_to.frame('box_menu')
    Port_19 = browser.find_element_by_id('Port-19')
    Port_19.click()

    browser.switch_to.default_content()

    # Show the Optic Module information TAB
    browser.switch_to.frame('main_body')
    CFP2_Info = browser.find_element_by_id('tab_XFP')
    CFP2_Info.click()

    # Collect the Rx Pwr from the CFP2 Info screen
    browser.switch_to.frame('config_port') # Move to the inner frame that holds all the tables
    #browser.find_element_by_class_name('table_round_corner')
    Rx_Pwr = browser.find_element_by_xpath('html/body/form/div[1]/div/table/tbody/tr[2]/td[2]') # Take the Rx Pwr according to its Xpath
    # print (Rx_Pwr.text) # print the Rx Pwr result to screen
    RcvPwr = Rx_Pwr.text

    # Collect the OSNR measurement from the CFP2 Info screen
    OSNR = browser.find_element_by_xpath('html/body/form/div[1]/div/table/tbody/tr[4]/td[2]')
    OSNR_Lvl = OSNR.text


#    time.sleep(5)
    return (RcvPwr, OSNR_Lvl)



def Save_2_File(Rx_Pwr, OSNR_Lvl, i):
    file = open("test_results.txt", "a")
    file.write("%i. " %i)
    file.write(datetime.now().strftime('%H:%M:%S %d-%m-%Y    ')) # Print Time & Date to the text file 
    file.write(Rx_Pwr) # Print the Rx_Pwr to the text file
    file.write('%10s' %(OSNR_Lvl)) # Format the placement of the OSNR value
    file.write('\n') # Make sure that the next iteration will write the results in the next line
    file.close() # Closing the file
    return


def execute_code(i):
    profile = webdriver.FirefoxProfile()
    profile.accept_untrusted_certs = True

    browser = webdriver.Firefox(firefox_profile = profile)
    browser.implicitly_wait(20) # Implicit wait


    URL1 = 'http://10.0.1.131' #  First device that will be Cold Restarted
    Gui_Login(browser, URL1)

    Cold_Restart(browser)
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)

    URL2 = 'http://10.0.1.134'
    Gui_Login(browser, URL2)

    Cold_Restart(browser)
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)


    browser.get(URL1)

    Port_Admin_Down(browser)
    Port_Admin_Up(browser)

    time.sleep(5)

    browser.get(URL2)   

    #Get Rx Pwr and OSNR and save in file
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)


    Port_Admin_Down(browser)
    Port_Admin_Up(browser)

    time.sleep(5)

    browser.get(URL1)   

    #Get Rx Pwr and OSNR and save in file
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)


    browser.quit()


if __name__ == '__main__':
    main()

【问题讨论】:

标签: python python-3.x selenium selenium-webdriver automated-tests


【解决方案1】:

如果失败,我会 wait 获取预期元素,except NoSuchElementException

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "Maintenance"))
    )
except NoSuchElementException:
    # Handle your error case here
else:
    element.click()

此时你可以优雅地停止,重试最后一次调用,直接跳过这个测试,等等……


查看python selenium waits doc


为 cmets 编辑

我不太喜欢这个,但是如果添加一些行让你感到困扰,你总是可以为此制作一种包装器:

def wait_for_func(driver, element_tuple, func_success, func_failed, time=10):
    try:
        element = WebDriverWait(driver, time).until(
            EC.presence_of_element_located(element_tuple)
        )
    except NoSuchElementException:
        return None, func_failed(element)
    else:
        return element, func_success(element)

def do_click(element):
    return element.click()

def do_print():
     return 'Something gone wrong'

element, result = wait_for_func(driver, (By.ID, 'Maintenance'), func_success=do_click, func_failed=do_print)

这将避免在任何地方重复 try/except,但你肯定知道Zen of Python

显式优于隐式。 简单胜于复杂。 复杂胜于复杂。

无论如何,如果你做这样的事情,你会注意到只为单击元素定义一个函数并不是很好,所以 lambda 可以帮助你:

wait_for_func(driver, (By.ID, 'Maintenance'), lambda el: el.click(), do_print)

我发现lambda 没有那么漂亮,但如你所愿。

【讨论】:

  • 通过这样做,我需要对我在自动化中引用的任何元素重复此方法。对?看起来开销太大了,是不是常规方式?
  • 我不自信,因为我是 Python 新手。我不熟悉正确编写代码的任何正确约定。非常感谢您的回答,我会尝试的。我唯一想要的额外内容是知道在我搜索(并想要执行操作)元素的任何时候使用 try catch 是否“正确”。顺便说一句,对不起,你能解释一下“lambda el: el.click()”吗?
  • 是的,尝试/除了是总是你应该比其他所有可能性更喜欢的。这甚至有一个名字:“请求宽恕比请求许可更容易”(EAFP
  • 你能解释一下“lambda el: el.click()”吗?为什么我不应该使用这个约定?
  • python lambda, Why lambda are useful。简而言之,lambda 是一种在不声明的情况下定义函数的方法。如果您只需要一次,它会避免创建一个函数。这不是很好,因为Readability counts. :)
【解决方案2】:

如果您想避免某些循环中的错误。 try/catch 会有所帮助。

首先:将一些用于初始化/退出 webdriver 的代码从 execute_code() 移动到 main,然后尝试/捕获 execute_code()。

def main():
    i = 0
    while True:
        i = i +1
        profile = webdriver.FirefoxProfile()
        profile.accept_untrusted_certs = True
        browser = webdriver.Firefox(firefox_profile = profile)
        browser.implicitly_wait(20) # Implicit wait
        try:
            execute_code(i, browser)
            browser.quit()
            if i == 500:
                break
        except:
            browser.quit()

第二:修改execute_code()。添加浏览器作为参数。删除代码以初始化/退出 webdriver。

def execute_code(i, browser):
    URL1 = 'http://10.0.1.131' #  First device that will be Cold Restarted
    Gui_Login(browser, URL1)

    Cold_Restart(browser)
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)

    URL2 = 'http://10.0.1.134'
    Gui_Login(browser, URL2)

    Cold_Restart(browser)
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)


    browser.get(URL1)

    Port_Admin_Down(browser)
    Port_Admin_Up(browser)

    time.sleep(5)

    browser.get(URL2)   

    #Get Rx Pwr and OSNR and save in file
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)


    Port_Admin_Down(browser)
    Port_Admin_Up(browser)

    time.sleep(5)

    browser.get(URL1)   

    #Get Rx Pwr and OSNR and save in file
    (RcvPwr, OSNR_Lvl) = Sample_Uplink(browser)
    Save_2_File(RcvPwr, OSNR_Lvl, i)

【讨论】:

  • 你能解释一下为什么你认为你的修改会达到要求的效果吗?
  • @MosheS。如果元素显示时间过长,可以使用wait。但是,如果元素由于错误而没有显示,wait 将无济于事。你需要 try/catch 来覆盖所有的语句。否则,您需要为find_element 创建自己的扩展方法来捕获错误。
  • 如果我理解你的实现是正确的,当我遇到一个找不到某个元素的循环时,我的自动化不会被中断,而是会继续。
  • @MosheS。正确的。当异常是预期行为时,我们需要 try/catch。您可以选择 try/catch 整个代码块,或创建扩展方法并在方法中添加 try/catch。如果您对扩展程序感兴趣,我可以为您提供另一个答案。
  • 我对扩展方法很感兴趣。我会非常感激。我也相信像我这样的其他初学者会从这种例子中学到很多
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多