Certified Jenkins Engineer
Pipeline Enhancement and Caching
Demo Refactoring Unit Test Stage
In this guide, we’ll refactor our Jenkins pipeline so that the Unit Testing stage runs concurrently on Node.js 18, 19, and 20. This approach speeds up feedback and ensures compatibility across multiple runtime versions.
Table of Contents
- Initial Pipeline Configuration
- Adding Parallel Unit Tests
- [Understanding the Node 20 Failure](#understanding-the-node 20-failure)
- [Fixing Dependencies for Node 20](#fixing-dependencies-for-node 20)
- Complete Refactored Pipeline
- Links and References
Initial Pipeline Configuration
We start with a simple declarative pipeline using a Kubernetes agent that defaults to a Node 18 container. By default, all steps in the Unit Testing stage will run on Node 18.
@Library('dasher-trusted-shared-library@featureTrivyScan') _
pipeline {
agent {
kubernetes {
cloud 'dasher-prod-k8s-us-east'
yamlFile 'k8s-agent.yaml'
defaultContainer 'node-18'
}
}
tools {
nodejs 'nodejs-22-6-0'
}
environment {
MONGO_URI = "mongodb+srv://supercluster.d8jjj.mongodb.net/superData"
MONGO_DB_CREDS = credentials('mongo-db-credentials')
MONGO_DB_USERNAME = credentials('mongo-db-username')
MONGO_PASSWORD = credentials('mongo-db-password')
}
stages {
stage('Unit Testing') {
options { retry(2) }
steps {
sh 'node -v'
sh 'npm test'
}
}
}
}
Note
All unit tests will execute inside the node-18
container by default.
You can learn more about the Jenkins Kubernetes Plugin here.
Adding Parallel Unit Tests
To run tests on multiple Node.js versions simultaneously, we replace the single Unit Testing stage with parallel sub-stages:
pipeline {
agent {
kubernetes {
cloud 'dasher-prod-k8s-us-east'
yamlFile 'k8s-agent.yaml'
defaultContainer 'node-18'
}
}
tools {
nodejs 'nodejs-22-6-0'
}
environment {
MONGO_URI = 'mongodb+srv://supercluster.d83jj.mongodb.net/superData'
MONGO_DB_CREDENTIALS = credentials('mongo-db-credentials')
MONGO_USERNAME = credentials('mongo-db-username')
MONGO_PASSWORD = credentials('mongo-db-password')
SONAR_SCANNER_HOME = tool 'sonar-scanner-610'
GITEA_TOKEN = credentials('gitea-api-token')
}
options {
disableResume()
disableConcurrentBuilds abortPrevious: true
}
stages {
stage('Installing Dependencies') {
options { timestamps() }
steps {
sh 'node -v'
sh 'npm install --no-audit'
}
}
stage('Dependency Scanning') {
parallel {
// other scanning stages...
}
}
stage('Unit Testing') {
parallel {
stage('NodeJS 18') {
options { retry(2) }
steps {
sh 'node -v'
sh 'npm test'
}
}
stage('NodeJS 19') {
options { retry(2) }
steps {
container('node-19') {
sh 'sleep 10s' // avoid port conflicts
sh 'node -v'
sh 'npm test'
}
}
}
stage('NodeJS 20') {
agent {
docker {
image 'node:20-alpine'
}
}
options { retry(2) }
steps {
sh 'node -v'
sh 'npm test'
}
}
}
}
}
}
Node.js Version | Container / Image | Configuration Location |
---|---|---|
18 | defaultContainer | Kubernetes Pod (node-18 ) |
19 | container('node-19') | Kubernetes Pod (node-19 ) |
20 | Docker node:20-alpine | Standalone Docker agent |
Understanding the Node 20 Failure
When the Node 20 stage runs as a separate Docker container, you might encounter:
> npm test
> Solar [email protected] test
> mocha app-test.js --timeout 10000 --reporter mocha-junit-reporter --exit
sh: mocha: not found
script returned exit code 127
The error occurs because npm install
was only executed in the Kubernetes Pod (Node 18/19). The separate Node 20 container has no node_modules
directory.
Warning
Docker agents do not share volumes with your Kubernetes Pod. Make sure to install dependencies inside every container or orchestrate a shared volume mount.
Fixing Dependencies for Node 20
Quick Fix: Inline npm install
Add the install step directly to the Node 20 stage:
stage('NodeJS 20') {
agent {
docker {
image 'node:20-alpine'
}
}
options { retry(2) }
steps {
sh 'npm install'
sh 'node -v'
sh 'npm test'
}
}
Cleaner Approach: Separate Install & Test Sub-Stages
Within the parallel group for Node 20, create two sub-stages—one for installing dependencies, one for running tests:
stage('Unit Testing') {
parallel {
// NodeJS 18 & 19 as before...
stage('NodeJS 20') {
parallel {
stage('Install Dependencies') {
agent {
docker {
image 'node:20-alpine'
}
}
steps {
sh 'npm install'
}
}
stage('Run Tests') {
agent {
docker {
image 'node:20-alpine'
}
}
options { retry(2) }
steps {
sh 'node -v'
sh 'npm test'
}
}
}
}
}
}
This structure ensures each Node.js container installs its own dependencies and runs tests in isolation.
Complete Refactored Pipeline
Putting it all together:
@Library('dasher-trusted-shared-library@featureTrivyScan') _
pipeline {
agent {
kubernetes {
cloud 'dasher-prod-k8s-us-east'
yamlFile 'k8s-agent.yaml'
defaultContainer 'node-18'
}
}
tools {
nodejs 'nodejs-22-6-0'
}
environment {
MONGO_URI = 'mongodb+srv://supercluster.d83jj.mongodb.net/superData'
MONGO_DB_CREDENTIALS= credentials('mongo-db-credentials')
MONGO_USERNAME = credentials('mongo-db-username')
MONGO_PASSWORD = credentials('mongo-db-password')
SONAR_SCANNER_HOME = tool 'sonar-scanner-610'
GITEA_TOKEN = credentials('gitea-api-token')
}
options {
disableResume()
disableConcurrentBuilds abortPrevious: true
}
stages {
stage('Installing Dependencies') {
steps {
sh 'node -v'
sh 'npm install --no-audit'
}
}
stage('Dependency Scanning') {
parallel {
// other scanning stages...
}
}
stage('Unit Testing') {
parallel {
stage('NodeJS 18') { /* ... */ }
stage('NodeJS 19') { /* ... */ }
stage('NodeJS 20') {
parallel {
stage('Install Dependencies') { /* ... */ }
stage('Run Tests') { /* ... */ }
}
}
}
}
}
}
Links and References
- Jenkins Pipeline Documentation
- Kubernetes Plugin for Jenkins
- Node.js Official Docker Images
- Mocha Test Framework
Watch Video
Watch video content