
API 测试:完整指南
Postman 集合与自动化
// Postman pre-request script
pm.environment.set("timestamp", Date.now())
pm.environment.set("request_id", pm.variables.replaceIn("{{$randomUUID}}"))
// HMAC authentication
const crypto = require('crypto-js')
const secret = pm.environment.get("API_SECRET")
const body = pm.request.body.raw || ''
const signature = crypto.HmacSHA256(body, secret).toString()
pm.request.headers.add({key: 'X-Signature', value: signature})
// Postman test script
pm.test("Status is 200", () => pm.response.to.have.status(200))
pm.test("Response time < 500ms", () => pm.expect(pm.response.responseTime).to.be.below(500))
pm.test("Response has required fields", () => {
const json = pm.response.json()
pm.expect(json).to.have.property('id')
pm.expect(json).to.have.property('email')
pm.expect(json.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)
})
// Store response for use in subsequent requests
pm.test("Store user ID", () => {
const json = pm.response.json()
pm.environment.set("userId", json.id)
})

Newman CLI(Postman 自动化)
# Install
npm install -g newman
# Run collection
newman run MyAPI.postman_collection.json \
--environment prod.postman_environment.json \
--reporters cli,junit \
--reporter-junit-export results.xml
# With data file (data-driven testing)
newman run MyAPI.postman_collection.json \
--data test-data.json \
--environment staging.json
# GitHub Actions integration
# - uses: actions/checkout@v4
# - run: npm install -g newman
# - run: newman run tests/api.collection.json -e tests/staging.env.json

k6 负载测试
// k6/load-test.js
import http from 'k6/http'
import { check, sleep, group } from 'k6'
import { Rate, Trend, Counter } from 'k6/metrics'
// Custom metrics
const errorRate = new Rate('errors')
const loginDuration = new Trend('login_duration')
const totalRequests = new Counter('total_requests')
export const options = {
stages: [
{ duration: '30s', target: 10 }, // Ramp up
{ duration: '1m', target: 50 }, // Stay at 50 users
{ duration: '30s', target: 100 }, // Spike
{ duration: '30s', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95% under 500ms
errors: ['rate<0.01'], // < 1% errors
http_req_failed: ['rate<0.01'],
},
}
const BASE_URL = 'https://api.example.com'
export function setup() {
// Login and get token
const resp = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: 'test@example.com',
password: 'password123',
}), { headers: { 'Content-Type': 'application/json' } })
return { token: resp.json('access_token') }
}
export default function(data) {
const headers = {
'Authorization': `Bearer ${data.token}`,
'Content-Type': 'application/json',
}
group('User Operations', () => {
// GET users
const listResp = http.get(`${BASE_URL}/users`, { headers })
check(listResp, {
'list status 200': (r) => r.status === 200,
'list has users': (r) => r.json('data').length > 0,
})
errorRate.add(listResp.status !== 200)
totalRequests.add(1)
sleep(0.5)
// Create user
const start = Date.now()
const createResp = http.post(`${BASE_URL}/users`,
JSON.stringify({ name: 'Test User', email: `test${Date.now()}@example.com` }),
{ headers }
)
loginDuration.add(Date.now() - start)
check(createResp, {
'create status 201': (r) => r.status === 201,
})
})
sleep(1)
}
export function teardown(data) {
// Cleanup after test
console.log('Test complete')
}
# Run k6 test
k6 run k6/load-test.js
# Run with cloud
k6 cloud k6/load-test.js
# Generate HTML report
k6 run --out json=results.json k6/load-test.js
k6 report results.json > report.html

使用 Pact 进行契约测试
// Consumer test - defines contract
import { Pact } from '@pact-foundation/pact'
import { fetchUser } from '../src/api/userService'
const provider = new Pact({
consumer: 'frontend-app',
provider: 'user-api',
port: 8080,
dir: './pacts',
})
describe('User API Contract', () => {
beforeAll(() => provider.setup())
afterAll(() => provider.finalize())
it('should fetch user by ID', async () => {
await provider.addInteraction({
state: 'User 123 exists',
uponReceiving: 'a request for user 123',
withRequest: {
method: 'GET',
path: '/users/123',
headers: { Accept: 'application/json' },
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: '123',
name: like('John Doe'),
email: like('john@example.com'),
createdAt: like('2026-01-01T00:00:00Z'),
},
},
})
const user = await fetchUser('123')
expect(user.id).toBe('123')
expect(user.email).toBeTruthy()
})
})
受 REST-assured 启发的测试(Node.js)
import request from 'supertest'
import app from '../src/app'
describe('Users API Integration Tests', () => {
let authToken: string
let createdUserId: string
beforeAll(async () => {
const resp = await request(app)
.post('/auth/login')
.send({ email: 'admin@test.com', password: 'admin123' })
authToken = resp.body.access_token
})
it('creates a user', async () => {
const resp = await request(app)
.post('/users')
.set('Authorization', `Bearer ${authToken}`)
.send({ name: 'Test User', email: 'newuser@test.com' })
.expect(201)
.expect('Content-Type', /json/)
expect(resp.body.id).toBeDefined()
expect(resp.body.email).toBe('newuser@test.com')
createdUserId = resp.body.id
})
it('returns 404 for missing user', async () => {
await request(app)
.get('/users/nonexistent-id')
.set('Authorization', `Bearer ${authToken}`)
.expect(404)
.expect(resp => expect(resp.body.error).toBe('User not found'))
})
})
API 测试金字塔
| 层级 |
工具 |
速度 |
覆盖范围 |
| 单元测试 |
Jest/Vitest |
快 |
逻辑 |
| 集成测试 |
Supertest |
中 |
端点 |
| 契约测试 |
Pact |
中 |
契约 |
| 端到端测试 |
Playwright |
慢 |
流程 |
| 负载测试 |
k6 |
可变 |
性能 |