在DevOps和微服务时代,接口测试已成为日常开发不可或缺的环节。虽然Postman等GUI工具广为人知,但在自动化场景下,Shell脚本配合curl的组合往往能发挥更大威力。这种方案特别适合以下场景:
我曾用这套方案为多个项目构建测试体系,单台4核虚拟机就能支撑每秒上千次的接口测试,相比专业测试工具节省了80%的资源消耗。下面分享的不仅是工具用法,更是在真实项目中积累的最佳实践。
推荐使用Linux环境(CentOS/Ubuntu均可),确保已安装:
bash复制# 检查curl版本(需7.0以上支持HTTP/2)
curl --version
# 安装jq(JSON处理神器)
sudo apt-get install jq -y # Debian系
sudo yum install jq -y # RHEL系
# 安装xmlstarlet(XML处理工具)
sudo apt-get install xmlstarlet
关键提示:生产环境建议固定工具版本。我曾遇到因jq版本升级导致解析逻辑失效的案例,现在都会在脚本开头显式声明所需版本。
基础GET请求大家都会用,但这些进阶参数才是实战关键:
bash复制# 超时控制(连接/传输)
--connect-timeout 5 --max-time 10
# 自动重试(适合不稳定网络)
--retry 3 --retry-delay 1
# 输出详细日志(调试用)
-v --trace-time
# 保持会话(避免重复握手)
--cookie-jar /tmp/cookies --cookie /tmp/cookies
# 流量控制(压测时保护服务)
--limit-rate 500K
基础GET很简单,但生产环境需要更多考量:
bash复制#!/bin/bash
API_ENDPOINT="https://api.example.com/v1/users"
AUTH_TOKEN="Bearer xxxxxxx"
# 带参数的GET请求
response=$(curl -sS -X GET \
-H "Authorization: $AUTH_TOKEN" \
-H "X-Request-ID: $(uuidgen)" \
-G --data-urlencode "page=1" \
--data-urlencode "page_size=20" \
"$API_ENDPOINT")
# 状态码检查
http_code=$(echo "$response" | grep -oP '(?<=HTTP\/1\.1 )\d+')
[ "$http_code" -ne 200 ] && echo "请求失败: $http_code" && exit 1
# 使用jq解析JSON响应
user_count=$(echo "$response" | jq '.data | length')
echo "获取到$user_count个用户"
避坑指南:URL参数一定要用--data-urlencode编码,我遇到过因未编码空格导致签名错误的惨痛教训。
不同内容类型的POST实现对比:
bash复制# 表单提交
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=test&password=123456" \
"$API_ENDPOINT"
# JSON提交(最常用)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}' \
"$API_ENDPOINT"
# 文件上传
curl -X POST \
-H "Content-Type: multipart/form-data" \
-F "file=@test.pdf" \
"$API_ENDPOINT"
jq的强大超乎想象:
bash复制# 基础字段提取
echo "$response" | jq '.data[0].username'
# 复杂转换(数组→CSV)
echo "$response" | jq -r '.data[] | [.id,.username,.email] | @csv'
# 条件过滤
echo "$response" | jq '.data[] | select(.age > 18)'
# 统计计算
echo "$response" | jq '[.data[].age] | length, add/length'
虽然JSON是主流,但传统系统仍多用XML:
bash复制# 安装xmlstarlet后使用
echo "$xml_response" | xmlstarlet sel -t -v "//user/name"
# 带条件的XPath查询
echo "$xml_response" | xmlstarlet sel -t -v "//user[@id='1001']/email"
bash复制# 状态码断言
assert_http_code() {
local actual=$1
local expected=$2
[ "$actual" -ne "$expected" ] && \
echo "断言失败: 预期$expected,实际$actual" && exit 1
}
# JSON字段断言
assert_json_field() {
local json="$1"
local path="$2"
local expected="$3"
local actual=$(echo "$json" | jq -r "$path")
[ "$actual" != "$expected" ] && \
echo "断言失败: $path 预期$expected,实际$actual" && exit 1
}
bash复制# 简单耗时测试
start_time=$(date +%s.%N)
curl -sS -o /dev/null "$API_ENDPOINT"
end_time=$(date +%s.%N)
echo "请求耗时: $(echo "$end_time - $start_time" | bc)s"
# 使用ab进行压力测试(需安装apache2-utils)
ab -n 1000 -c 50 -H "Authorization: Bearer xxx" "$API_ENDPOINT"
建议目录结构:
code复制/api-test/
├── config.sh # 公共配置
├── lib/
│ ├── http.sh # HTTP基础函数
│ └── assert.sh # 断言库
├── cases/
│ ├── user_api.sh # 测试用例
│ └── order_api.sh
└── run.sh # 入口脚本
bash复制#!/bin/bash
source ../lib/http.sh
source ../lib/assert.sh
test_user_create() {
echo "测试用户创建..."
local response=$(http_post "/users" '{"name":"testuser"}')
assert_http_code "$response" 201
local user_id=$(echo "$response" | jq -r '.id')
assert_not_empty "$user_id"
# 清理测试数据
http_delete "/users/$user_id" > /dev/null
}
签名验证陷阱:当接口需要签名验证时,确保:
Cookie管理技巧:
bash复制# 保持会话的推荐方式
curl -b cookies.txt -c cookies.txt "$API_ENDPOINT"
bash复制# 显示进度条且限速下载
curl -# --limit-rate 1M -o largefile.zip "$DOWNLOAD_URL"
这套方案在多个百万级用户系统中稳定运行,核心优势在于:
最后分享一个真实案例:通过分析接口响应时间分布,我们发现某API在P99延迟突然升高,最终定位到数据库连接池配置问题。这一切都始于一个简单的curl测试脚本。