CI Basics with Jenkins

A regression suite that only runs when someone remembers to run it on their laptop is, in practice, a regression suite that doesn't run. The whole point of the RecipeBox suite we've built across this track is to catch a broken login or a broken "Add Recipe" form before it merges — which means it has to run automatically, on a server, every time someone opens a pull request. That's what Continuous Integration (CI) gives us, and Jenkins is one of the most widely used CI tools for exactly this job.

Why "It Works On My Laptop" Isn't Enough

Running locally onlyRunning in CI on every PR
Depends on someone remembering to run itRuns automatically, every time, no one has to remember
Only tests the author's machine/browser versionRuns in a clean, consistent environment every time
A broken test is discovered whenever, if everA broken test blocks the pull request immediately
Doesn't scale past one or two developersScales to an entire team submitting PRs in parallel

A Basic Jenkins Job Running mvn test

Jenkins jobs come in two common flavors for a suite like ours:

  • Freestyle job — configured through Jenkins' UI. You point it at the RecipeBox test repo, add a build step, and tell it what shell command to run.
  • Pipeline job — configured through a Jenkinsfile checked into the repo itself, as code. More flexible, and the direction most teams eventually move toward.

Either way, the core of the job is the same Maven command you already run locally:

mvn clean test

A minimal declarative Jenkinsfile for the RecipeBox automation repo:

pipeline {
    agent any

    triggers {
        githubPullRequest() // conceptually: trigger on every PR
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://github.com/example/recipebox-tests.git'
            }
        }
        stage('Run Regression Suite') {
            steps {
                sh 'mvn clean test'
            }
        }
    }

    post {
        always {
            junit 'target/surefire-reports/*.xml'
        }
    }
}
Note: the exact trigger syntax depends on which Jenkins plugin you use (GitHub plugin, GitHub Branch Source, a webhook, etc.) — the important concept is just that a pull request event kicks off this pipeline automatically, with no human involved.

Headless Chrome: There's No Display on a CI Server

Your laptop has a screen, so a normal Chrome window popping up during a test run is fine. A Jenkins build agent usually doesn't have a display attached at all. Running Chrome headless — with no visible UI — solves this, and is also faster since there's no rendering to a screen:

ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");   // modern headless mode, Chrome 109+
options.addArguments("--window-size=1920,1080");
options.addArguments("--no-sandbox");      // commonly needed inside CI containers
options.addArguments("--disable-dev-shm-usage"); // avoids /dev/shm size issues in containers

WebDriver driver = new ChromeDriver(options);
driver.get("https://recipebox.example.com/login");
Tip: keep headless mode behind an environment variable or Maven profile rather than hard-coding it, so developers can still run tests with a visible browser locally while CI always runs headless:
boolean isCi = Boolean.parseBoolean(System.getenv("CI"));
ChromeOptions options = new ChromeOptions();
if (isCi) {
    options.addArguments("--headless=new");
}

Publishing the TestNG HTML Report as a Build Artifact

TestNG already generates an HTML report under test-output/index.html after every run. Rather than let it disappear when the build finishes, tell Jenkins to keep it around and show it directly on the build page:

post {
    always {
        publishHTML(target: [
            reportDir: 'test-output',
            reportFiles: 'index.html',
            reportName: 'TestNG Report',
            keepAll: true
        ])
        archiveArtifacts artifacts: 'test-output/**', allowEmptyArchive: true
    }
}
StepWhat it gives the team
publishHTMLA clickable "TestNG Report" link right on the Jenkins build page — no digging through console logs.
archiveArtifactsThe raw report files saved with the build, so you can go back and check what a run from three weeks ago actually reported.
junit step (previous section)Jenkins' own pass/fail trend graph across builds, parsed from Surefire's XML output.
Result: every RecipeBox pull request now automatically triggers the full regression suite headlessly, and anyone reviewing the PR can click straight into a readable pass/fail report without pulling the branch or running anything locally themselves.
Next: we've now covered every building block — locators, waits, TestNG, POM, data -driven testing, parallel execution, and CI. Head to the final lesson, Automation Framework Best Practices, to see how it all ties together into habits worth keeping.