1. Shell脚本与接口测试的完美结合
在自动化测试领域,Shell脚本与curl的组合堪称一把瑞士军刀。我曾在多个项目中用这套组合拳快速搭建轻量级接口测试框架,特别适合需要快速验证接口的场景。不同于重量级的Postman或JMeter,这种方案无需复杂环境配置,直接在Linux终端就能运行,对持续集成(CI)环境也极其友好。
curl作为一款支持多种协议的命令行工具,其强大之处在于能精确控制HTTP请求的每个细节。而Shell脚本则提供了流程控制和结果处理的灵活性。两者结合后,我们可以实现:
- 自动化发送GET/POST等各类HTTP请求
- 动态构造请求参数和请求体
- 解析响应状态码和响应体
- 实现断言验证逻辑
- 生成可读性强的测试报告
2. 基础环境准备
2.1 工具安装检查
大多数Linux发行版已预装curl和bash,但建议先确认版本:
bash复制# 检查curl版本(建议7.0以上)
curl --version | head -n 1
# 检查bash版本(建议4.0以上)
bash --version | head -n 1
若需安装,不同系统的命令如下:
bash复制# Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y curl
# CentOS/RHEL
sudo yum install -y curl
# MacOS(需先安装Homebrew)
brew install curl
2.2 测试接口准备
为方便演示,我们可以使用免费的测试API服务:
bash复制# 示例GET接口
GET_URL="https://httpbin.org/get"
# 示例POST接口
POST_URL="https://httpbin.org/post"
3. GET请求实战
3.1 基础GET请求
最简单的GET请求示例:
bash复制response=$(curl -s -X GET "$GET_URL?name=test&value=1")
echo "$response"
关键参数说明:
-s:静默模式,不显示进度信息-X GET:指定GET方法(可省略,因为GET是默认方法)$GET_URL?name=test&value=1:带查询参数的URL
3.2 带Header的GET请求
实际项目中常需要添加认证头:
bash复制response=$(curl -s -X GET \
-H "Authorization: Bearer token123" \
-H "Content-Type: application/json" \
"$GET_URL")
提示:使用
\实现命令行换行,提高长命令的可读性
3.3 响应处理技巧
获取HTTP状态码和响应体:
bash复制status_code=$(curl -o /dev/null -s -w "%{http_code}\n" "$GET_URL")
response_body=$(curl -s "$GET_URL")
echo "Status: $status_code"
echo "Response: $response_body"
参数说明:
-o /dev/null:将响应体重定向到空设备-w "%{http_code}\n":只输出HTTP状态码
4. POST请求深度解析
4.1 表单POST请求
bash复制response=$(curl -s -X POST \
-d "username=admin" \
-d "password=123456" \
"$POST_URL")
4.2 JSON格式POST请求
bash复制json_data='{"username":"admin","password":"123456"}'
response=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "$json_data" \
"$POST_URL")
4.3 文件上传实现
bash复制# 先创建一个测试文件
echo "test content" > test.txt
response=$(curl -s -X POST \
-F "file=@test.txt" \
"$POST_URL")
5. 响应解析高级技巧
5.1 使用jq解析JSON
安装jq工具:
bash复制sudo apt-get install -y jq # Ubuntu
sudo yum install -y jq # CentOS
brew install jq # MacOS
解析示例:
bash复制response=$(curl -s "$GET_URL")
echo "$response" | jq '.headers.Host'
5.2 正则表达式提取
当没有jq时,可以用grep+正则:
bash复制url=$(echo "$response" | grep -oE '"url": "[^"]+"' | cut -d'"' -f4)
5.3 XML响应处理
对于XML响应,可以安装xmlstarlet:
bash复制sudo apt-get install -y xmlstarlet
解析示例:
bash复制curl -s "http://example.com/api" | xmlstarlet sel -t -v "//title"
6. 实战:构建完整测试脚本
6.1 脚本框架设计
bash复制#!/bin/bash
# 配置部分
API_BASE="https://api.example.com/v1"
AUTH_TOKEN="your_token_here"
TEST_RESULTS="test_results.log"
# 初始化日志文件
> "$TEST_RESULTS"
# 测试用例1:用户登录
test_login() {
local response=$(curl -s -X POST \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123"}' \
"$API_BASE/login")
local status=$(echo "$response" | jq -r '.status')
if [ "$status" == "success" ]; then
echo "PASS: Login test" | tee -a "$TEST_RESULTS"
else
echo "FAIL: Login test" | tee -a "$TEST_RESULTS"
echo "Response: $response" | tee -a "$TEST_RESULTS"
fi
}
# 执行所有测试
test_login
# 输出总结
echo -e "\n=== Test Summary ==="
cat "$TEST_RESULTS"
6.2 断言增强实现
更专业的断言函数:
bash复制assert_equals() {
local expected="$1"
local actual="$2"
local message="$3"
if [ "$expected" == "$actual" ]; then
echo "PASS: $message" | tee -a "$TEST_RESULTS"
return 0
else
echo "FAIL: $message (Expected: $expected, Actual: $actual)" | tee -a "$TEST_RESULTS"
return 1
fi
}
# 使用示例
status_code=$(curl -o /dev/null -s -w "%{http_code}\n" "$GET_URL")
assert_equals "200" "$status_code" "GET请求状态码"
6.3 性能测试集成
添加简单的响应时间测量:
bash复制start_time=$(date +%s%N)
curl -s -o /dev/null "$GET_URL"
end_time=$(date +%s%N)
duration=$(( (end_time - start_time) / 1000000 ))
echo "Request took $duration ms"
7. 高级应用场景
7.1 OAuth2.0认证流程
实现完整的OAuth2.0授权码流程:
bash复制# 获取授权码
auth_code=$(curl -s -X POST \
-u "client_id:client_secret" \
-d "grant_type=authorization_code" \
-d "code=$CODE" \
-d "redirect_uri=$REDIRECT_URI" \
"$OAUTH_SERVER/token" | jq -r '.access_token')
7.2 文件下载与校验
下载文件并验证MD5:
bash复制curl -s -o downloaded_file.zip "$DOWNLOAD_URL"
expected_md5="d41d8cd98f00b204e9800998ecf8427e"
actual_md5=$(md5sum downloaded_file.zip | cut -d' ' -f1)
assert_equals "$expected_md5" "$actual_md5" "文件MD5校验"
7.3 WebSocket连接测试
虽然curl原生不支持WebSocket,但可以通过其他工具组合实现:
bash复制# 需要安装websocat
echo '{"type":"ping"}' | websocat wss://echo.websocket.org
8. 常见问题排查指南
8.1 证书相关问题
跳过证书验证(仅测试环境使用):
bash复制curl -k https://invalid-cert-site.com
指定自定义CA证书:
bash复制curl --cacert /path/to/ca.pem https://site.com
8.2 超时设置
bash复制# 连接超时2秒,传输超时5秒
curl --connect-timeout 2 --max-time 5 "$URL"
8.3 代理设置
bash复制curl -x http://proxy.example.com:8080 "$URL"
8.4 调试技巧
使用-v参数查看详细通信过程:
bash复制curl -v "$URL" > /dev/null
9. 性能优化建议
9.1 连接复用
bash复制# 使用--next选项复用连接
curl -s --next "$URL1" --next "$URL2"
9.2 并行请求
使用GNU parallel实现并行:
bash复制cat url_list.txt | parallel -j 4 curl -s -o /dev/null
9.3 结果缓存
bash复制cache_dir="/tmp/curl_cache"
mkdir -p "$cache_dir"
cached_request() {
local url="$1"
local cache_file="$cache_dir/$(echo "$url" | md5sum | cut -d' ' -f1)"
if [ -f "$cache_file" ]; then
cat "$cache_file"
else
curl -s "$url" | tee "$cache_file"
fi
}
10. 安全最佳实践
10.1 敏感信息处理
不要在脚本中硬编码凭证:
bash复制# 从环境变量读取
curl -u "$API_USER:$API_PASS" "$URL"
10.2 输入验证
bash复制validate_url() {
if [[ ! "$1" =~ ^https?:// ]]; then
echo "Invalid URL: $1" >&2
exit 1
fi
}
10.3 速率限制
bash复制# 限制请求频率
for url in "${urls[@]}"; do
curl -s "$url"
sleep 1 # 每秒1个请求
done
11. 测试报告生成
11.1 JUnit格式报告
bash复制generate_junit_report() {
local passes=$(grep -c "PASS:" "$TEST_RESULTS")
local fails=$(grep -c "FAIL:" "$TEST_RESULTS")
cat <<EOF > report.xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="Curl Tests" tests="$((passes + fails))" failures="$fails">
$(while read -r line; do
if [[ "$line" == PASS:* ]]; then
local name="${line#PASS: }"
echo " <testcase name=\"$name\"/>"
elif [[ "$line" == FAIL:* ]]; then
local name="${line%% (*}"
name="${name#FAIL: }"
local message="${line#* (}"
message="${message%)}"
echo " <testcase name=\"$name\">"
echo " <failure message=\"$message\"/>"
echo " </testcase>"
fi
done < "$TEST_RESULTS")
</testsuite>
EOF
}
11.2 HTML可视化报告
bash复制generate_html_report() {
cat <<EOF > report.html
<!DOCTYPE html>
<html>
<head>
<title>API Test Report</title>
<style>
.pass { color: green; }
.fail { color: red; }
</style>
</head>
<body>
<h1>API Test Report</h1>
<ul>
$(while read -r line; do
if [[ "$line" == PASS:* ]]; then
echo " <li class=\"pass\">✓ ${line#PASS: }</li>"
elif [[ "$line" == FAIL:* ]]; then
echo " <li class=\"fail\">✗ ${line#FAIL: }</li>"
fi
done < "$TEST_RESULTS")
</ul>
</body>
</html>
EOF
}
12. 持续集成集成示例
12.1 GitLab CI配置
yaml复制stages:
- test
api_tests:
stage: test
image: alpine/curl
script:
- apk add --no-cache jq
- chmod +x run_tests.sh
- ./run_tests.sh
artifacts:
when: always
paths:
- test_results.log
- report.xml
- report.html
12.2 Jenkins Pipeline示例
groovy复制pipeline {
agent any
stages {
stage('Test') {
steps {
sh '''
apt-get update -qq && apt-get install -y curl jq
chmod +x run_tests.sh
./run_tests.sh
'''
junit 'report.xml'
archiveArtifacts artifacts: 'report.html', fingerprint: true
}
}
}
}
13. 扩展思路与进阶方向
13.1 与Python/Ruby等语言结合
bash复制# 调用Python处理复杂逻辑
process_data() {
local json="$1"
echo "$json" | python3 -c "import json,sys; data=json.load(sys.stdin); print(data['complex']['field'])"
}
13.2 数据库验证
bash复制# 查询数据库验证结果
verify_in_db() {
local query="SELECT COUNT(*) FROM users WHERE username='$1'"
local count=$(mysql -uuser -ppass dbname -Nse "$query")
assert_equals "1" "$count" "数据库验证"
}
13.3 分布式测试
使用SSH在多台机器上运行测试:
bash复制for host in host1 host2 host3; do
ssh "$host" "curl -s $URL > /dev/null" &
done
wait
14. 真实项目经验分享
在实际电商API测试中,我总结了几个关键点:
-
幂等性处理:对于订单类接口,一定要在测试脚本中添加随机参数,避免重复下单
bash复制order_id="TEST_$(date +%s%N)" curl -X POST -d "order_id=$order_id" "$ORDER_API" -
依赖管理:使用变量集中管理所有API端点
bash复制declare -A API_ENDPOINTS=( ["LOGIN"]="/auth/login" ["ORDER"]="/v2/orders" ) -
环境切换:通过参数切换测试环境
bash复制case "$ENV" in dev) BASE_URL="https://dev.api.example.com" ;; prod) BASE_URL="https://api.example.com" ;; esac -
敏感数据保护:使用
set +x防止敏感信息输出到日志bash复制set +x curl -u "admin:$ADMIN_PASS" "$URL" set -x
15. 调试技巧与工具推荐
15.1 使用httpbin调试
httpbin.org提供了完美的测试端点:
bash复制# 测试各种HTTP方法
curl -X PUT https://httpbin.org/put
curl -X DELETE https://httpbin.org/delete
# 测试重定向
curl -L https://httpbin.org/redirect/2
15.2 网络诊断命令
bash复制# 检查DNS解析
dig +short api.example.com
# 检查网络连通性
tcping api.example.com 443
# 查看详细TLS握手
openssl s_client -connect api.example.com:443 -servername api.example.com
15.3 可视化工具
虽然我们主要使用命令行,但有时可视化工具也很帮助:
- Wireshark:网络包分析
- Charles:HTTP代理/监控
- Postman:复杂场景可视化测试
16. 资源清理与管理
16.1 临时文件清理
bash复制cleanup() {
rm -f tmp_*.json
rm -f *.tmp
}
trap cleanup EXIT
16.2 会话管理
bash复制# 保存cookie
curl -c cookies.txt "$LOGIN_URL" -d "user=test&pass=123"
# 使用cookie
curl -b cookies.txt "$PROTECTED_URL"
16.3 连接池管理
bash复制# 查看当前连接
ss -tnp | grep curl
# 强制关闭连接
killall curl 2>/dev/null
17. 跨平台兼容性
17.1 MacOS差异处理
bash复制# 时间戳获取(MacOS使用gdate)
if [[ "$(uname)" == "Darwin" ]]; then
timestamp() { gdate +%s%N; }
else
timestamp() { date +%s%N; }
fi
17.2 Windows兼容方案
Git Bash中的注意事项:
bash复制# 路径转换
curl --data-binary @$(cygpath -w "$FILE") "$URL"
17.3 Alpine Linux特殊处理
bash复制# Alpine需要安装不同包
if grep -q Alpine /etc/os-release; then
apk add --no-cache curl jq
fi
18. 性能监控与告警
18.1 监控脚本示例
bash复制monitor_api() {
while true; do
start=$(date +%s%N)
status=$(curl -o /dev/null -s -w "%{http_code}" "$HEALTH_CHECK_URL")
end=$(date +%s%N)
latency=$(( (end - start) / 1000000 ))
if [ "$status" -ne 200 ] || [ "$latency" -gt 500 ]; then
send_alert "API异常: 状态码=$status, 延迟=${latency}ms"
fi
sleep 60
done
}
18.2 告警集成
bash复制send_alert() {
# Slack通知
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$1\"}" \
"$SLACK_WEBHOOK"
# 邮件通知
echo "$1" | mail -s "API告警" admin@example.com
}
19. 版本控制与协作
19.1 脚本模块化
将常用功能拆分为单独文件:
bash复制# common.sh
assert_equals() { ... }
# auth.sh
login() { ... }
# 主脚本中引用
source common.sh
source auth.sh
19.2 配置分离
使用单独配置文件:
bash复制# config.ini
API_BASE=https://api.example.com
API_KEY=123456
# 脚本中读取
source config.ini
19.3 参数化脚本
bash复制usage() {
echo "用法: $0 [-e env] [-t testcase]"
exit 1
}
while getopts "e:t:" opt; do
case "$opt" in
e) ENV="$OPTARG" ;;
t) TEST_CASE="$OPTARG" ;;
*) usage ;;
esac
done
20. 终极实战案例
20.1 电商API测试套件
bash复制#!/bin/bash
# 初始化
source common.sh
source config.ini
# 测试用户注册
test_register() {
local username="user_$(date +%s)"
local email="${username}@test.com"
local response=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"username\":\"$username\",\"email\":\"$email\",\"password\":\"Test@123\"}" \
"$API_BASE/auth/register")
assert_equals "success" "$(jq -r '.status' <<< "$response")" "用户注册"
}
# 测试商品搜索
test_product_search() {
local query="phone"
local response=$(curl -s -X GET \
"$API_BASE/products/search?q=$query")
local count=$(jq -r '.results | length' <<< "$response")
assert_greater "$count" "0" "商品搜索结果"
}
# 主流程
test_register
test_product_search
# 生成报告
generate_html_report
20.2 微服务健康检查
bash复制check_service() {
local name="$1"
local endpoint="$2"
local status=$(curl -s -o /dev/null -w "%{http_code}" "$endpoint/health")
if [ "$status" -eq 200 ]; then
echo "✅ $name 正常"
else
echo "❌ $name 异常 (状态码: $status)"
fi
}
# 检查所有服务
check_service "用户服务" "http://user-service"
check_service "订单服务" "http://order-service"
check_service "支付服务" "http://payment-service"
20.3 自动化监控看板
bash复制generate_dashboard() {
local html="<!DOCTYPE html>
<html>
<head>
<title>服务监控看板</title>
<style>
body { font-family: Arial; }
.service { margin: 10px; padding: 10px; border: 1px solid #ddd; }
.up { background: #dfd; }
.down { background: #fdd; }
</style>
</head>
<body>
<h1>服务状态 - $(date)</h1>"
while read -r line; do
if [[ "$line" == *正常* ]]; then
html+="<div class=\"service up\">$line</div>"
else
html+="<div class=\"service down\">$line</div>"
fi
done < <(check_all_services)
html+="</body></html>"
echo "$html" > dashboard.html
}
通过这个完整的Shell+curl接口测试方案,我们实现了从简单请求到复杂测试场景的全覆盖。在实际项目中,这套方案帮我节省了数百小时的测试时间,特别是在CI/CD流水线中,它的轻量级特性展现出了巨大优势。