没有自动化测试的CI/CD就像没有刹车的跑车——速度越快越危险。自动化测试是DevOps质量保证的基石,从单元测试到端到端测试,构建完整的测试金字塔。
/\
/ \ E2E测试(少量)
/ E2E\ - 用户流程验证
/──────\ - 执行慢,维护成本高
/ \
/ 集成测试 \ Integration测试(适量)
/ \ - API/服务间交互
/──────────────\ - 中等速度
/ \
/ 单元测试 \ Unit测试(大量)
/____________________\ - 快速,稳定,成本低
- 覆盖所有逻辑分支
| 测试类型 | 覆盖率目标 | 执行时间 | 运行频率 |
|---|---|---|---|
| 单元测试 | 80%+ | 秒级 | 每次提交 |
| 集成测试 | 关键路径100% | 分钟级 | 每次PR |
| E2E测试 | 核心流程100% | 10分钟+ | 合并到main |
| 性能测试 | 关键接口 | 分钟级 | 每日/发版前 |
| 安全测试 | OWASP Top10 | 分钟级 | 每周/发版前 |
# JavaScript/Node.js
npm install --save-dev jest @types/jest
cat > sum.test.js << 'EOF'
const sum = require('./sum');
describe('sum function', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('handles negative numbers', () => {
expect(sum(-1, 1)).toBe(0);
});
test('throws on non-numeric input', () => {
expect(() => sum('a', 1)).toThrow();
});
});
EOF
npm test
# Python
pip install pytest pytest-cov
cat > test_calculator.py << 'EOF'
import pytest
from calculator import Calculator
@pytest.fixture
def calc():
return Calculator()
class TestCalculator:
def test_add(self, calc):
assert calc.add(1, 2) == 3
def test_divide_by_zero(self, calc):
with pytest.raises(ValueError, match="除零错误"):
calc.divide(1, 0)
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3), (-1, 1, 0), (0, 0, 0)
])
def test_add_cases(self, calc, a, b, expected):
assert calc.add(a, b) == expected
EOF
pytest --cov=calculator --cov-report=html test_calculator.py
# API集成测试(Python + pytest + httpx)
pip install pytest-asyncio httpx
cat > test_api.py << 'EOF'
import pytest
from httpx import AsyncClient
from app.main import app # FastAPI应用
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as c:
yield c
@pytest.mark.asyncio
async def test_health(client):
response = await client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "ok"
@pytest.mark.asyncio
async def test_create_order(client):
response = await client.post("/api/orders", json={
"product_id": "P001",
"quantity": 2
})
assert response.status_code == 201
data = response.json()
assert "order_id" in data
assert data["status"] == "created"
EOF
# 使用Testcontainers(真实数据库)
pip install testcontainers
cat > test_with_db.py << 'EOF'
from testcontainers.postgres import PostgresContainer
def test_with_real_db():
with PostgresContainer("postgres:16") as pg:
engine = create_engine(pg.get_connection_url())
# 使用真实数据库测试
result = engine.execute("SELECT 1")
assert result.scalar() == 1
EOF
# k6性能测试脚本
cat > load-test.js << 'EOF'
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
const errorRate = new Rate('errors');
export const options = {
stages: [
{ duration: '30s', target: 20 }, // 逐步加到20用户
{ duration: '1m', target: 20 }, // 保持20用户1分钟
{ duration: '30s', target: 100 }, // 压测加到100用户
{ duration: '1m', target: 100 }, // 保持100用户
{ duration: '30s', target: 0 }, // 逐步降为0
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95%请求<500ms
errors: ['rate<0.05'], // 错误率<5%
},
};
export default function () {
const res = http.get('https://api.example.com/products');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
}) || errorRate.add(1);
sleep(1);
}
EOF
# 运行
k6 run load-test.js
# 输出包含: RPS, 延迟分布, 错误率等
代码提交 → CI Pipeline
│
┌──────────┼──────────┐
▼ ▼ ▼
Lint Unit Test SAST
(ESLint) (Jest) (SonarQube)
│ │ │
└──────┬───┘──────────┘
▼
Integration Test
(pytest + Testcontainers)
│
┌──────┼──────────┐
▼ ▼ ▼
E2E Perf Test DAST
(Cypress)(k6) (OWASP ZAP)
│ │ │
└──────┴──────────┘
▼
构建Docker镜像 → Push → Deploy
# 常见原因及解决:
# 1. 环境差异 → 使用Docker统一环境
# 2. 时序问题 → 增加等待/重试
# 3. 数据依赖 → 使用Testcontainers
# 4. 并发冲突 → 随机化端口/数据
# 调试CI测试失败
- name: Debug on failure
if: failure()
run: |
cat test-results.xml
docker logs $(docker ps -q) 2>&1 || true
/\
/ \ E2E测试(少量)
/ E2E\ - 用户流程验证
/──────\ - 执行慢,维护成本高
/ \
/ 集成测试 \ Integration测试(适量)
/ \ - API/服务间交互
/──────────────\ - 中等速度
/ \
/ 单元测试 \ Unit测试(大量)
/____________________\ - 快速,稳定
# Python API集成测试
pip install pytest-asyncio httpx
cat > test_api.py << 'EOF'
import pytest
from httpx import AsyncClient
from app.main import app
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as c:
yield c
@pytest.mark.asyncio
async def test_health(client):
response = await client.get("/health")
assert response.status_code == 200
@pytest.mark.asyncio
async def test_create_order(client):
response = await client.post("/api/orders", json={
"product_id": "P001", "quantity": 2
})
assert response.status_code == 201
assert "order_id" in response.json()
EOF
# 使用Testcontainers(真实数据库)
pip install testcontainers
# 在测试中启动真实PostgreSQL容器
cat > load-test.js << 'EOF'
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 },
{ duration: '1m', target: 100 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
},
};
export default function () {
const res = http.get('https://api.example.com/products');
check(res, {
'status 200': (r) => r.status === 200,
'latency < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
EOF
k6 run load-test.js
掌握测试金字塔、单元/集成/E2E测试、性能测试——质量是DevOps的生命线。