name: api-tester description: 快速API端点测试,包含全面的请求/响应验证。
API测试器技能
快速API端点测试,包含全面的请求/响应验证。
指令
您是一个API测试专家。当调用时:
-
测试API端点:
- 验证HTTP方法(GET、POST、PUT、PATCH、DELETE)
- 测试请求头和正文格式
- 验证响应状态码
- 验证响应模式和数据类型
- 检查认证和授权
-
生成测试用例:
- 创建用于测试的curl命令
- 生成Postman集合
- 编写自动化测试脚本
- 测试边界情况和错误场景
- 验证API契约
-
性能测试:
- 使用并发请求进行负载测试
- 响应时间基准测试
- 速率限制验证
- 超时处理
- 连接池测试
-
安全测试:
- 认证/授权检查
- 输入验证测试
- SQL注入预防
- XSS预防
- CORS配置
使用示例
@api-tester
@api-tester --endpoint /api/users
@api-tester --method POST
@api-tester --load-test
@api-tester --generate-collection
REST API测试
GET请求示例
基本GET请求
# curl
curl -X GET https://api.example.com/api/users \
-H "Content-Type: application/json"
# 带认证
curl -X GET https://api.example.com/api/users \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"
# 带查询参数
curl -X GET "https://api.example.com/api/users?page=1&limit=10&sort=created_at" \
-H "Authorization: Bearer YOUR_TOKEN"
# 详细输出(包含头信息)
curl -v -X GET https://api.example.com/api/users
JavaScript/Node.js
// 使用fetch
async function getUsers() {
const response = await fetch('https://api.example.com/api/users', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP错误!状态码:${response.status}`);
}
const data = await response.json();
return data;
}
// 使用axios
const axios = require('axios');
async function getUsers() {
try {
const response = await axios.get('https://api.example.com/api/users', {
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
},
params: {
page: 1,
limit: 10
}
});
return response.data;
} catch (error) {
console.error('错误:', error.response?.data || error.message);
throw error;
}
}
Python
import requests
# 基本GET请求
response = requests.get('https://api.example.com/api/users')
print(response.json())
# 带认证和参数
headers = {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
}
params = {
'page': 1,
'limit': 10,
'sort': 'created_at'
}
response = requests.get(
'https://api.example.com/api/users',
headers=headers,
params=params
)
if response.status_code == 200:
data = response.json()
print(data)
else:
print(f"错误:{response.status_code}")
print(response.text)
POST请求示例
创建资源
# curl
curl -X POST https://api.example.com/api/users \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"role": "user"
}'
# 从文件
curl -X POST https://api.example.com/api/users \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d @user.json
JavaScript/Node.js
// 使用fetch
async function createUser(userData) {
const response = await fetch('https://api.example.com/api/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
const data = await response.json();
return data;
}
// 使用
const newUser = {
name: 'John Doe',
email: 'john@example.com',
role: 'user'
};
createUser(newUser)
.then(user => console.log('已创建:', user))
.catch(error => console.error('错误:', error));
// 使用axios带错误处理
async function createUser(userData) {
try {
const response = await axios.post(
'https://api.example.com/api/users',
userData,
{
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
}
}
);
return response.data;
} catch (error) {
if (error.response) {
// 服务器响应错误
console.error('错误:', error.response.status);
console.error('消息:', error.response.data);
} else if (error.request) {
// 未收到响应
console.error('服务器无响应');
} else {
console.error('错误:', error.message);
}
throw error;
}
}
Python
import requests
# 创建用户
user_data = {
'name': 'John Doe',
'email': 'john@example.com',
'role': 'user'
}
headers = {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
}
response = requests.post(
'https://api.example.com/api/users',
json=user_data,
headers=headers
)
if response.status_code == 201:
print('用户已创建:', response.json())
else:
print(f'错误:{response.status_code}')
print(response.json())
PUT/PATCH请求示例
# PUT - 替换整个资源
curl -X PUT https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "John Updated",
"email": "john.updated@example.com",
"role": "admin"
}'
# PATCH - 部分更新
curl -X PATCH https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "admin"
}'
DELETE请求示例
# 删除资源
curl -X DELETE https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN"
# 带确认删除
curl -X DELETE https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Confirm-Delete: true"
认证示例
Bearer令牌(JWT)
# 获取令牌
curl -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password123"
}'
# 使用令牌
curl -X GET https://api.example.com/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
API密钥
# 在头中
curl -X GET https://api.example.com/api/users \
-H "X-API-Key: your-api-key-here"
# 在查询参数中
curl -X GET "https://api.example.com/api/users?api_key=your-api-key-here"
基本认证
# 用户名和密码
curl -X GET https://api.example.com/api/users \
-u username:password
# Base64编码
curl -X GET https://api.example.com/api/users \
-H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ="
OAuth 2.0
// 获取访问令牌
async function getAccessToken() {
const response = await fetch('https://oauth.example.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET'
})
});
const data = await response.json();
return data.access_token;
}
// 使用访问令牌
async function callAPI() {
const token = await getAccessToken();
const response = await fetch('https://api.example.com/api/users', {
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json();
}
GraphQL测试
基本查询
# curl
curl -X POST https://api.example.com/graphql \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "{ users { id name email } }"
}'
# 带变量
curl -X POST https://api.example.com/graphql \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query GetUser($id: ID!) { user(id: $id) { id name email } }",
"variables": { "id": "123" }
}'
GraphQL突变
// 创建用户突变
async function createUser(name, email) {
const query = `
mutation CreateUser($name: String!, $email: String!) {
createUser(input: { name: $name, email: $email }) {
id
name
email
createdAt
}
}
`;
const response = await fetch('https://api.example.com/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
query,
variables: { name, email }
})
});
const data = await response.json();
return data.data.createUser;
}
自动化测试
Jest测试套件
const axios = require('axios');
describe('用户API测试', () => {
const API_URL = 'https://api.example.com';
const token = 'YOUR_TEST_TOKEN';
const api = axios.create({
baseURL: API_URL,
headers: {
'Authorization': `Bearer ${token}`
}
});
describe('GET /api/users', () => {
test('应返回用户列表', async () => {
const response = await api.get('/api/users');
expect(response.status).toBe(200);
expect(Array.isArray(response.data)).toBe(true);
expect(response.data.length).toBeGreaterThan(0);
});
test('应通过ID返回用户', async () => {
const response = await api.get('/api/users/123');
expect(response.status).toBe(200);
expect(response.data).toHaveProperty('id', '123');
expect(response.data).toHaveProperty('name');
expect(response.data).toHaveProperty('email');
});
test('对于不存在的用户应返回404', async () => {
try {
await api.get('/api/users/999999');
} catch (error) {
expect(error.response.status).toBe(404);
}
});
});
describe('POST /api/users', () => {
test('应创建新用户', async () => {
const newUser = {
name: 'Test User',
email: 'test@example.com'
};
const response = await api.post('/api/users', newUser);
expect(response.status).toBe(201);
expect(response.data).toHaveProperty('id');
expect(response.data.name).toBe(newUser.name);
expect(response.data.email).toBe(newUser.email);
});
test('应验证必填字段', async () => {
const invalidUser = { name: 'Test' }; // 缺少email
try {
await api.post('/api/users', invalidUser);
} catch (error) {
expect(error.response.status).toBe(400);
expect(error.response.data).toHaveProperty('error');
}
});
test('应防止重复邮箱', async () => {
const user = {
name: 'Duplicate',
email: 'existing@example.com'
};
try {
await api.post('/api/users', user);
} catch (error) {
expect(error.response.status).toBe(409);
}
});
});
describe('认证', () => {
test('应拒绝无令牌的请求', async () => {
const noAuthAPI = axios.create({ baseURL: API_URL });
try {
await noAuthAPI.get('/api/users');
} catch (error) {
expect(error.response.status).toBe(401);
}
});
test('应拒绝无效令牌', async () => {
const badAuthAPI = axios.create({
baseURL: API_URL,
headers: { 'Authorization': 'Bearer invalid-token' }
});
try {
await badAuthAPI.get('/api/users');
} catch (error) {
expect(error.response.status).toBe(401);
}
});
});
});
Python pytest
import pytest
import requests
API_URL = 'https://api.example.com'
TOKEN = 'YOUR_TEST_TOKEN'
@pytest.fixture
def headers():
return {
'Authorization': f'Bearer {TOKEN}',
'Content-Type': 'application/json'
}
def test_get_users(headers):
response = requests.get(f'{API_URL}/api/users', headers=headers)
assert response.status_code == 200
assert isinstance(response.json(), list)
assert len(response.json()) > 0
def test_get_user_by_id(headers):
response = requests.get(f'{API_URL}/api/users/123', headers=headers)
assert response.status_code == 200
data = response.json()
assert data['id'] == '123'
assert 'name' in data
assert 'email' in data
def test_create_user(headers):
user_data = {
'name': 'Test User',
'email': 'test@example.com'
}
response = requests.post(
f'{API_URL}/api/users',
json=user_data,
headers=headers
)
assert response.status_code == 201
data = response.json()
assert 'id' in data
assert data['name'] == user_data['name']
def test_unauthorized_access():
response = requests.get(f'{API_URL}/api/users')
assert response.status_code == 401
Postman集合
集合结构
{
"info": {
"name": "API测试集合",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{access_token}}",
"type": "string"
}
]
},
"item": [
{
"name": "用户",
"item": [
{
"name": "获取所有用户",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/users?page=1&limit=10",
"host": ["{{base_url}}"],
"path": ["api", "users"],
"query": [
{ "key": "page", "value": "1" },
{ "key": "limit", "value": "10" }
]
}
},
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('状态码为200', function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test('响应为数组', function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData).to.be.an('array');",
"});"
]
}
}
]
},
{
"name": "创建用户",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{
\"name\": \"{{$randomFullName}}\",
\"email\": \"{{$randomEmail}}\",
\"role\": \"user\"
}"
},
"url": {
"raw": "{{base_url}}/api/users",
"host": ["{{base_url}}"],
"path": ["api", "users"]
}
},
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('状态码为201', function () {",
" pm.response.to.have.status(201);",
"});",
"",
"pm.test('用户有ID', function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData).to.have.property('id');",
" pm.environment.set('user_id', jsonData.id);",
"});"
]
}
}
]
}
]
}
],
"variable": [
{
"key": "base_url",
"value": "https://api.example.com"
}
]
}
负载测试
使用Apache Bench
# 1000个请求,10个并发
ab -n 1000 -c 10 -H "Authorization: Bearer TOKEN" \
https://api.example.com/api/users
# POST请求带JSON
ab -n 1000 -c 10 -p data.json -T application/json \
-H "Authorization: Bearer TOKEN" \
https://api.example.com/api/users
使用Artillery
# artillery.yml
config:
target: 'https://api.example.com'
phases:
- duration: 60
arrivalRate: 10
name: 预热
- duration: 300
arrivalRate: 50
name: 持续负载
defaults:
headers:
Authorization: 'Bearer YOUR_TOKEN'
scenarios:
- name: "获取用户"
flow:
- get:
url: "/api/users"
expect:
- statusCode: 200
- think: 1
- post:
url: "/api/users"
json:
name: "Test User"
email: "test@example.com"
expect:
- statusCode: 201
# 运行负载测试
artillery run artillery.yml
# 生成HTML报告
artillery run artillery.yml --output report.json
artillery report report.json --output report.html
响应验证
模式验证
const Ajv = require('ajv');
const ajv = new Ajv();
// 定义模式
const userSchema = {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string', format: 'email' },
role: { type: 'string', enum: ['user', 'admin'] },
createdAt: { type: 'string', format: 'date-time' }
},
required: ['id', 'name', 'email', 'role']
};
const validate = ajv.compile(userSchema);
// 验证响应
async function testUserAPI() {
const response = await fetch('https://api.example.com/api/users/123');
const data = await response.json();
const valid = validate(data);
if (!valid) {
console.error('验证错误:', validate.errors);
} else {
console.log('响应有效!');
}
}
最佳实践
请求最佳实践
- 始终设置适当的
Content-Type头 - 使用正确的HTTP方法(GET用于读取,POST用于创建等)
- 安全地包含认证令牌
- 处理超时和重试
- 发送前验证输入
- 生产API使用HTTPS
响应处理
- 解析前检查状态码
- 优雅地处理错误
- 验证响应模式
- 记录请求和响应以进行调试
- 为重试实现指数退避
安全测试
- 使用无效令牌测试
- 测试无认证情况
- 尝试在参数中进行SQL注入
- 测试输入字段中的XSS
- 验证CORS设置
- 测试速率限制
要测试的错误场景
- 无效认证
- 缺少必填字段
- 无效数据类型
- 重复资源
- 未找到(404)
- 服务器错误(500)
- 速率限制超出(429)
- 网络超时
常见HTTP状态码
200 OK - 请求成功
201 Created - 资源已创建
204 No Content - 成功,无响应体
400 Bad Request - 无效请求
401 Unauthorized - 缺少/无效认证
403 Forbidden - 不允许访问
404 Not Found - 资源不存在
409 Conflict - 资源已存在
422 Unprocessable Entity - 验证失败
429 Too Many Requests - 速率限制超出
500 Internal Server Error - 服务器错误
502 Bad Gateway - 上游服务器错误
503 Service Unavailable - 服务器过载
备注
- 始终在开发/预生产环境中测试
- 使用环境变量存储API URL和令牌
- 记录所有测试用例和预期结果
- 在CI/CD管道中自动化测试
- 监控API性能和错误率
- 保持Postman集合更新
- 测试边界情况和错误场景
- 验证成功和失败路径
- 使用适当的认证方法
- 切勿将API密钥或令牌提交到版本控制