I have a very complex py.test
python-selenium test setup where I create a Firefox webdriver inside a py.test
fixture. Here is some idea of what I am doing:
'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")
And when running the test I can only see Debug 1
printed out. The whole process stops at this point and does not seem to proceed. The whole selenium test is stuck at the webdriver.quit)
.
The tests, however, completed successfully...
What reasons could be for that behavior?
Addendum:
The reason why the execution hangs seems to be a popup that asks the user if he wants to leave the page because of unsaved data. That means that the documentation for the quit
method is incorrect. It states:
Quits the driver and close every associated window.
3 Answers
Answers 1
This is a non-trivial problem, to which selenium acts really a inconsistent. The quit
method should, as documented, just close the browser window(s) but it does not. Instead you get a popup asking the user if he wants to leave the page:
The nasty thing is that this popup appears only after the user called
driver.quit()
One way to fix this is to set the following profile for the driver
from selenium import webdriver profile = webdriver.FirefoxProfile() # other settings here profile.set_preference("dom.disable_beforeunload", True) driver = webdriver.Firefox(firefox_profile = profile)
Answers 2
The warning to close is true by default in firefox as you can see in about:config
and you can disable them for your profile:
And since,
The reason why the execution hangs seems to be a popup that asks the user if he wants to leave the page because of unsaved data.
You can set browser.tabs.warnOnClose
in your Firefox configuration profile as follows:
from selenium import webdriver profile = webdriver.FirefoxProfile() profile.set_preference("browser.tabs.warnOnClose", False) driver = webdriver.Firefox(firefox_profile = profile)
You can look at profile.DEFAULT_PREFERENCES
which is the json at python/site-packages/selenium/webdriver/firefox/webdriver_prefs.json
Answers 3
As far as I understood, there are basically two questions asked which I will try to answer :
- Why failure of
driver.webdriver.quit()
method call leaves the script in hang/unresponsive state instead of raising any exception ?- Why the testcase was still a pass if the script never completed it's execution cycle ?
For answering the first question I will try to explain the Selenium Architecture which will clear most of our doubts.
So how Selenium Webdriver Functions ?
Every statement or command you write using Selenium Client Library will be converted to JSON Wire Protocol over http which in turn will be passed to our browser drivers(chromedriver, geckodriver) . So basically these generated http URLs (based on REST architecture) reaches to browser drivers. Inside the browser drivers there are http servers which will internally pass the received URLs to Real Browser (as HTTP over HTTP Server) for which the appropriate response will be generated by Web Browser and sent back to Browser Drivers (as HTTP over HTTP Server) which in turn will use JSON Wire Protocol to send the response back to Selenium Client Library which will finally decide on how to proceed further based on response achieved. Please refer to attached image for more clarification :
Now coming back to the question where script is in hold we can simply conclude that our browser is still working on request received that's why no response is sent back to Browser Driver which in turn left Selenium Library quit()
function in hold i.e. waiting for the request processing completion.
So there are variety of workarounds available which we can use, among which one is already explained by Alex. But I believe there is a much better way to handle such conditions, as browser Selenium could leave us in hold/freeze state for other cases too as per my experience so I personally prefer Thread Kill Approach with Timeout as Selenium Object always runs in main() thread
. We can allocate a specific time to the main thread and can kill main() thread
if timeout session time is reached.
Now moving to the second question which is :
Why the testcase was still a pass if the script never completed it's execution cycle ?
Well I don't have much idea on how pytest
works but I do have basic idea on how test engine operates based on which I will try to answer this one.
For starters it's not at all possible for any test case to pass until the full script run is completed. Again, if your test cases are passing there could be very few possible scenarios such as :
- Your test methods never made use of method which leaves the whole execution in hang/freeze state.
- You must have called the method inside test tear down environment (w.r.t
[TestNG][4]
test engine in Java : @AfterClass, @AfterTest, @AfterGroups, @AfterMethod, @AfterSuite) meaning your test execution is completed already. So this might be the reason for tests showing up as successful completion.
I am still not sure what proper cause is there for second reason. I will keep looking and update the post if came up with something.
@Alex : Can you update the question with better understanding i.e. your current test design which I can explore to find better explanation.
0 comments:
Post a Comment