A Jenkins pipeline defines a CI/CD workflow — checkout, build, test, deploy — as code. That code lives in a file called Jenkinsfile, stored in Git next to the project, so the pipeline is reviewed, versioned and reproducible like any other code. For a test automation team, the pipeline decides when tests run, on which browsers, how failures are reported and whether a release can go ahead. This guide explains the building blocks and then puts them together in one complete Jenkinsfile.
Pipeline Building Blocks
| Block | What it does | Example |
|---|---|---|
pipeline |
Wraps the whole declarative pipeline | pipeline { … } |
agent |
Where it runs — any agent, a labelled agent, or a Docker container | agent { label 'linux' } |
parameters |
Inputs chosen when the build starts | choice(name: 'ENV', choices: ['qa','staging']) |
environment |
Variables for all steps, including credentials | API_KEY = credentials('qa-api-key') |
options |
Job behaviour — timeouts, log retention, timestamps | timeout(time: 60, unit: 'MINUTES') |
triggers |
Automatic starts — schedules, polling, webhooks | cron('H 2 * * 1-5') |
stages / stage |
Named phases shown in the UI | stage('Smoke') { … } |
steps |
The commands inside a stage | sh 'mvn -B test' |
when |
Run a stage only if a condition is true | when { branch 'main' } |
parallel |
Run stages at the same time | Chrome and Firefox suites together |
input |
Pause for a human decision | Approve deployment to production |
post |
Actions after the pipeline or a stage, depending on the result | always, success, failure, unstable |
A Complete Declarative Jenkinsfile for Test Automation
pipeline {
agent any
parameters {
choice(name: 'ENV', choices: ['qa', 'staging'], description: 'Environment to test')
booleanParam(name: 'RUN_REGRESSION', defaultValue: false, description: 'Run the full regression after smoke')
}
environment {
QA_PASSWORD = credentials('qa-test-user-password') // masked in the console log
}
options {
timeout(time: 90, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '30'))
timestamps()
}
triggers {
cron('H 2 * * 1-5') // nightly on weekdays
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Smoke') {
steps {
sh "mvn -B clean test -Denv=${params.ENV} -Dgroups=smoke"
}
}
stage('Regression') {
when {
anyOf {
expression { params.RUN_REGRESSION }
triggeredBy 'TimerTrigger' // always on the nightly run
}
}
parallel {
stage('Chrome') {
steps {
sh "mvn -B test -Denv=${params.ENV} -Dgroups=regression -Dbrowser=chrome -Dsurefire.reportsDirectory=target/reports-chrome"
}
}
stage('Firefox') {
steps {
sh "mvn -B test -Denv=${params.ENV} -Dgroups=regression -Dbrowser=firefox -Dsurefire.reportsDirectory=target/reports-firefox"
}
}
}
}
stage('Approve release') {
when {
branch 'main'
}
steps {
timeout(time: 1, unit: 'DAYS') {
input message: 'Tests passed. Deploy to staging?', ok: 'Deploy'
}
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: 'target/**/*.xml'
archiveArtifacts artifacts: 'target/screenshots/**',
allowEmptyArchive: true
}
failure {
mail to: 'qa-team@example.com',
subject: "FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER} (${params.ENV})",
body: "See ${env.BUILD_URL}"
}
}
}
What Each Part Is Doing
- Parameters let a tester run smoke only, or smoke plus regression, against QA or staging from the "Build with Parameters" page.
credentials()pulls the test user's password from Jenkins' credential store; it never appears in the Jenkinsfile or in the log.- Smoke first: If the quick checks fail, the pipeline stops before spending an hour on regression.
whenruns regression only when asked or on the nightly timer.parallelruns Chrome and Firefox at the same time, each writing reports to its own folder so they don't overwrite each other. Parallel branches need enough agents or executors — and browsers — to actually run side by side.inputpauses for approval onmain, with a timeout so a forgotten build doesn't wait forever. (when { branch 'main' }works in multibranch pipeline jobs, where Jenkins knows which branch is being built.)post { always }publishes results and screenshots whether tests passed or failed.
Running Chrome and Firefox on agents usually means containers: see the Docker + Selenium Grid Guide.
The triggers block is explained in Jenkins CRON & Webhooks.
Declarative vs Scripted Pipeline
The same smoke-then-regression flow in scripted syntax:
node('linux') {
stage('Checkout') {
checkout scm
}
try {
stage('Smoke') {
sh 'mvn -B clean test -Dgroups=smoke'
}
if (params.RUN_REGRESSION) {
stage('Regression') {
parallel(
chrome: {
sh 'mvn -B test -Dgroups=regression -Dbrowser=chrome'
},
firefox: {
sh 'mvn -B test -Dgroups=regression -Dbrowser=firefox'
}
)
}
}
}
finally {
junit allowEmptyResults: true,
testResults: 'target/**/*.xml'
}
}
| Declarative | Scripted | |
|---|---|---|
| Starts with | pipeline { } |
node { } |
| Structure | Fixed sections such as agent, stages, and post |
Free-form Groovy |
| Conditions | when { } |
Ordinary if |
| Error handling | post { failure { } } |
try / catch / finally |
| Validation | Syntax checked before running | Errors appear when the line runs |
| Best for | Most CI/CD and test pipelines | Unusual, highly dynamic logic |
Declarative is the default choice; when you need a bit of Groovy inside it, wrap it in a script { } block rather than switching the whole pipeline to scripted.
Tips for Writing and Debugging Jenkinsfiles
- Pipeline Syntax — The link available on Jenkins pipeline jobs can generate correct snippets for steps such as
junit,archiveArtifacts, andwithCredentials. - Replay — Lets you edit and rerun the Jenkinsfile of a previous build without committing, which is useful for experiments.
- Double quotes — Interpolate Groovy variables such as
"${params.ENV}"; single quotes pass the text to the shell unchanged. Don't interpolate secrets into strings — let the shell read the environment variable. - Triggers, parameters and options — These are registered only after the pipeline has run once; run it manually after changing them.
- Stage View / Blue Ocean — Show which stage failed and how long each took, which is useful for spotting a slow test stage.
From Real Projects
In my projects I ran Selenium and TestNG suites in batch, group, parallel and cross-browser mode. A CI server like Jenkins is the natural next step: the same suites, triggered automatically after each build or on a schedule, with results published for the whole team. On Canolog, the many forms and screens across sales, inventory, finance and service are where keeping page details in POM classes and reusable steps in a business library paid off. Start declarative and add a script block only when you truly need it.
📚 Official documentation: Selenium Grid documentation · Selenium documentation
FAQs
What Is a Jenkins Pipeline?
A CI/CD workflow defined as code — stages such as checkout, build, test and deploy — executed by Jenkins.
What Is a Jenkinsfile?
The text file containing the pipeline definition, stored in the project's Git repository so it's versioned and reviewed.
What Is the Difference Between Declarative and Scripted Pipelines?
Declarative uses a fixed pipeline { } structure that's easier to read and is validated before running; scripted is free-form Groovy in node { }, more flexible but harder to maintain.
How Do You Run Tests in Parallel in Jenkins?
With a parallel block of stages — for example, one per browser — making sure each writes reports to its own location and enough agents are available.
How Do You Add a Manual Approval?
An input step inside a stage, ideally wrapped in a timeout.
What Does the Post Block Do?
Runs steps after the pipeline or stage depending on the result — always, success, failure, unstable, changed — typically publishing reports and sending notifications.
Related