Integrating Tests into Your CI/CD Pipeline
Practical approaches to automated testing in continuous delivery workflows.
devops testing ci-cd automation
Integrating Tests into Your CI/CD Pipeline
Automated testing in CI/CD pipelines is essential for maintaining code quality and shipping with confidence. This guide covers practical strategies for integrating different types of tests into your continuous delivery workflow.
The Testing Pyramid in CI/CD
Unit Tests (Base)
- Fast execution (milliseconds)
- Run on every commit
- High code coverage
- Isolate business logic
Integration Tests (Middle)
- Test component interactions
- Database, API, and service integration
- Run on pull requests
- Moderate execution time
End-to-End Tests (Top)
- Test critical user flows
- Run on staging/pre-production
- Slowest but most comprehensive
- Focus on happy paths and critical journeys
CI/CD Platforms
GitHub Actions Example
name: CI Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
- name: Run E2E tests
run: npm run test:e2e
- name: Upload coverage
uses: codecov/codecov-action@v3
GitLab CI Example
stages:
- test
- build
- deploy
unit-test:
stage: test
script:
- npm ci
- npm test
coverage: '/Statements\s*:\s*(\d+\.\d+)%/'
integration-test:
stage: test
services:
- postgres:14
script:
- npm run test:integration
only:
- merge_requests
- main
e2e-test:
stage: test
script:
- npm run test:e2e
only:
- main
Best Practices
1. Fail Fast
Run quick tests first:
# Run linting and unit tests before expensive operations
- Lint
- Unit tests
- Integration tests
- E2E tests
- Build
- Deploy
2. Parallel Execution
Split tests across multiple runners:
test:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npm test -- --shard=${{ matrix.shard }}/4
3. Caching Dependencies
Speed up pipeline with caching:
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
4. Test Different Environments
test:
strategy:
matrix:
node-version: [18.x, 20.x, 21.x]
os: [ubuntu-latest, windows-latest, macos-latest]
Performance Testing in CI/CD
Lighthouse CI
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun
lighthouserc.json:
{
"ci": {
"collect": {
"startServerCommand": "npm run preview",
"url": ["http://localhost:4173"]
},
"assert": {
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}]
}
}
}
}
Load Testing with k6
- name: Run load tests
run: |
docker run --rm -v $(pwd):/scripts grafana/k6 run /scripts/load-test.js
Handling Flaky Tests
- Retry Mechanism:
- name: Run tests with retry
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: npm run test:e2e
- Quarantine Flaky Tests:
// Mark as known flaky
test.skip('flaky test', () => {
// Quarantined until fixed
});
- Monitor Test Stability: Track pass/fail rates over time to identify patterns.
Code Quality Gates
Coverage Requirements
- name: Check coverage
run: |
npm test -- --coverage
npx nyc check-coverage --lines 80 --functions 80 --branches 80
Blocking Merge on Failures
required_status_checks:
strict: true
contexts:
- unit-tests
- integration-tests
Security Testing
Dependency Scanning
- name: Run npm audit
run: npm audit --audit-level=moderate
- name: Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Conclusion
A well-designed CI/CD testing strategy catches bugs early, maintains code quality, and enables confident deployments. Start with a solid foundation of unit tests, gradually add integration and E2E tests, and continuously optimize your pipeline for speed and reliability.
Need help setting up your CI/CD pipeline? Reach out.