Chương 23 Senior
CI/CD Cho QA
Integrate testing vào CI/CD pipeline — GitHub Actions, Jenkins, Docker và automated quality gates để ship với confidence.
🔄 CI/CD Concepts cho QA Senior
- Continuous Integration (CI)Developer merge code vào main branch thường xuyên (nhiều lần/ngày). Automated tests chạy sau mỗi merge. Fail fast → fix fast. QA benefit: immediate feedback on code quality.
- Continuous Delivery (CD)Automated pipeline build, test và prepare release. Nhưng deploy lên production cần manual approval. QA có thể vẫn review trước production deploy.
- Continuous DeploymentTất cả automated — không cần manual approval. Mỗi merge lên main → auto-deploy production nếu all tests pass. Mature teams chỉ. QA phải cực kỳ tin tưởng vào automation coverage.
- Pipeline StagesThông thường: Build → Unit Tests → Integration Tests → Deploy to Staging → E2E Tests → Security Scan → Manual QA (optional) → Deploy to Production.
- Fail Fast PrincipleSlow tests last, fast tests first. Unit tests (seconds) → Integration tests (minutes) → E2E tests (hours). Developer gets fast feedback on obvious issues, slower on complex ones.
💡 QA Role trong CI/CD
Senior QA không chỉ "chạy tests trong CI" mà còn: design pipeline stages, define quality gates, choose which tests go where, optimize pipeline speed, và ensure reporting is actionable. QA owns the quality of the pipeline, not just the tests.
🐙 GitHub Actions — Playwright Integration
# .github/workflows/test-pipeline.yml
name: QA Test Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
BASE_URL: ${{ secrets.STAGING_URL }}
API_TOKEN: ${{ secrets.QA_API_TOKEN }}
jobs:
# Job 1: Fast unit/integration tests
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm test # unit tests
- run: npm run test:api # API integration tests
# Job 2: E2E tests (depends on unit-tests passing)
e2e-tests:
needs: unit-tests # only run if unit tests pass
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chromium, firefox] # parallel per browser
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Run E2E tests
run: npx playwright test --browser=${{ matrix.browser }}
- name: Upload test report
uses: actions/upload-artifact@v4
if: always() # upload even on failure
with:
name: playwright-report-${{ matrix.browser }}
path: playwright-report/
# Job 3: Performance tests (scheduled, not on every PR)
performance:
if: github.ref == 'refs/heads/main' # only on main branch
needs: e2e-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm install -g k6
- name: Run load test
run: k6 run --out json=results.json tests/performance/load.js
- name: Check thresholds
run: node scripts/check-performance.js results.json
Secrets Management:
- Credentials → GitHub Secrets (Settings → Secrets → Actions). Never hardcode in YAML.
- Environment-specific configs → GitHub Environments (staging, production). Require approval before deploying to production.
- Rotate secrets regularly. Use least-privilege accounts for CI.
⚙️ Jenkins Pipeline Basics
// Jenkinsfile — Declarative Pipeline
pipeline {
agent any
environment {
BASE_URL = credentials('staging-url')
SLACK_TOKEN = credentials('slack-token')
}
stages {
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Unit Tests') {
steps {
sh 'npm test -- --reporter=junit'
}
post {
always {
junit 'reports/junit.xml' // publish results
}
}
}
stage('E2E Tests') {
parallel { // run browsers in parallel
stage('Chrome') {
steps { sh 'npx playwright test --browser=chromium' }
}
stage('Firefox') {
steps { sh 'npx playwright test --browser=firefox' }
}
}
}
stage('Deploy to Production') {
when { branch 'main' }
input { message 'Deploy to production?'; ok 'Deploy' } // manual approval
steps { sh './deploy.sh production' }
}
}
post {
failure {
slackSend channel: '#qa-alerts',
message: "Pipeline FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
🐳 Docker cho QA
Tại sao QA cần Docker:
- Consistent test environment — "works on my machine" problem eliminated
- Spin up test dependencies (DB, message queue) locally
- Isolation — tests không ảnh hưởng nhau khi chạy parallel
- CI environment identical to local — no surprises
# docker-compose.test.yml — Test environment
version: '3.8'
services:
app:
image: myapp:latest
ports: ["3000:3000"]
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://postgres:password@db:5432/testdb
depends_on: [db, redis]
db:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
redis:
image: redis:7-alpine
playwright:
image: mcr.microsoft.com/playwright:v1.40.0-jammy
volumes:
- ./tests:/tests
- ./playwright-report:/playwright-report
environment:
- BASE_URL=http://app:3000
command: npx playwright test
depends_on: [app]
Basic Docker commands cho QA:
# Start test environment
docker-compose -f docker-compose.test.yml up -d
# Run tests
docker-compose -f docker-compose.test.yml run playwright
# View logs
docker-compose logs -f app
# Cleanup
docker-compose -f docker-compose.test.yml down -v
🧩 Integrating Tests vào Pipeline
| Test Type | Where in Pipeline | Trigger | Fail Action |
|---|---|---|---|
| Unit Tests | Stage 1 — earliest | Every commit | Block merge |
| API/Integration Tests | Stage 2 | Every PR | Block merge |
| Smoke Tests (E2E) | Stage 3 — after deploy | After staging deploy | Alert + revert |
| Full Regression (E2E) | Stage 4 | Nightly or pre-release | Block release |
| Performance Tests | Stage 5 | Pre-release + weekly | Investigate, may block |
| Security Scan | Stage 5 (parallel) | Pre-release + weekly | Security review required |
| Accessibility Scan | Stage 3 | PR (on changed pages) | Block merge if Critical |
🏆 Pipeline Design Principles
1. Fail fast: Fastest tests first. 2. Parallel where possible: Browsers, test suites, services — run simultaneously. 3. Cache aggressively: npm cache, docker layers, browser binaries. 4. Isolate test data: Each test run uses isolated data — no shared state. 5. Report clearly: Failed test = clear artifact with screenshots/logs, not just "FAILED."
📊 Test Reporting trong CI
- JUnit XML FormatUniversal test report format. Generated by most frameworks. Parsed by Jenkins (junit step), GitHub Actions (test-reporter), and Azure DevOps. Playwright:
reporter: ['junit']. - Allure ReportRich HTML reports với screenshots, videos, test steps. Playwright/Jest plugin. Host trên server hoặc serve từ CI artifact. Very readable for stakeholders.
- GitHub PR CommentsAutomatic comment trên PR với test summary: "✅ 150 tests passed | ❌ 3 failed | Screenshots attached." Shows results without leaving PR context.
- Slack NotificationsKhi pipeline fails → Slack alert tới #qa-alerts channel với: branch, build number, failing tests, link to report. Fast response time.
- Test History TrendingTrack pass/fail over time. GitHub Actions: "Test Reporter" action. Jenkins: "Test Results Analyzer" plugin. Identify patterns: does a specific test fail every Monday?
✏️ Bài Tập
📝 Bài Tập: CI/CD Pipeline Design
Design CI/CD pipeline cho một team: 4 devs, 2 QA, 1 PM. Tech stack: React frontend, Node.js API, PostgreSQL DB. Deployment: AWS (staging + production). Current state: manual deployments, no automation.
- Sketch (text/table) toàn bộ pipeline stages từ developer push code đến production deployment
- Identify 3 quality gates và define criteria cho mỗi gate
- Viết GitHub Actions workflow YAML (skeleton, không cần full implementation) cho smoke test stage
- Define Slack notification strategy: khi nào notify, channel nào, format message
- Estimate: pipeline này mất bao lâu để run từ đầu đến cuối? Optimize để giảm xuống dưới 15 phút.