关于 api-tester
api-tester 是面向 API 开发与测试的技能,涵盖端点验证、测试用例生成、性能和安全检查,并提供多种语言的请求示例。
主要功能
- 01验证 HTTP 方法与响应
- 02生成 curl、Postman 和自动化测试
- 03执行负载与响应时间测试
- 04检查认证、输入和常见安全问题
- 05覆盖 REST 与 GraphQL API
适用场景
- 01上线前验证端点和响应结构
- 02生成可复用的 API 测试用例
- 03检查速率限制与超时处理
- 04排查认证及输入验证问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ curiouslearner/ devkit --skill api-tester安装后在 Claude Code 中输入/api-tester确认已加载。
详细介绍
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"
# With authentication
curl -X GET https://api.example.com/api/users \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"
# With query parameters
curl -X GET "https://api.example.com/api/users?page=1&limit=10&sort=created_at" \
-H "Authorization: Bearer YOUR_TOKEN"
# Verbose output (includes headers)
curl -v -X GET https://api.example.com/api/users
JavaScript/Node.js
// Using 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 error! status: ${response.status}`);
}
const data = await response.json();
return data;
}
// Using 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:', error.response?.data || error.message);
throw error;
}
}
Python
import requests
# Basic GET request
response = requests.get('https://api.example.com/api/users')
print(response.json())
# With authentication and parameters
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"Error: {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": "[email protected]",
"role": "user"
}'
# From file
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
// Using 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;
}
// Usage
const newUser = {
name: 'John Doe',
email: '[email protected]',
role: 'user'
};
createUser(newUser)
.then(user => console.log('Created:', user))
.catch(error => console.error('Error:', error));
// Using axios with error handling
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) {
// Server responded with error
console.error('Error:', error.response.status);
console.error('Message:', error.response.data);
} else if (error.request) {
// No response received
console.error('No response from server');
} else {
console.error('Error:', error.message);
}
throw error;
}
}
Python
import requests
# Create user
user_data = {
'name': 'John Doe',
'email': '[email protected]',
'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('User created:', response.json())
else:
print(f'Error: {response.status_code}')
print(response.json())
PUT/PATCH 请求示例
# PUT - Replace entire resource
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": "[email protected]",
"role": "admin"
}'
# PATCH - Partial update
curl -X PATCH https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "admin"
}'
DELETE 请求示例
# Delete resource
curl -X DELETE https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN"
# Delete with confirmation
curl -X DELETE https://api.example.com/api/users/123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Confirm-Delete: true"
身份验证示例
Bearer Token(JWT)
# Get token
curl -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "password123"
}'
# Use token
curl -X GET https://api.example.com/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
API 密钥
# In header
curl -X GET https://api.example.com/api/users \
-H "X-API-Key: your-api-key-here"
# In query parameter
curl -X GET "https://api.example.com/api/users?api_key=your-api-key-here"
Basic Auth
# Username and password
curl -X GET https://api.example.com/api/users \
-u username:password
# Base64 encoded
curl -X GET https://api.example.com/api/users \
-H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ="
OAuth 2.0
// Get access token
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;
}
// Use 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 } }"
}'
# With variables
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 变更操作
// Create user mutation
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('User API Tests', () => {
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('should return list of users', 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('should return user by 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('should return 404 for non-existent user', async () => {
try {
await api.get('/api/users/999999');
} catch (error) {
expect(error.response.status).toBe(404);
}
});
});
describe('POST /api/users', () => {
test('should create new user', async () => {
const newUser = {
name: 'Test User',
email: '[email protected]'
};
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('should validate required fields', async () => {
const invalidUser = { name: 'Test' }; // missing email
try {
await api.post('/api/users', invalidUser);
} catch (error) {
expect(error.response.status).toBe(400);
expect(error.response.data).toHaveProperty('error');
}
});
test('should prevent duplicate emails', async () => {
const user = {
name: 'Duplicate',
email: '[email protected]'
};
try {
await api.post('/api/users', user);
} catch (error) {
expect(error.response.status).toBe(409);
}
});
});
describe('Authentication', () => {
test('should reject requests without token', async () => {
const noAuthAPI = axios.create({ baseURL: API_URL });
try {
await noAuthAPI.get('/api/users');
} catch (error) {
expect(error.response.status).toBe(401);
}
});
test('should reject invalid token', 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': '[email protected]'
}
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 Test Collection",
"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": "Users",
"item": [
{
"name": "Get All Users",
"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('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test('Response is array', function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData).to.be.an('array');",
"});"
]
}
}
]
},
{
"name": "Create User",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"{{$randomFullName}}\",\n \"email\": \"{{$randomEmail}}\",\n \"role\": \"user\"\n}"
},
"url": {
"raw": "{{base_url}}/api/users",
"host": ["{{base_url}}"],
"path": ["api", "users"]
}
},
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 201', function () {",
" pm.response.to.have.status(201);",
"});",
"",
"pm.test('User has 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 requests, 10 concurrent
ab -n 1000 -c 10 -H "Authorization: Bearer TOKEN" \
https://api.example.com/api/users
# POST request with 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: Warm up
- duration: 300
arrivalRate: 50
name: Sustained load
defaults:
headers:
Authorization: 'Bearer YOUR_TOKEN'
scenarios:
- name: "Get users"
flow:
- get:
url: "/api/users"
expect:
- statusCode: 200
- think: 1
- post:
url: "/api/users"
json:
name: "Test User"
email: "[email protected]"
expect:
- statusCode: 201
# Run load test
artillery run artillery.yml
# Generate HTML report
artillery run artillery.yml --output report.json
artillery report report.json --output report.html
响应验证
架构验证
const Ajv = require('ajv');
const ajv = new Ajv();
// Define schema
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);
// Validate response
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('Validation errors:', validate.errors);
} else {
console.log('Response is valid!');
}
}
最佳实践
请求最佳实践
- 始终设置适当的
Content-Type标头 - 使用正确的 HTTP 方法(读取使用 GET,创建使用 POST,等等)
- 安全地包含身份验证令牌
- 处理超时和重试
- 发送前验证输入
- 在生产环境中使用 HTTPS API
响应处理
- 解析前检查状态码
- 妥善处理错误
- 验证响应架构
- 记录请求和响应以便调试
- 为重试实现指数退避
安全测试
- 使用无效令牌进行测试
- 测试未进行身份验证的情况
- 尝试在参数中进行 SQL 注入
- 测试输入字段中的 XSS
- 验证 CORS 设置
- 测试速率限制
要测试的错误场景
- 身份验证无效
- 缺少必填字段
- 数据类型无效
- 资源重复
- 未找到(404)
- 服务器错误(500)
- 超出速率限制(429)
- 网络超时
常见 HTTP 状态码
200 OK - Request successful
201 Created - Resource created
204 No Content - Success, no response body
400 Bad Request - Invalid request
401 Unauthorized - Missing/invalid authentication
403 Forbidden - Not allowed to access
404 Not Found - Resource doesn't exist
409 Conflict - Resource already exists
422 Unprocessable Entity - Validation failed
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Server error
502 Bad Gateway - Upstream server error
503 Service Unavailable - Server overloaded
备注
- 始终先在开发/暂存环境中测试,再部署到生产环境
- 使用环境变量存储 API URL 和令牌
- 记录所有测试用例和预期结果
- 在 CI/CD 流水线中自动化测试
- 监控 API 性能和错误率
- 及时更新 Postman 集合
- 测试边缘情况和错误场景
- 验证成功和失败路径
- 使用恰当的身份验证方法
- 切勿将 API 密钥或令牌提交到版本控制系统
评论
支持 Markdown