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
Advertisement

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.
  • when runs regression only when asked or on the nightly timer.
  • parallel runs 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.
  • input pauses for approval on main, 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, and withCredentials.
  • 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.