【问题标题】:Why does the python-selenium-webdriver 'quit' not quit?为什么 python-selenium-webdriver '退出' 不退出?
【发布时间】:2018-07-20 02:24:11
【问题描述】:

我有一个非常复杂的py.test python-selenium 测试设置,我在py.test 固定装置内创建了一个Firefox webdriver。以下是我在做什么的一些想法:

'driver.py':

class Driver(object):
    """
    Driver class with basic wrappers around the selenium webdriver
    and other convenience methods.
    """
    def __init__(self, config, options):
        """Sets the driver and the config.
        """

        self.remote = options.getoption("--remote")
        self.headless = not options.getoption("--with-head")    
        if self.headless:
            self.display = Display(visible=0, size=(13660, 7680))
            self.display.start()

        # Start the selenium webdriver
        self.webdriver = fixefox_module.get_driver()

'conftest.py':

@pytest.fixture
def basedriver(config, options):

    driver = driver.Driver(config, options)

    yield driver

    print("Debug 1")
    driver.webdriver.quit()
    print("Debug 2")

在运行测试时,我只能看到 Debug 1 打印出来。整个过程在这一点上停止,似乎没有继续进行。整个硒测试卡在webdriver.quit)

但是,测试成功完成...

这种行为可能是什么原因?

附录:

执行挂起的原因似乎是由于未保存数据而询问用户是否要离开页面的弹出窗口。这意味着quit 方法的文档不正确。它指出:

Quits the driver and close every associated window.

【问题讨论】:

  • 最常见的 selenium 异常是由 selenium 驱动程序不匹配引起的。您的 geckodriver 版本是否与您正在测试的 Firefox 版本匹配?
  • 测试工作正常。只是webdriver的quit方法似乎卡住了……
  • 您可以先尝试使用driver.webdriver.close() 方法吗?那个工作正常吗?
  • 这会产生错误SessionNotCreatedException: Message: Tried to run command without establishing a connection...
  • 奇怪,我在这个问题 (github.com/mozilla/geckodriver/issues/1071) 中看到了一条评论,证实了我的想法:如果只打开一个浏览器窗口并且您使用 driver.close(),它应该退出 webdriver 会话。 你还有其他调试日志吗?

标签: python selenium pytest


【解决方案1】:

对于 ChromeDriver 用户:

options = Options()
options.add_argument('no-sandbox')
driver.close()
driver.quit()

归功于... https://bugs.chromium.org/p/chromedriver/issues/detail?id=1135

【讨论】:

  • 这也适用于没有选项实例变量的firefox geckodriver
【解决方案2】:

保证您在 pytest 中运行拆卸代码的最佳方法是定义一个终结器函数并将其作为终结器添加到该夹具。这保证了即使在 yield 命令之前出现故障,您仍然可以进行拆解。

为避免弹出窗口挂起您的拆解,请投资一些 WebdriverWait.until 命令,这些命令会在您需要时超时。出现弹出窗口,测试无法继续,超时,调用拆卸。

