问题描述
我在非公开网页上运行非常复杂的python-selenium测试。 在大多数情况下,这些测试运行良好,但有时在webdriver自身初始化期间这些测试之一会失败。
提示:尝试初始化网络驱动程序时,即执行以下操作时,会发生此错误:
# Start of the tests
mydriver = webdriver.Firefox(firefox_profile=profile, log_path=logfile)
# ERROR HAPPENS HERE
# Doing other stuff here
....
# Doing tests here
....
# Doing shutdown here
mydriver.quit()
这是此类错误的完整示例:
___________ ERROR at setup of TestSuite.test_synaptic_events_fitting ___________
> lambda: ihook(item=item, **kwds),
when=when,
)
/usr/local/lib/python2.7/dist-packages/flaky/flaky_pytest_plugin.py:273:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
conftest.py:157: in basedriver
mydriver = firefox.get_driver(*args)
bsp_usecase_tests/tools/firefox.py:44: in get_driver
driver = webdriver.Firefox(firefox_profile=profile, log_path=logfile) #### INITIALIZING OF WEBDRIVER HERE
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/firefox/webdriver.py:158: in __init__
keep_alive=True)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:154: in __init__
self.start_session(desired_capabilities, browser_profile)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:243: in start_session
response = self.execute(Command.NEW_SESSION, parameters)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:311: in execute
self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7efd3b702f90>
response = {'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}
def check_response(self, response):
"""
Checks that a JSON response from the WebDriver does not have an error.
:Args:
- response - The JSON response from the WebDriver server as a dictionary
object.
:Raises: If the response contains an error message.
"""
status = response.get('status', None)
if status is None or status == ErrorCode.SUCCESS:
return
value = None
message = response.get("message", "")
screen = response.get("screen", "")
stacktrace = None
if isinstance(status, int):
value_json = response.get('value', None)
if value_json and isinstance(value_json, basestring):
import json
try:
value = json.loads(value_json)
if len(value.keys()) == 1:
value = value['value']
status = value.get('error', None)
if status is None:
status = value["status"]
message = value["value"]
if not isinstance(message, basestring):
value = message
message = message.get('message')
else:
message = value.get('message', None)
except ValueError:
pass
exception_class = ErrorInResponseException
if status in ErrorCode.NO_SUCH_ELEMENT:
exception_class = NoSuchElementException
elif status in ErrorCode.NO_SUCH_FRAME:
exception_class = NoSuchFrameException
elif status in ErrorCode.NO_SUCH_WINDOW:
exception_class = NoSuchWindowException
elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
exception_class = StaleElementReferenceException
elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
exception_class = ElementNotVisibleException
elif status in ErrorCode.INVALID_ELEMENT_STATE:
exception_class = InvalidElementStateException
elif status in ErrorCode.INVALID_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
exception_class = InvalidSelectorException
elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
exception_class = ElementNotSelectableException
elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
exception_class = ElementNotInteractableException
elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
exception_class = InvalidCookieDomainException
elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
exception_class = UnableToSetCookieException
elif status in ErrorCode.TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.SCRIPT_TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.UNKNOWN_ERROR:
exception_class = WebDriverException
elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
exception_class = UnexpectedAlertPresentException
elif status in ErrorCode.NO_ALERT_OPEN:
exception_class = NoAlertPresentException
elif status in ErrorCode.IME_NOT_AVAILABLE:
exception_class = ImeNotAvailableException
elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
exception_class = ImeActivationFailedException
elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
exception_class = MoveTargetOutOfBoundsException
elif status in ErrorCode.JAVASCRIPT_ERROR:
exception_class = JavascriptException
elif status in ErrorCode.SESSION_NOT_CREATED:
exception_class = SessionNotCreatedException
elif status in ErrorCode.INVALID_ARGUMENT:
exception_class = InvalidArgumentException
elif status in ErrorCode.NO_SUCH_COOKIE:
exception_class = NoSuchCookieException
elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
exception_class = ScreenshotException
elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
exception_class = ElementClickInterceptedException
elif status in ErrorCode.INSECURE_CERTIFICATE:
exception_class = InsecureCertificateException
elif status in ErrorCode.INVALID_COORDINATES:
exception_class = InvalidCoordinatesException
elif status in ErrorCode.INVALID_SESSION_ID:
exception_class = InvalidSessionIdException
elif status in ErrorCode.UNKNOWN_METHOD:
exception_class = UnknownMethodException
else:
exception_class = WebDriverException
if value == '' or value is None:
value = response['value']
if isinstance(value, basestring):
if exception_class == ErrorInResponseException:
raise exception_class(response, value)
raise exception_class(value)
if message == "" and 'message' in value:
message = value['message']
screen = None
if 'screen' in value:
screen = value['screen']
stacktrace = None
if 'stackTrace' in value and value['stackTrace']:
stacktrace = []
try:
for frame in value['stackTrace']:
line = self._value_or_default(frame, 'lineNumber', '')
file = self._value_or_default(frame, 'fileName', '<anonymous>')
if line:
file = "%s:%s" % (file, line)
meth = self._value_or_default(frame, 'methodName', '<anonymous>')
if 'className' in frame:
meth = "%s.%s" % (frame['className'], meth)
msg = " at %s (%s)"
msg = msg % (meth, file)
stacktrace.append(msg)
except TypeError:
pass
if exception_class == ErrorInResponseException:
raise exception_class(response, message)
elif exception_class == UnexpectedAlertPresentException and 'alert' in value:
raise exception_class(message, screen, stacktrace, value['alert'].get('text'))
> raise exception_class(message, screen, stacktrace)
E WebDriverException: Message: connection refused
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/errorhandler.py:237: WebDriverException
这些测试作为docker容器内jenkins计划的一部分运行,以确保始终保持完全相同的环境。 以下是使用的软件包及其版本的列表:
- python 2.7.12
- pytest 3.6.1
- 硒3.8.0
- 壁虎起子0.19.1
- 火狐62.0
- 片状3.4.0
该错误大约在所有测试的1%中出现。 大约有15种不同的测试,并且错误似乎随机出现(即,并非总是相同的测试)。
这是firefox / selenium / geckodriver中的错误吗? 有办法解决这个问题吗?
以下代码段不是我正在使用的某些代码 ! 这只是解决上述问题的一种方法。 这也许是解决我原来的问题的好方法吗?
while counter<5:
try:
webdriver = webdriver.Firefox(firefox_profile=profile, log_path=logfile)
break
except WebDriverException:
counter +=1
有一个更好的方法吗?
1楼
此错误消息...
{'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}
...暗示GeckoDriver无法启动/产生新的WebBrowsing会话,即Firefox浏览器会话。
在讨论中的中, @ andreastt提到:
当最后一个窗口关闭时,geckodriver隐式结束(上一个)会话。 如果将
driver.quit()
作为后续命令调用,它将失败,因为该会话已被隐式删除。在这些情况下,GeckoDriver应该检测到在
driver.close()
之后隐式关闭了会话,或者如果会话已经关闭,则忽略来自driver.quit()
的响应。
在这种情况下,将生成以下跟踪日志:
1505753594121 webdriver::server DEBUG Last window was closed, deleting session
1505753594121 webdriver::server DEBUG Deleting session
1505753594121 geckodriver::marionette DEBUG Stopping browser process
1505753594364 webdriver::server DEBUG <- 200 OK {"value": []}
1505753594523 webdriver::server DEBUG -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9
1505753594524 webdriver::server DEBUG <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n 2: 0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n 3: 0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n 4: 0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n 5: 0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:98\n 7: 0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n at /checkout/src/liballoc/boxed.rs:661\n - std::sys_common::thread::start_thread\n at /checkout/src/libstd/sys_common/thread.rs:21\n - std::sys::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594533 webdriver::server DEBUG -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9
1505753594542 webdriver::server DEBUG <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n 2: 0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n 3: 0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n 4: 0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n 5: 0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:98\n 7: 0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n at /checkout/src/liballoc/boxed.rs:661\n - std::sys_common::thread::start_thread\n at /checkout/src/libstd/sys_common/thread.rs:21\n - std::sys::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594549 webdriver::server DEBUG -> GET /shutdown
1505753594551 webdriver::server DEBUG <- 404 Not Found {"value":{"error":"unknown command","message":"GET /shutdown did not match a known command","stacktrace":"stack backtrace:\n 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n 2: 0x442d88 - webdriver::error::WebDriverError::new::hea6d4dbf778b2b24\n 3: 0x43c65f - <webdriver::server::HttpHandler<U> as hyper::server::Handler>::handle::hd03629bd67672697\n 4: 0x403a04 - std::sys_common::backtrace::__rust_begin_short_backtrace::h32e6ff325c0d7f46\n 5: 0x40c036 - std::panicking::try::do_call::h5f902dc1eea01ffe\n 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:98\n 7: 0x4209a2 - <F as alloc::boxed::FnBox<A>>::call_box::h032bafb4b576d1cd\n 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n
尽管您看到的错误的错误代码是“状态”:500 ,而我提供的错误示例是404 Not Found ,显然看起来有所不同,其核心原因类似于:
"message":"connection refused"
由于:
imp::thread::{{impl}}::new::thread_start
从:
/checkout/src/libstd/sys/unix/thread.rs:84
从另一个角度看,在使用GeckoDriver时 , Selenium和Firefox确保二进制文件兼容,如下所示:
分析
自从geckodriver 0.19.1可用以来, geckodriver二进制文件有了重大变化。 进行了一些更改:
-
GeckoDriver v0.22.0 (2018-09-15):
-
用于[脚本超时]和[超时]错误的HTTP状态代码已从请求超时(408)更改为内部服务器错误(500),以便不破坏HTTP / 1.1
Keep-Alive
状态,因为HTTP客户端会解释旧状态代码表示他们应该重复该请求。 -
持久连接的HTTP / 1.1
Keep-Alive
超时已增加到90秒。 - 现在,当没有活动会话时,将返回[无效会话ID]错误。
- geckodriver连接到木偶时的握手已通过杀死Firefox进程失败而得到加强。
- 握手读取超时已减少至10秒,而不是永远等待。
-
用于[脚本超时]和[超时]错误的HTTP状态代码已从请求超时(408)更改为内部服务器错误(500),以便不破坏HTTP / 1.1
-
GeckoDriver v0.21.0 ( 2018-06-15 ):
-
现在,没有返回值的WebDriver命令可以正确返回
{value: null}
而不是空字典。 - 强制使用IPv4网络堆栈。
-
在某些系统配置中,如果
localhost
解析为IPv6地址,则geckodriver会尝试在错误的IP堆栈上连接到Firefox,从而导致连接尝试在60秒后超时。 现在,我们确保geckodriver始终使用IPv4来连接Firefox和分配空闲端口。
-
现在,没有返回值的WebDriver命令可以正确返回
-
GeckoDriver v0.20.1 ( 2018-04-06 ):
- 避免尝试杀死已停止的Firefox进程。
-
通过更改使Firefox有足够的时间在0.20.0中关闭,geckodriver开始无条件终止进程以获取退出状态。 这导致geckodriver不正确地将Firefox成功关闭报告为失败。
-
GeckoDriver v0.20.0 ( 2018-03-08 ):
- 来自geckodriver的回溯不再替代丢失的木偶堆栈跟踪。
- 现在有足够的时间来关闭Firefox进程,从而有足够的时间启动Firefox的关闭挂起监视器。
-
Firefox具有集成的后台监视器,该监视器可在关机期间观察长时间运行的线程。 如果挂起,这些线程将在63秒后被杀死。 为了允许Firefox自行关闭这些线程,geckodriver必须等待该时间和另外几秒钟。
解
- 将Selenium升级到当前 。
- 升级GeckoDriver到水平。
- 将Firefox版本升级到Firefox v62.0.2级别。
- 如果您的基本Web客户端版本太旧,请通过卸载它,并安装最新的GA和Web Client的发行版本。
-
始终在
tearDown(){}
方法内调用driver.quit()
,以优雅地关闭和破坏WebDriver和Web Client实例。 -
以非root用户身份执行
Test
。
更新资料
根据您粗略的问题更新,您可以引发一个多次尝试循环,以初始化selenium webdriver实例,如下所示:
通过调用
taskkill
命令( 特定于WindowsOS ),确保没有悬空的geckodriver实例,如下所示:os.system("taskkill /f /im geckodriver.exe /T")
通过调用
kill()
命令( Cross platform ),确保没有悬空的geckodriver实例,如下所示:from selenium import webdriver import psutil from selenium.common.exceptions import WebDriverException for counter in range(5): try: webdriver = webdriver.Firefox(executable_path=r'C:\\Utility\\BrowserDrivers\\geckodriver.exe') print("WebDriver and WebBrowser initialized ...") break except WebDriverException: #Cross platform PROCNAME = "geckodriver" for proc in psutil.process_iter(): # check whether the process name matches if proc.name() == PROCNAME: proc.kill() print("Retrying ...") print("Out of loop ...")