talkingtoaj
talkingtoaj

Reputation: 888

How can I get Selenium tests to run in python with Github actions?

I'm having trouble getting my python Selenium running in github actions.

I have been using Circle CI for the past year, but recently began migrating over to github actions.

For Circle CI to run selenium in a chrome browser, I had the following lines in my config.yml:

docker:
    # includes chrome browser for selenium testing
  - image: circleci/python:3.7.4-browsers

and there didn't seem to be a need to install a chromedriver.

I am using the following in my githubs action .yml file:

jobs:
  build:
    runs-on: ubuntu-latest
    services:
      selenium:
        image: selenium/standalone-chrome
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install pipenv
        pipenv install
    - name: Prepare Selenium
      # https://github.com/marketplace/actions/setup-chromedriver
      uses: nanasess/setup-chromedriver@master
    - name: Launch browser
      run: |
        google-chrome --version
        export DISPLAY=:99
        chromedriver --url-base=/wd/hub &
        sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional, disables headless mode
    - name: Run tests
      run: pipenv run python manage.py test functional_tests.tests.test_selenium.test_exams -v 2

But I get the following error when in my python code I try to run:

from selenium import webdriver
driver = webdriver.Chrome()
  File "/home/runner/.local/share/virtualenvs/lang-EMCZ4oUT/lib/python3.7/site-packages/selenium/webdriver/chrome/webdriver.py", line 81, in __init__
    desired_capabilities=desired_capabilities)
  File "/home/runner/.local/share/virtualenvs/lang-EMCZ4oUT/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 157, in __init__
    self.start_session(capabilities, browser_profile)
  File "/home/runner/.local/share/virtualenvs/lang-EMCZ4oUT/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 252, in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
  File "/home/runner/.local/share/virtualenvs/lang-EMCZ4oUT/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/home/runner/.local/share/virtualenvs/lang-EMCZ4oUT/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: exited abnormally.
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)

From what I can read online, I should need just the uses: nanasess/setup-chromedriver@master and shouldn't need image: selenium/standalone-chrome, but switching either in or out doesn't make any difference, the python tests still cannot find chrome browser.

Am I supposed to set up a port to listen to?

Upvotes: 10

Views: 8314

Answers (3)

topefolorunso
topefolorunso

Reputation: 1

Check out the chromedriver-autoinstaller python package.

With the line of code below in your script, you can automatically check and download the latest version of chromedriver exists and then add it to path.

chromedriver_autoinstaller.install()

You should check out this template as well.

Upvotes: 0

F4-Z4
F4-Z4

Reputation: 689

I’m going to answer your question first and then I’m going to offer an alternative approach. I’m going to use a unified diff format to highlight the changes I would make to your workflow. If you’re not familiar with the format, ignore the first three lines, and then imagine that I’m deleting the lines starting with “-” from your workflow and adding the lines starting with “+”. Lines starting with “ ” are left as-is.

When you posted your question, the action nanasess/setup-chromedriver downloaded Chrome Browser along with chromedriver (that was v1.0.1). As of this writing, it still does the same (v1.0.5). Because of this, you don’t need an extra service container to run Chrome Browser and chromedriver – they are already in your primary container.

--- original.yml    2020-06-13 20:42:25 +0000
+++ step1.yml   2021-04-23 00:01:00 +0000
@@ -1,9 +1,6 @@
 jobs:
   build:
     runs-on: ubuntu-latest
-    services:
-      selenium:
-        image: selenium/standalone-chrome
     steps:
     - uses: actions/checkout@v1
     - name: Set up Python 3.7

You also don’t need your “Launch browser” step. Selenium library will do it for you. It executes local chromedriver binary by default, which in turn executes Chrome Browser binary by default. If you don’t want to use headless mode, you still have to start the virtual framebuffer (but nothing else):

--- step1.yml   2021-04-23 00:01:00 +0000
+++ step2.yml   2021-04-23 00:02:00 +0000
@@ -15,11 +15,10 @@
     - name: Prepare Selenium
       # https://github.com/marketplace/actions/setup-chromedriver
       uses: nanasess/setup-chromedriver@master
-    - name: Launch browser
+    - name: Start XVFB
       run: |
-        google-chrome --version
-        export DISPLAY=:99
-        chromedriver --url-base=/wd/hub &
         sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional, disables headless mode
     - name: Run tests
       run: pipenv run python manage.py test functional_tests.tests.test_selenium.test_exams -v 2
+      env:
+        DISPLAY: :99

And this should do the trick. Notice addition of env with the same DISPLAY port as was passed when starting XVFB.

My guess is that the error you shared here occurs due to conflict between chromedriver and Google Chrome you’ve started and the ones your test suite is trying to start and control.

What is my alternative approach? I’m personally a bit cautious when introducing third-party dependencies. Especially for things which should be just about couple lines of code. And when looking for inspiration, I try to look as close to the source as possible. So, how is Selenium testing their code?

Interestingly, Selenium project is using GitHub Actions to test the libraries themselves and they have quite extensive integration test suite which requires running browsers. They don’t use third-party action to setup the browser environment. Their tests are pretty complex, but you can take a hint, take individuals actions and steps, and apply them based on your needs.

The important part is the action to setup Chrome and chromedriver. Straightforward and pretty much the same compared to the action from Kentaro Ohkouchi (a.k.a. nanasess) which you’re using.

The next important part is starting the virtual framebuffer. Again, you don’t need it if you are using headless mode. They are starting it very simply compared to you or Ohkouchi’s example:

--- step2.yml   2021-04-23 00:02:00 +0000
+++ step3.yml   2021-04-23 00:03:00 +0000
@@ -18,3 +18,3 @@
     - name: Start XVFB
       run: |
-        sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional, disables headless mode
+        Xvfb :99 &

No sudo, no extra arguments, just the DISPLAY port.

P.S.: When using a third-party action, use tags. You never know what kind of change squeezes into someone else’s code. Rather let the test fail during CI, investigate what changed and then bump the version, than blindly trust that someone is always going to make the right change for you…

--- step3.yml   2021-04-23 00:03:00 +0000
+++ step4.yml   2021-04-23 00:04:00 +0000
@@ -15,3 +15,3 @@
     - name: Prepare Selenium
       # https://github.com/marketplace/actions/setup-chromedriver
-      uses: nanasess/setup-chromedriver@master
+      uses: nanasess/[email protected]

Upvotes: 9

Alexandr Kardash
Alexandr Kardash

Reputation: 1

Use headless Chromedriver

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(options=chrome_options)

If you need Chrome with GUI - you can use macos or windows instead of ubuntu

MacOS

jobs:
  build:
    runs-on: macos-latest
    ...

Windows

jobs:
  build:
    runs-on: windows-latest
    ...

Upvotes: -2

Related Questions