【讨论】:

    【解决方案3】:

    所以我能够使用下面的示例 HTML 文件重现您的问题

    <html>
    <body>
        Please enter a value for me: <input name="name" >
    <script>
        window.onbeforeunload = function(e) {
            return 'Dialog text here.';
        };
    
    </script>
    <h2>ask questions on exit</h2>
    </body>
    </html>
    

    然后我运行了一个重现挂起的示例脚本

    from selenium import webdriver
    
    driver = webdriver.Firefox()
    
    driver.get("http://localhost:8090/index.html")
    driver.find_element_by_name("name").send_keys("Tarun")
    driver.quit()
    

    这将无限期地挂起 selenium python。这不是一件好事。问题是 window.onloadwindow.onbeforeunload 对于 Selenium 来说很难处理,因为它发生的生命周期。 onload 甚至在 selenium 注入自己的代码以抑制 alertconfirm 进行处理之前发生。我很确定onbeforeunload 也不是硒的范围。

    所以有多种解决方法。

    应用变化

    要求开发者不要使用onloadonbeforeunload 事件。他们会听吗?不确定

    在配置文件中禁用 beforeunload

    这是您已经在答案中发布的内容

    from selenium import webdriver
    profile = webdriver.FirefoxProfile()
    # other settings here
    profile.set_preference("dom.disable_beforeunload", True)
    driver = webdriver.Firefox(firefox_profile = profile)
    

    通过代码禁用事件

    try:
        driver.execute_script("window.onunload = null; window.onbeforeunload=null")
    finally:
        pass
    driver.quit()
    

    这仅在您没有打开多个选项卡或假设生成弹出窗口的选项卡处于焦点时才有效。但这是处理这种情况的一种很好的通用方法

    不让 Selenium 挂起

    selenium 挂起的原因是它向 geckodriver 发送请求,然后将请求发送到 firefox,其中一个在等待用户关闭对话框时没有响应。但问题是 Selenium python 驱动程序没有在这个连接部分设置任何超时。

    解决问题很简单,只需添加以下两行代码

    import socket
    socket.setdefaulttimeout(10)
    try:
       driver.quit()
    finally:
       # Set to something higher you want
       socket.setdefaulttimeout(60)
    

    但是这种方法的问题是驱动程序/浏览器仍然不会关闭。这是您需要更强大的方法来杀死浏览器的地方,如下面的答案所述

    In Python, how to check if Selenium WebDriver has quit or not?

    上面链接中的代码用于完成答案

    from selenium import webdriver
    import psutil
    
    driver = webdriver.Firefox()
    
    driver.get("http://tarunlalwani.com")
    
    driver_process = psutil.Process(driver.service.process.pid)
    
    if driver_process.is_running():
        print ("driver is running")
    
        firefox_process = driver_process.children()
        if firefox_process:
            firefox_process = firefox_process[0]
    
            if firefox_process.is_running():
                print("Firefox is still running, we can quit")
                driver.quit()
            else:
                print("Firefox is dead, can't quit. Let's kill the driver")
                firefox_process.kill()
        else:
            print("driver has died")
    

    【讨论】:

      【解决方案4】:

      就我而言,基本上有两个问题问我会尝试回答:

      1. 为什么driver.webdriver.quit() 方法调用失败会使脚本处于挂起/无响应状态而不是引发任何异常?
      2. 如果脚本从未完成执行周期?
      3. ,为什么TestCase仍然是通过的

      为了回答第一个问题,我将尝试解释 Selenium 架构,这将消除我们的大部分疑问。

      那么 Selenium Webdriver 的功能如何?

      您使用 Selenium 客户端库 编写的每个语句或命令都将通过 http 转换为 JSON Wire Protocol,然后将其传递给我们的浏览器驱动程序(chromedriver、geckodriver)。所以基本上这些生成的HTTP URL(基于REST体系结构)到达浏览器驱动程序。在浏览器驱动程序内部有 http 服务器,它将在内部将接收到的 URL 传递给 Real Browser(作为 HTTP over HTTP Server),Web 浏览器将为此生成适当的响应并发送回 浏览器驱动程序(作为 HTTP over HTTP 服务器)反过来将使用 JSON Wire Protocol 将响应发送回 Selenium 客户端库,最终将根据获得的响应决定如何进一步处理。有关更多澄清,请参阅附图:

      现在回到脚本被搁置的问题,我们可以简单地得出结论,我们的浏览器仍在处理收到的请求,这就是为什么没有响应发送回 Browser Driver 进而离开 > Selenium库 quit()函数暂停IE等待请求处理完成。

      因此,我们可以使用多种解决方法,Alex 已经解释了其中一种。但我相信有更好的方法来处理这种情况,因为根据我的经验,浏览器 Selenium 也可能让我们在其他情况下处于保持/冻结状态,所以我个人更喜欢 Thread Kill Approach with Timeout,因为 Selenium 对象总是在 main() thread 中运行。我们可以分配到主线程的特定时间,如果达到超时会话时间,则可以杀死main() thread

      现在迁移到第二个问题,即:

      如果脚本从未完成,为什么测试用例仍然通过 执行周期?

      嗯,我对pytest 的工作原理不太了解,但我对测试引擎的运行方式有基本的了解,我将尝试回答这个问题。

      对于启动器,任何测试用例都无法通过它直到完整脚本运行完成。同样,如果您的测试用例通过,可能会有很少的可能场景,例如:
      • 您的测试方法从未利用过悬挂/冻结状态的整个执行的方法。
      • 您必须在测试拆卸环境中调用了方法(w.r.t [TestNG][4] Java 中的测试引擎:@AfterClass、@AfterTest、@AfterGroups、@AfterMethod、@AfterSuite)意味着您的测试执行已经完成。因此,这可能是测试显示为成功完成的原因。

      我仍然不确定第二个原因是什么正确的原因。如果有什么想法,我会继续寻找和更新帖子。

      @Alex :您能否以更好的理解更新问题,即您当前的测试设计,我可以探索以找到更好的解释。

      【讨论】:

      • 很长的答案,包含大量信息……几乎没有一个是连贯的、有用的或与问题相关的
      【解决方案5】:

      如您在about:config 中看到的,Firefox 中默认关闭警告为真,您可以为您的个人资料禁用它们:

      以来,

      执行挂起的原因似乎是一个弹出窗口询问 如果由于未保存的数据而想要离开页面的用户。

      您可以在 Firefox 配置文件中设置browser.tabs.warnOnClose,如下所示:

      from selenium import webdriver
      
      profile = webdriver.FirefoxProfile()
      profile.set_preference("browser.tabs.warnOnClose", False)
      driver = webdriver.Firefox(firefox_profile = profile)
      

      你可以查看profile.DEFAULT_PREFERENCES,这是python/site-packages/selenium/webdriver/firefox/webdriver_prefs.json的json

      【讨论】:

        【解决方案6】:

        这是一个不平凡的问题,硒对它的作用确实是不一致的。如文档所述,quit 方法应该只关闭浏览器窗口,但它不会。相反,您会收到一个弹出窗口,询问用户是否要离开该页面:

        令人讨厌的是,这个弹窗只有在用户调用后才会出现

        driver.quit()
        

        解决此问题的一种方法是为驱动程序设置以下配置文件

        from selenium import webdriver
        profile = webdriver.FirefoxProfile()
        # other settings here
        profile.set_preference("dom.disable_beforeunload", True)
        driver = webdriver.Firefox(firefox_profile = profile)
        

        【讨论】:

        • 复制了该问题并发布了有关该问题的详细答案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-17
        • 2015-05-21
        • 2022-12-05
        • 1970-01-01
        • 2018-03-19
        • 2018-09-11
        • 1970-01-01
        相关资源
        最近更新 更多