CI/CDIntermediate
CI/CD for Test Automation
Set up continuous integration and deployment pipelines for your test automation with GitHub Actions, Jenkins, and more
6 min read
...
ci-cdautomationgithub-actionsjenkinsdevops
Introduction
Integrating test automation into CI/CD pipelines ensures code quality and catches bugs early. This guide covers setting up automated testing in popular CI/CD platforms.
GitHub Actions
Basic Test Workflow
# .github/workflows/test.yml
name: Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run unit tests
run: npm test
- name: Run E2E tests
run: npm run test:e2eParallel Testing
name: Tests
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests (shard ${{ matrix.shard }}/4)
run: npx playwright test --shard=${{ matrix.shard }}/4Test with Multiple Node Versions
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testUpload Test Artifacts
- name: Run tests
run: npm test
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: |
test-results/
screenshots/
videos/
retention-days: 30Jenkins Pipeline
Declarative Pipeline
// Jenkinsfile
pipeline {
agent any
tools {
nodejs 'NodeJS 18'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Unit Tests') {
steps {
sh 'npm test'
}
post {
always {
junit 'test-results/junit.xml'
}
}
}
stage('E2E Tests') {
steps {
sh 'npm run test:e2e'
}
post {
always {
publishHTML([
reportDir: 'playwright-report',
reportFiles: 'index.html',
reportName: 'Playwright Report'
])
}
}
}
}
post {
always {
cleanWs()
}
failure {
mail to: 'team@example.com',
subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
body: "Build failed: ${env.BUILD_URL}"
}
}
}Parallel Execution in Jenkins
stage('Tests') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
stage('E2E Tests') {
steps {
sh 'npm run test:e2e'
}
}
}
}GitLab CI
# .gitlab-ci.yml
image: node:18
stages:
- install
- lint
- test
- e2e
cache:
paths:
- node_modules/
install:
stage: install
script:
- npm ci
artifacts:
paths:
- node_modules/
expire_in: 1 day
lint:
stage: lint
script:
- npm run lint
unit-tests:
stage: test
script:
- npm test
coverage: '/Statements\s+:\s+(\d+\.\d+)%/'
artifacts:
reports:
junit: test-results/junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
e2e-tests:
stage: e2e
script:
- npm run test:e2e
artifacts:
when: always
paths:
- playwright-report/
- screenshots/
expire_in: 30 daysDocker for Consistent Test Environments
Dockerfile for Tests
FROM node:18
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci
# Install Playwright browsers
RUN npx playwright install --with-deps
# Copy application code
COPY . .
# Run tests
CMD ["npm", "test"]Docker Compose for Full Stack Testing
# docker-compose.test.yml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_PASSWORD: testpass
ports:
- "5432:5432"
redis:
image: redis:7
ports:
- "6379:6379"
app:
build: .
depends_on:
- postgres
- redis
environment:
DATABASE_URL: postgres://postgres:testpass@postgres:5432/testdb
REDIS_URL: redis://redis:6379
command: npm testTest Reporting
JUnit XML Reports
// jest.config.js
module.exports = {
reporters: [
'default',
[
'jest-junit',
{
outputDirectory: 'test-results',
outputName: 'junit.xml',
},
],
],
};HTML Reports
// playwright.config.js
export default {
reporter: [
['html', { outputFolder: 'playwright-report' }],
['junit', { outputFile: 'test-results/junit.xml' }],
],
};Code Coverage
# GitHub Actions
- name: Run tests with coverage
run: npm test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.jsonBest Practices
1. Fast Feedback
# Run quick checks first
jobs:
quick-checks:
runs-on: ubuntu-latest
steps:
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
full-tests:
needs: quick-checks # Only run if quick checks pass
runs-on: ubuntu-latest
steps:
- name: Run all tests
run: npm test2. Fail Fast
jobs:
test:
strategy:
fail-fast: true # Stop all jobs if one fails
matrix:
shard: [1, 2, 3, 4]3. Conditional Tests
# Only run E2E tests on main branch
- name: E2E Tests
if: github.ref == 'refs/heads/main'
run: npm run test:e2e4. Secrets Management
- name: Run tests
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
run: npm testPerformance Optimization
1. Caching
# Cache dependencies
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# Cache Playwright browsers
- name: Cache Playwright browsers
uses: actions/cache@v3
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}2. Selective Testing
# Only run affected tests
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
- name: Run tests for changed files
run: |
npx jest --findRelatedTests ${{ steps.changed-files.outputs.all_changed_files }}3. Test Retries
// playwright.config.js
export default {
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
};Monitoring and Notifications
Slack Notifications
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Tests failed on ${{ github.ref }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Build failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}Test Analytics
# Send test results to analytics service
- name: Upload test results
if: always()
uses: buildpulse/buildpulse-action@main
with:
account: ${{ secrets.BUILDPULSE_ACCOUNT }}
repository: ${{ secrets.BUILDPULSE_REPOSITORY }}
path: test-results/junit.xml
key: ${{ secrets.BUILDPULSE_ACCESS_KEY }}Complete Example
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run lint
unit-tests:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v3
e2e-tests:
needs: unit-tests
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shard }}/3
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report-${{ matrix.shard }}
path: playwright-report/
deploy:
needs: [unit-tests, e2e-tests]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to production
run: echo "Deploying..."Conclusion
Effective CI/CD for test automation requires:
- Fast, reliable tests
- Parallel execution for speed
- Proper caching and optimization
- Clear reporting and notifications
- Security best practices
Start simple, then optimize based on your needs.
Further Reading
Comments (0)
Loading comments...