pytest-dev / pytest-selenium

Plugin for running Selenium with pytest
Other
333 stars 120 forks source link

"'geckodriver' executable needs to be in PATH" error in 1.11.2 #139

Closed bavaria95 closed 6 years ago

bavaria95 commented 6 years ago

After update of pytest-selenium to 1.11.2 (https://github.com/pytest-dev/pytest-selenium/commit/0773c93a54f3218d960f4b11b026f2b58a918922 to be more precise) my selenium tests stopped working (using Firefox). I started to receive WebDriverException: Message: 'geckodriver' executable needs to be in PATH. error. Debugging has shown that capabilities now are getting set to {'acceptInsecureCerts': True, 'browserName': 'firefox', 'marionette': True}, instead of {'browserName': 'firefox'}, which was the case before the update (now it takes them from selenium's DesiredCapabilities)

bavaria95 commented 6 years ago

Setting marionette capability as a pytest argument (to empty string, due to the fact that it considers all passed arguments as strings and python will consider empty string as False) helped me: --capability marionette ''

davehunt commented 6 years ago

This was in fact a bug before, as the default has been to use Marionette since Selenium 3 was released, and meant that you would have inconsistent behaviour between local browsers and those running against a remote server. To use the plugin against old Firefox versions with the legacy driver, you can either set the marionette capability to an empty string on the command line, which evaluates as Falsy (as you have), or you can set it to False using the capabilities fixture:

@pytest.fixture
def capabilities(capabilities):
    capabilities['marionette'] = False
    return capabilities

My recommendation would be to run your tests against a modern version of Firefox, which requires the geckodriver binary to be available in your path. There's some documentation on this here.

bavaria95 commented 6 years ago

@davehunt Selenium is running as a separate docker container, thus it's remote)

davehunt commented 6 years ago

@bavaria95 then it would depend on the version of Selenium running. Marionette has been enabled by default since 3.0. The official docker images from Selenium also come with geckodriver binary installed.

david-caro commented 6 years ago

@davehunt hi, a collegue of @bavaria95 here, so we are running pytest-selenium on a container, and selenium on another, but for some reason it's looking for the binary on the pytest-selenium container instead of trying to connect to the selenium one (as it did before, and as it does when settting marionette to False). Maybe there's something else we have to specify to force it to connect to the remote selenium?

davehunt commented 6 years ago

@david-caro okay, that does sound like a bug.. let me take a look

davehunt commented 6 years ago

@bavaria95 @david-caro could you provide a full traceback? I can't see why pytest-selenium or selenium would be looking for geckodriver when specifying a remote driver, and a trace would help me to identify what code is failing.

bavaria95 commented 6 years ago
request = <SubRequest 'driver' for <Function 'test_advisors_typehead'>>
 driver_class = <class 'selenium.webdriver.firefox.webdriver.WebDriver'>
 driver_kwargs = {'capabilities': {'acceptInsecureCerts': True, 'browserName': 'firefox', 'marionette': True}, 'firefox_options': <sele...ox.options.Options object at 0x975e7d0>, 'log_path': '/tmp/pytest-of-root/pytest-0/test_advisors_typehead0/driver.log'}

 @pytest.yield_fixture
 def driver(request, driver_class, driver_kwargs):
 """Returns a WebDriver instance based on options and capabilities"""
 > driver = driver_class(**driver_kwargs)

 /virtualenv/lib/python2.7/site-packages/pytest_selenium/pytest_selenium.py:128: 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 /virtualenv/lib/python2.7/site-packages/selenium/webdriver/firefox/webdriver.py:144: in __init__
 self.service.start()
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

 self = <selenium.webdriver.firefox.service.Service object at 0x975e390>

 def start(self):
 """
 Starts the Service.

 :Exceptions:
 - WebDriverException : Raised either when it can't start the service
 or when it can't connect to the service
 """
 try:
 cmd = [self.path]
 cmd.extend(self.command_line_args())
 self.process = subprocess.Popen(cmd, env=self.env,
 close_fds=platform.system() != 'Windows',
 stdout=self.log_file, stderr=self.log_file)
 except TypeError:
 raise
 except OSError as err:
 if err.errno == errno.ENOENT:
 raise WebDriverException(
 "'%s' executable needs to be in PATH. %s" % (
 > os.path.basename(self.path), self.start_error_message)
 E WebDriverException: Message: 'geckodriver' executable needs to be in PATH.

 /virtualenv/lib/python2.7/site-packages/selenium/webdriver/common/service.py:81: WebDriverException
davehunt commented 6 years ago

Okay, you have selenium.webdriver.firefox.webdriver.WebDriver where you should have selenium.webdriver.remote.webdriver.WebDriver.. What are you command line options? Could you also provide the pytest header from the console?

davehunt commented 6 years ago

@bavaria95 I've noticed https://github.com/inspirehep/inspire-next/blob/1595d6707e7cb6499dafb0ff9fa769874e6db446/docker-compose.test.yml#L109 which specifies --driver=Firefox, which should perhaps be --driver=Remote?

bavaria95 commented 6 years ago

py.test --driver Remote --host selenium --port 4444 --capability browserName firefox --html=selenium-report.html tests/acceptance

============================= test session starts ==============================
platform linux2 -- Python 2.7.5, pytest-3.2.5, py-1.5.2, pluggy-0.4.0 -- /virtualenv/bin/python
cachedir: .cache
driver: Firefox
sensitiveurl: .*
metadata: {'Python': '2.7.5', 'Driver': 'Firefox', 'Capabilities': {'browserName': 'firefox'}, 'Server': 'selenium:4444', 'Base URL': '', 'Platform': 'Linux-4.11.8-300.fc26.x86_64-x86_64-with-centos-7.4.1708-Core', 'Plugins': {'base-url': '1.4.1', 'cov': '2.5.1', 'variables': '1.7.0', 'selenium': '1.11.2', 'html': '1.16.0', 'metadata': '1.5.0', 'flake8': '0.9.1'}, 'Packages': {'py': '1.5.2', 'pytest': '3.2.5', 'pluggy': '0.4.0'}}
rootdir: /code, inifile: pytest.ini
plugins: variables-1.7.0, selenium-1.11.2, metadata-1.5.0, html-1.16.0, flake8-0.9.1, cov-2.5.1, base-url-1.4.1
davehunt commented 6 years ago

hmm.. there's clearly something odd because driver: Firefox in the pytest header should be driver: Remote based on those command line arguments. I'll keep digging.

davehunt commented 6 years ago

@bavaria95 is this something I can attempt to replicate? I've cloned https://github.com/inspirehep/inspire-next/ and was able to run the tests using:

docker-compose pull
docker-compose -f docker-compose.deps.yml run --rm pip
docker-compose run --rm web scripts/recreate_records
docker-compose -f docker-compose.test.yml run --rm acceptance

The tests ran, with the following pytest header showing the correct driver:

============================= test session starts ==============================
platform linux2 -- Python 2.7.5, pytest-3.2.5, py-1.5.2, pluggy-0.4.0
driver: Remote
sensitiveurl: .*
metadata: {'Python': '2.7.5', 'Driver': 'Remote', 'Capabilities': {'browserName': 'firefox'}, 'Server': 'selenium:4444', 'Base URL': '', 'Platform': 'Linux-4.9.49-moby-x86_64-with-centos-7.4.1708-Core', 'Plugins': {'base-url': '1.4.1', 'cov': '2.5.1', 'variables': '1.7.0', 'selenium': '1.11.2', 'html': '1.16.0', 'metadata': '1.5.0', 'flake8': '0.9.1'}, 'Packages': {'py': '1.5.2', 'pytest': '3.2.5', 'pluggy': '0.4.0'}}
rootdir: /code, inifile: pytest.ini
plugins: variables-1.7.0, selenium-1.11.2, metadata-1.5.0, html-1.16.0, flake8-0.9.1, cov-2.5.1, base-url-1.4.1

The first time I ran I had tests failing due to elements not being found, and another time I had connection timeouts. I did not see the geckodriver issue, however I did when I ran docker-compose -f docker-compose.test.yml run --rm visible-acceptance, which specified a driver of Firefox.

davehunt commented 6 years ago

I just following the instructions again (I think I missed the docker-compose -f docker-compose.deps.yml run --rm assets step originally, and the 'acceptance' tests are now passing for me.

bavaria95 commented 6 years ago

@davehunt Okay, there is my bad, actually in the code I was using --driver Firefox. And it makes sense why it doesn't work (after changing to --driver Remote it works without setting up marionette to False). Buuut, the question is, how was it working before with the same driver? Right now I made some tests with my older image (before library update) and my new image (after library update). Looks like:

+ stdbuf -o 0 py.test -s -vv --driver Firefox --host selenium --port 4444 --capability browserName firefox --html=selenium-report.html --junitxml=output.xml tests/acceptance
Coverage.py warning: --include is ignored because --source is set (include-ignored)
============================= test session starts ==============================
platform linux2 -- Python 2.7.5, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- /virtualenv/bin/python
cachedir: .cache
driver: Firefox
sensitiveurl: .*
metadata: {'Python': '2.7.5', 'Driver': 'Firefox', 'Capabilities': {'browserName': 'firefox'}, 'Server': 'selenium:4444', 'Base URL': '', 'Platform': 'Linux-4.11.8-300.fc26.x86_64-x86_64-with-centos-7.4.1708-Core', 'Plugins': {'base-url': '1.4.1', 'cov': '2.5.1', 'variables': '1.7.0', 'selenium': '1.11.1', 'html': '1.16.0', 'metadata': '1.5.0', 'flake8': '0.9.1'}, 'Packages': {'py': '1.4.34', 'pytest': '3.2.3', 'pluggy': '0.4.0'}}
rootdir: /code, inifile: pytest.ini
plugins: variables-1.7.0, selenium-1.11.1, metadata-1.5.0, html-1.16.0, flake8-0.9.1, cov-2.5.1, base-url-1.4.1
collected 23 items                                                              

tests/acceptance/conftest.py PASSED
tests/acceptance/authors/test_authors_new_form.py PASSED
tests/acceptance/authors/test_authors_new_form.py::test_institutions_typeahead Processed 0 records
All migration tasks have been completed.
PASSED
+ stdbuf -o 0 py.test -s -vv --driver Firefox --host selenium --port 4444 --capability browserName firefox --html=selenium-report.html --junitxml=output.xml tests/acceptance
Coverage.py warning: --include is ignored because --source is set (include-ignored)
============================= test session starts ==============================
platform linux2 -- Python 2.7.5, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- /virtualenv/bin/python
cachedir: .cache
driver: Firefox
sensitiveurl: .*
metadata: {'Python': '2.7.5', 'Driver': 'Firefox', 'Capabilities': {'browserName': 'firefox'}, 'Server': 'selenium:4444', 'Base URL': '', 'Platform': 'Linux-4.11.8-300.fc26.x86_64-x86_64-with-centos-7.4.1708-Core', 'Plugins': {'base-url': '1.4.1', 'cov': '2.5.1', 'variables': '1.7.0', 'selenium': '1.11.2', 'html': '1.16.0', 'metadata': '1.5.0', 'flake8': '0.9.1'}, 'Packages': {'py': '1.4.34', 'pytest': '3.2.3', 'pluggy': '0.4.0'}}
rootdir: /code, inifile: pytest.ini
plugins: variables-1.7.0, selenium-1.11.2, metadata-1.5.0, html-1.16.0, flake8-0.9.1, cov-2.5.1, base-url-1.4.1
collected 23 items                                                              

tests/acceptance/conftest.py PASSED
tests/acceptance/authors/test_authors_new_form.py PASSED
tests/acceptance/authors/test_authors_new_form.py::test_institutions_typeahead Processed 0 records
All migration tasks have been completed.
ERROR
tests/acceptance/authors/test_authors_new_form.py::test_experiments_typehead ERROR
davehunt commented 6 years ago

This was because before version 1.11.2 the capabilities were empty by default, meaning the marionette capability was not present and legacy Firefox was used. This was fixed so that the appropriate defaults were set according to the Selenium client, and therefore marionette and modern Firefox are used. See https://github.com/pytest-dev/pytest-selenium/commit/0773c93a54f3218d960f4b11b026f2b58a918922#diff-88a7aec9c2361f634a0f93e317ffa663R44 for the specific change.

I'd suggest this is a welcome issue to discover, as it suggests your tests are running on an old version of Firefox, which most likely does not represent your user base. It should be trivial to update Firefox and install geckodriver onto this image to resolve you current issue.

Also note that when using --driver Firefox the --host and --port arguments are no necessary, and might add confusion. Please let me know if there's anything else I can help with.

bavaria95 commented 6 years ago

Great, thanks. I think this one can be closed then. @david-caro, perhaps we can try upgrading firefox version for tests, right?

davehunt commented 6 years ago

In case it's helpful, here's where the official Selenium images install Firefox and geckodriver: https://github.com/SeleniumHQ/docker-selenium/blob/master/NodeFirefox/Dockerfile#L10-L35

davehunt commented 6 years ago

Closing this, as it seems to not be an issue with the plugin.