August 3, 2026
How to Run Selenium Tests in Jenkins
Learn how to run Selenium tests in Jenkins with a practical CI setup, browser dependencies, reports, artifacts, headless execution, and common failure modes.
If you want Selenium tests to matter, they need to run where your delivery pipeline runs. Jenkins is still a common place for that work because it can pull source, provision agents, execute browser tests, publish reports, and keep build history in one place. The hard part is not triggering a test command. The hard part is making the pipeline reliable enough that people trust the result.
This tutorial shows how to run Selenium tests in Jenkins with the pieces that matter in practice, browser dependencies, job configuration, reports, artifacts, and the failure modes that turn a simple setup into a flaky one. The examples use Selenium with Python, but the same structure applies to Java, JavaScript, or C#.
A Jenkins job that merely launches a test suite is not a test system. A useful Jenkins pipeline also captures evidence when the suite fails, isolates browser dependencies, and makes flakiness visible instead of hiding it.
What you need before you start
At minimum, you need:
- A Jenkins controller and at least one build agent
- A Selenium test suite already running locally
- A browser and driver strategy, either installed on the agent or containerized
- A reporting format Jenkins can publish, such as JUnit XML, HTML, or screenshots
For the underlying tools, the official docs are the right place to start:
The design question is simple: do you want Jenkins to execute browser tests on a reusable machine, or in an ephemeral container? Both work, but the tradeoffs are different.
Agent-based execution
A persistent Jenkins agent is straightforward if your team already manages virtual machines. You install Chrome or Firefox, install the matching driver approach, and run tests from the workspace. The downside is drift. Browser versions, driver versions, and system libraries slowly diverge from what your laptop uses.
Container-based execution
Docker-based agents reduce drift and make dependencies explicit. The tradeoff is that browser tests inside containers can require extra setup, especially for display, sandboxing, and shared memory. This is often the better default if your organization already uses containers in CI.
A minimal Selenium test example
Before putting the test into Jenkins, keep the test itself small and deterministic. Here is a basic Python example that checks a page title.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options() options.add_argument(“–headless=new”) options.add_argument(“–no-sandbox”) options.add_argument(“–disable-dev-shm-usage”)
driver = webdriver.Chrome(options=options) try: driver.get(“https://example.com”) assert “Example Domain” in driver.title finally: driver.quit()
This test is intentionally simple. In a real suite, you should not use title checks everywhere, but a minimal example helps make the CI plumbing visible before you add locator-heavy flows.
The main technical detail here is headless execution. Headless mode is fine for many suites, but it does not eliminate browser differences. A test that fails because of timing, viewport size, or rendering assumptions will still fail in Jenkins.
Build the Selenium CI pipeline in Jenkins
The smallest useful Jenkins pipeline has these stages:
- Checkout source
- Install dependencies
- Run Selenium tests
- Publish results
- Archive artifacts
A declarative Jenkinsfile keeps the flow readable and versioned with the test code.
yaml
Example structure shown for readability, but Jenkins declarative pipeline is written in Groovy.
groovy pipeline { agent any
stages { stage(‘Checkout’) { steps { checkout scm } }
stage('Install') {
steps {
sh 'python -m venv .venv'
sh '. .venv/bin/activate && pip install -r requirements.txt'
}
}
stage('Test') {
steps {
sh '. .venv/bin/activate && pytest --junitxml=reports/junit.xml'
}
} }
post { always { junit ‘reports/junit.xml’ archiveArtifacts artifacts: ‘reports/*/’, allowEmptyArchive: true } } }
Two parts deserve attention. First, the junit step gives Jenkins structured test results. Second, archiveArtifacts preserves evidence. Without artifacts, a failed browser run can become an argument about what happened rather than a debugging exercise.
Install the browser dependencies correctly
Most Jenkins Selenium problems start here. The suite passes locally, then fails on the agent because the browser is missing, the driver is mismatched, or the OS libraries are incomplete.
Option 1, install Chrome and ChromeDriver on the agent
This approach is easy to understand, but it creates version management work. You must keep browser and driver compatibility under control, and the build node becomes a special machine.
Typical failure modes include:
- Chrome updates automatically, but the driver does not
- Linux packages missing from the image
- Headless tests failing because of sandbox or shared memory limits
Option 2, use Selenium Manager or driver management
Modern Selenium can manage drivers more automatically, which reduces manual driver download logic. That helps, but it does not remove the need to install the browser itself or solve all environment issues. Read the Selenium docs carefully for the version you use, because setup behavior changes over time.
Option 3, run tests in a browser container
This is usually the most reproducible approach for a team that can support Docker. The container image should include the browser, the driver, and the system libraries needed to launch and render correctly. You still need to think about test data, secrets, and network access, but the browser stack itself becomes much easier to pin.
If a Selenium test fails only in Jenkins, assume the environment first, not the app. Browser automation is very good at exposing environment drift.
Add artifacts that make failures debuggable
A browser test that only emits pass or fail is not enough for CI. You want evidence that shortens triage.
Useful artifacts include:
- JUnit XML test results
- Screenshots on failure
- Browser console logs
- Page source or DOM snapshots when appropriate
- Video, if your setup supports it
A common pattern in Python is to take a screenshot in pytest teardown when a test fails.
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
@pytest.fixture def driver(): options = Options() options.add_argument(“–headless=new”) options.add_argument(“–no-sandbox”) options.add_argument(“–disable-dev-shm-usage”) d = webdriver.Chrome(options=options) yield d d.quit()
def test_homepage_title(driver): driver.get(“https://example.com”) assert “Example Domain” in driver.title
In a larger suite, you would usually add a hook that inspects the test outcome and writes a screenshot to a known directory. Jenkins can archive that directory after the run.
Why artifacts matter operationally
When a build fails, the first question is whether the application regressed or the test environment regressed. Artifacts narrow that gap. They also make it easier to separate:
- locator breakage
- page load timing issues
- authentication failures
- browser crashes
- environment misconfiguration
Without artifacts, teams often end up rerunning the pipeline until it passes, which is not debugging, it is wishful thinking with extra CPU cost.
Publish Selenium results in Jenkins
Jenkins understands JUnit-style results well. If your test runner can emit them, you should publish them. That lets Jenkins show trends, failed test cases, and historical stability.
For pytest, the earlier example already writes reports/junit.xml. In Jenkins, the junit step collects that file. For Java projects using JUnit, Surefire or Failsafe can emit compatible XML. For JavaScript, many reporters can do the same.
If you want richer HTML reports, you can generate them separately and archive them as artifacts. The pattern is the same, report generation in the test step, publication in the post block.
A more realistic Jenkins browser test pipeline
Once the basic flow works, the next step is making it resilient enough for repeated execution.
groovy pipeline { agent { docker { image ‘python:3.12-slim’ args ‘–shm-size=2g’ } }
stages { stage(‘Checkout’) { steps { checkout scm } }
stage('Dependencies') {
steps {
sh 'apt-get update && apt-get install -y chromium chromium-driver'
sh 'python -m venv .venv'
sh '. .venv/bin/activate && pip install -r requirements.txt'
}
}
stage('Run tests') {
steps {
sh '. .venv/bin/activate && pytest --junitxml=reports/junit.xml'
}
} }
post { always { junit ‘reports/junit.xml’ archiveArtifacts artifacts: ‘reports/*/’, allowEmptyArchive: true } } }
This example is intentionally not a production-ready final form for every team, because browser dependencies in containers vary by base image. But it shows the shape of the solution, explicit install, explicit test execution, explicit publication.
Common failure modes and how to handle them
1. Flaky waits
The most common source of instability is timing. A test clicks a button before the element is ready, or asserts before a page has finished loading. Prefer explicit waits over arbitrary sleep calls.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10) button = wait.until(EC.element_to_be_clickable((By.ID, “save”))) button.click()
2. Shared state between tests
If tests reuse a browser session or share data without cleanup, Jenkins will expose the coupling faster than local execution does. Keep each test independent where possible. If you need seeded data, create it in setup and clean it in teardown.
3. Headless-only assumptions
Some browser differences show up only in CI, especially around viewport size, fonts, and rendering. If a test depends on layout, make the viewport explicit and avoid brittle absolute positioning checks.
4. Authentication and secrets
CI runs should not hardcode credentials in pipeline files. Store secrets in Jenkins credentials and inject them at runtime. Treat browser credentials with the same care as API keys.
5. Slow test suites
If your Jenkins run takes too long, teams stop using it as a signal. Split fast smoke tests from slower end-to-end coverage. Run smoke tests on every change, broader regression on a schedule or after merge.
A practical structure for Selenium CI pipeline ownership
The goal is not just to make tests pass. The goal is to make the system operable by more than one person.
A maintainable structure usually looks like this:
- Test code in the same repository as the application or in a dedicated automation repo with clear versioning
- Shared fixtures or page objects, but not so many abstractions that debugging becomes hard
- A stable naming convention for reports and artifacts
- A small number of pipeline entry points, such as smoke, regression, and full suite
- Clear retry policy, if retries are used at all
Retries deserve caution. They can reduce noise from transient infrastructure issues, but they can also hide real failures. If you add retries, log them and treat repeated retries as a signal that the suite needs attention.
When Jenkins is the right place for Selenium
Jenkins is a good fit when:
- your team already uses it for CI
- you need control over agents, network, or secrets
- you want test execution close to production-like infrastructure
- your organization is comfortable maintaining build images and agents
Jenkins is a poor fit when:
- nobody owns agent maintenance
- browser setup changes frequently and breaks builds
- the team wants test execution without infrastructure work
- most of the value is in authoring and reviewing tests, not managing runners
For teams that do not want to maintain Jenkins browser test infrastructure, a simpler alternative can make sense. Endtest is one option to evaluate, especially if you want an agentic AI Test automation platform with low-code or no-code workflows and less environment maintenance. It also has a migration path for existing Selenium suites, including Java, Python, and C#.
That said, the right choice depends on ownership. If your team needs deep control over code, browser behavior, and CI internals, Selenium in Jenkins is still a defensible setup. If your main problem is maintaining the stack rather than writing the checks, a maintained platform may reduce operational drag.
A short checklist before you scale up
Before you move from a single test to a full pipeline, verify:
- The browser starts reliably on the Jenkins agent
- The driver version matches the browser strategy
- Tests run headless without hidden layout assumptions
- JUnit results are published
- Screenshots or logs are archived on failure
- Credentials come from Jenkins secrets, not source control
- Suite time is acceptable for how often you intend to run it
- The pipeline has one clear owner
If one of those is missing, the setup will usually look fine at first and then become expensive later.
Final take
To run Selenium tests in Jenkins successfully, treat the pipeline as production infrastructure. The test code matters, but the environment matters just as much. Install browsers deliberately, publish machine-readable results, archive failure evidence, and keep your suite independent enough that Jenkins failures tell you something real.
If you get those basics right, Jenkins becomes a useful control point for browser tests instead of a place where flaky tests go to hide. And if that infrastructure burden is not a good use of your team’s time, it is reasonable to evaluate alternatives that keep the test logic editable while offloading more of the runner maintenance.