Browser tests that pass on a laptop can fail in continuous integration when the runner has a different browser, driver, viewport, or dependency set. Running Selenium tests in GitHub Actions turns a headless browser smoke test into a repository check that can block broken UI changes before merge.
A GitHub-hosted ubuntu-24.04 runner image includes Google Chrome and ChromeDriver, and actions/setup-python@v6 prepares the selected Python runtime before pytest runs. Printing the browser versions in the job log makes later failures easier to compare when the hosted runner image changes.
Keep the first CI test small and pointed at a stable staging URL. Authentication prompts, one-time passcodes, live payment flows, and fragile third-party pages make poor required checks because they can fail for reasons outside the code under review.
selenium>=4.34,<5 pytest>=8,<9
import os from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By def test_homepage_loads(): app_url = os.environ["APP_URL"] expected_title = os.environ["EXPECTED_TITLE"] options = webdriver.ChromeOptions() options.add_argument("--headless=new") options.add_argument("--window-size=1280,720") options.add_argument("--disable-dev-shm-usage") if os.environ.get("CHROME_NO_SANDBOX") == "1": options.add_argument("--no-sandbox") chrome_binary = os.environ.get("CHROME_BINARY") if chrome_binary: options.binary_location = chrome_binary service_path = os.environ.get("CHROMEDRIVER") service = Service(service_path) if service_path else None driver = webdriver.Chrome(service=service, options=options) try: driver.get(app_url) assert expected_title in driver.title assert driver.find_element(By.TAG_NAME, "body").is_displayed() finally: driver.quit()
Set CHROME_BINARY or CHROMEDRIVER only for self-hosted runners or containers that keep the browser outside the normal PATH. Set CHROME_NO_SANDBOX=1 only when a containerized runner cannot start Chrome with its sandbox.
name: Selenium CI on: push: branches: [main] pull_request: workflow_dispatch: jobs: selenium: runs-on: ubuntu-24.04 permissions: contents: read timeout-minutes: 10 steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: "3.13" cache: "pip" cache-dependency-path: requirements-dev.txt - name: Install test dependencies run: python -m pip install -r requirements-dev.txt - name: Show browser versions run: | google-chrome --version chromedriver --version - name: Run Selenium tests env: APP_URL: https://example.com/ EXPECTED_TITLE: Example Domain run: pytest -q
Replace APP_URL and EXPECTED_TITLE with a stable staging page and a title fragment that belongs to the application. Use repository variables or secrets for private endpoints instead of hard-coding credentials in the workflow.
$ git add requirements-dev.txt tests/test_homepage.py .github/workflows/selenium.yml $ git commit -m "Run Selenium tests in GitHub Actions" [feature/selenium-ci 6f4a7d2] Run Selenium tests in GitHub Actions 3 files changed, 61 insertions(+) create mode 100644 .github/workflows/selenium.yml create mode 100644 requirements-dev.txt create mode 100644 tests/test_homepage.py
$ git push -u origin feature/selenium-ci Enumerating objects: 9, done. Counting objects: 100% (9/9), done. Writing objects: 100% (6/6), 1.42 KiB | 1.42 MiB/s, done. branch 'feature/selenium-ci' set up to track 'origin/feature/selenium-ci'.
$ gh run list --workflow selenium.yml --branch feature/selenium-ci --limit 1 --json databaseId,status,conclusion,event
[{"conclusion":"success","databaseId":9812345671,"event":"push","status":"completed"}]
The GitHub CLI command requires an authenticated session that can read Actions runs for the repository.
$ gh run view 9812345671 --log ##### snipped ##### selenium Show browser versions Google Chrome 149.0.7827.53 selenium Show browser versions ChromeDriver 149.0.7827.54 selenium Run Selenium tests . [100%] selenium Run Selenium tests 1 passed in 3.21s
If the conclusion is failure, run gh run view 9812345671 with --log-failed and fix the first failing setup or test step before making the workflow required.