第4章 - 测试中的网络调试
嗨,测试工程师朋友们!
作为测试工程师,你需要测试各种网络接口、分析网络请求、模拟各种网络环境……掌握网络调试技能可以让你更高效地发现和定位问题。
这一章,我会带你学习测试工作中最实用的网络调试技能和工具,让你成为网络测试专家!
🛠️ 接口测试工具
Postman 使用
基础请求
// GET 请求示例
GET https://api.example.com/users
Headers:
Authorization: Bearer {{access_token}}
Content-Type: application/json
// 使用环境变量
{{base_url}}/users/{{user_id}}
测试脚本
// Pre-request Script (请求前执行)
// 生成随机数据
pm.globals.set("random_email", `test${Math.random()}@example.com`);
// 获取当前时间戳
pm.globals.set("timestamp", Date.now());
// 从环境变量获取 token
const token = pm.environment.get("access_token");
console.log("Token:", token);
// Tests (响应后执行)
// 测试状态码
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
// 测试响应时间
pm.test("Response time is less than 500ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
// 测试响应体
pm.test("Response has success field", function () {
const jsonData = pm.response.json();
pm.expect(jsonData).to.have.property('success');
pm.expect(jsonData.success).to.be.true;
});
// 测试响应头
pm.test("Content-Type is JSON", function () {
pm.response.to.have.header("Content-Type");
pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});
// 保存响应数据到环境变量
const responseData = pm.response.json();
if (responseData.data && responseData.data.id) {
pm.environment.set("user_id", responseData.data.id);
console.log("Saved user_id:", responseData.data.id);
}
// 验证数组
pm.test("Response has users array", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.data).to.be.an('array');
pm.expect(jsonData.data.length).to.be.above(0);
});
// 验证对象结构
pm.test("User has required fields", function () {
const user = pm.response.json().data[0];
pm.expect(user).to.have.property('id');
pm.expect(user).to.have.property('name');
pm.expect(user).to.have.property('email');
});
自动化测试集合
// Collection Runner 变量
// 创建 data.json 文件
[
{
"username": "user1",
"email": "user1@example.com",
"password": "password123"
},
{
"username": "user2",
"email": "user2@example.com",
"password": "password456"
}
]
// 在请求中使用
POST {{base_url}}/register
Body (JSON):
{
"username": "{{username}}",
"email": "{{email}}",
"password": "{{password}}"
}
cURL 命令行测试
# GET 请求
curl -X GET "https://api.example.com/users"
# 带 Header 的请求
curl -X GET "https://api.example.com/users" \
-H "Authorization: Bearer TOKEN123" \
-H "Content-Type: application/json"
# POST 请求提交 JSON
curl -X POST "https://api.example.com/users" \
-H "Content-Type: application/json" \
-d '{"name":"张三","email":"zhangsan@example.com"}'
# POST 请求提交表单
curl -X POST "https://api.example.com/login" \
-d "username=admin" \
-d "password=123456"
# 上传文件
curl -X POST "https://api.example.com/upload" \
-F "file=@/path/to/file.jpg" \
-F "user_id=123"
# 保存响应到文件
curl -o response.json "https://api.example.com/users"
# 显示详细信息
curl -v "https://api.example.com/users"
# 只显示响应头
curl -I "https://api.example.com/users"
# 跟随重定向
curl -L "https://example.com"
# 设置超时
curl --connect-timeout 5 --max-time 10 "https://api.example.com/users"
# 使用代理
curl -x http://proxy.example.com:8080 "https://api.example.com/users"
# 忽略 SSL 证书验证
curl -k "https://api.example.com/users"
🔍 抓包分析
Chrome DevTools
// 使用 Chrome DevTools Network 面板
// 1. 打开 DevTools (F12)
// 2. 切换到 Network 标签
// 3. 刷新页面或触发请求
// 查看请求详情:
// - Headers: 请求头和响应头
// - Preview: 格式化的响应数据
// - Response: 原始响应数据
// - Timing: 请求各阶段耗时
// 过滤请求:
// - XHR: 只显示 Ajax 请求
// - JS: 只显示 JavaScript 文件
// - CSS: 只显示样式文件
// - Img: 只显示图片
// 性能分析:
// - DOMContentLoaded: DOM 加载完成时间(蓝线)
// - Load: 页面完全加载时间(红线)
// - 瀑布图: 查看资源加载顺序和耗时
Wireshark 抓包
基础使用
# 启动 Wireshark
# 选择网络接口(如 eth0 或 Wi-Fi)
# 点击开始捕获
# 常用过滤器
http # 只显示 HTTP 流量
http.request.method == "POST" # 只显示 POST 请求
http.response.code == 404 # 只显示 404 响应
tcp.port == 80 # 只显示 80 端口的 TCP 流量
tcp.port == 443 # HTTPS 流量
ip.addr == 192.168.1.100 # 指定 IP 地址
ip.src == 192.168.1.100 # 源 IP
ip.dst == 192.168.1.100 # 目标 IP
# 组合过滤
http && ip.addr == 192.168.1.100
tcp.port == 80 || tcp.port == 443
# 排除过滤
!arp # 排除 ARP 流量
!(tcp.port == 22) # 排除 SSH 流量
分析 TCP 三次握手
# 过滤器: tcp.flags.syn == 1
# 观察三次握手过程:
# 1. SYN: 客户端 -> 服务器 (SYN=1, ACK=0)
# 2. SYN-ACK: 服务器 -> 客户端 (SYN=1, ACK=1)
# 3. ACK: 客户端 -> 服务器 (SYN=0, ACK=1)
# 右键数据包 -> Follow -> TCP Stream
# 可以看到完整的 TCP 会话内容
分析 HTTP 请求
# 过滤器: http.request
# 查看 HTTP 请求详情:
# - 请求方法 (GET/POST/PUT/DELETE)
# - 请求路径
# - 请求头 (User-Agent, Cookie, Authorization)
# - 请求体 (POST 数据)
# 过滤器: http.response
# 查看 HTTP 响应详情:
# - 状态码 (200, 404, 500)
# - 响应头 (Content-Type, Set-Cookie)
# - 响应体 (HTML, JSON)
Charles 代理
基础配置
# 1. 启动 Charles
# 2. Proxy -> Proxy Settings
# - 端口设置: 8888
# - 启用 "Enable transparent HTTP proxying"
# 3. 配置浏览器代理
# - HTTP Proxy: 127.0.0.1:8888
# - HTTPS Proxy: 127.0.0.1:8888
# 4. 安装 SSL 证书
# - Help -> SSL Proxying -> Install Charles Root Certificate
# - 信任该证书
# 5. 启用 SSL 代理
# - Proxy -> SSL Proxying Settings
# - 添加: *.example.com:443
修改请求/响应
// Breakpoints (断点)
// 右键请求 -> Breakpoints
// 可以修改请求头、请求体、响应头、响应体
// Map Local (映射本地文件)
// Tools -> Map Local
// 将远程资源映射到本地文件,测试修改后的代码
// Map Remote (映射远程地址)
// Tools -> Map Remote
// 将一个地址的请求重定向到另一个地址
// Rewrite (重写)
// Tools -> Rewrite
// 添加/修改/删除 请求头、响应头、请求参数
// Throttling (限速)
// Proxy -> Throttle Settings
// 模拟慢速网络环境
⚡ 性能测试
Apache Bench (ab)
# 基础压测
ab -n 1000 -c 10 http://example.com/
# -n: 总请求数
# -c: 并发数
# POST 请求
ab -n 1000 -c 10 -p data.json -T application/json http://example.com/api
# 带认证
ab -n 1000 -c 10 -H "Authorization: Bearer TOKEN" http://example.com/api
# 结果分析
# Time taken for tests: 总耗时
# Requests per second: 每秒请求数 (QPS)
# Time per request: 平均响应时间
# Transfer rate: 传输速率
# Percentage of requests: 响应时间百分位
JMeter 性能测试
基础测试计划
<!-- 测试计划结构 -->
Test Plan
├── Thread Group (线程组)
│ ├── Number of Threads: 100 (虚拟用户数)
│ ├── Ramp-Up Period: 10 (启动时间,秒)
│ ├── Loop Count: 10 (循环次数)
│ │
│ ├── HTTP Request (HTTP 请求)
│ │ ├── Server: api.example.com
│ │ ├── Port: 443
│ │ ├── Protocol: https
│ │ ├── Method: GET
│ │ ├── Path: /users
│ │
│ ├── HTTP Header Manager (请求头)
│ │ ├── Authorization: Bearer ${token}
│ │ ├── Content-Type: application/json
│ │
│ └── Assertions (断言)
│ ├── Response Code: 200
│ ├── Response Time: < 1000ms
│
├── Listeners (监听器)
│ ├── View Results Tree (查看结果树)
│ ├── Summary Report (聚合报告)
│ ├── Graph Results (图形结果)
│ └── Response Time Graph (响应时间图)
使用变量和参数化
# users.csv (CSV 数据文件)
username,password,email
user1,pass123,user1@example.com
user2,pass456,user2@example.com
user3,pass789,user3@example.com
# 添加 CSV Data Set Config
# Filename: users.csv
# Variable Names: username,password,email
# 在请求中使用变量
# ${username}
# ${password}
# ${email}
🌐 Mock 测试
Mock Server (使用 json-server)
# 安装 json-server
npm install -g json-server
# 创建 db.json
cat > db.json << EOF
{
"users": [
{ "id": 1, "name": "张三", "email": "zhangsan@example.com" },
{ "id": 2, "name": "李四", "email": "lisi@example.com" }
],
"posts": [
{ "id": 1, "title": "文章1", "userId": 1 },
{ "id": 2, "title": "文章2", "userId": 2 }
]
}
EOF
# 启动 Mock 服务器
json-server --watch db.json --port 3000
# 现在可以访问:
# GET /users # 获取所有用户
# GET /users/1 # 获取单个用户
# POST /users # 创建用户
# PUT /users/1 # 更新用户
# DELETE /users/1 # 删除用户
# GET /users?name=张三 # 过滤查询
使用 Postman Mock Server
// 1. 在 Postman 中创建 Collection
// 2. 点击 "Mock Servers" -> "Create Mock Server"
// 3. 为每个请求添加 Example
// Example 响应示例:
{
"success": true,
"data": [
{
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
}
]
}
// 使用 Mock Server URL
// https://xxxxx.mock.pstmn.io/users
🔧 网络异常测试
弱网模拟
Chrome DevTools 网络限速
// 1. 打开 DevTools (F12)
// 2. 切换到 Network 标签
// 3. 点击 "No throttling" 下拉菜单
// 预设选项:
// - Fast 3G: 1.5Mbps 下载, 750Kbps 上传, 100ms 延迟
// - Slow 3G: 500Kbps 下载, 500Kbps 上传, 400ms 延迟
// - Offline: 模拟离线
// 自定义网络条件:
// - Download: 下载速度
// - Upload: 上传速度
// - Latency: 延迟时间
Linux 网络限速 (tc 命令)
# 添加延迟 (100ms)
sudo tc qdisc add dev eth0 root netem delay 100ms
# 添加延迟波动
sudo tc qdisc add dev eth0 root netem delay 100ms 20ms
# 添加丢包率 (10%)
sudo tc qdisc add dev eth0 root netem loss 10%
# 限制带宽 (1Mbps)
sudo tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 400ms
# 组合条件
sudo tc qdisc add dev eth0 root netem delay 100ms loss 5% rate 1mbit
# 查看当前设置
sudo tc qdisc show dev eth0
# 删除限制
sudo tc qdisc del dev eth0 root
超时测试
// 测试连接超时
fetch('https://api.example.com/users', {
signal: AbortSignal.timeout(5000) // 5秒超时
})
.then(response => response.json())
.catch(error => {
if (error.name === 'TimeoutError') {
console.log('请求超时');
}
});
// 手动实现超时
const timeout = (ms) => new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
Promise.race([
fetch('https://api.example.com/users'),
timeout(5000)
])
.then(response => response.json())
.catch(error => console.error('请求失败:', error));
错误场景测试
# 测试 DNS 解析失败
curl http://nonexistent-domain-12345.com
# 测试连接被拒绝
curl http://localhost:9999
# 测试 SSL 证书错误
curl https://self-signed.badssl.com/
# 测试各种 HTTP 状态码
curl https://httpstat.us/200 # 成功
curl https://httpstat.us/400 # 错误请求
curl https://httpstat.us/401 # 未授权
curl https://httpstat.us/404 # 未找到
curl https://httpstat.us/500 # 服务器错误
curl https://httpstat.us/503 # 服务不可用
# 测试超时
curl --max-time 1 https://httpstat.us/200?sleep=5000
📊 测试报告
性能测试报告模板
# 性能测试报告
## 测试环境
- 测试时间: 2024-01-01 10:00:00
- 测试工具: JMeter 5.5
- 服务器: 4核8G, Ubuntu 22.04
- 数据库: MySQL 8.0
## 测试场景
### 场景1: 用户列表查询
- 接口: GET /api/users
- 并发数: 100
- 持续时间: 5分钟
- 目标: QPS > 1000, 响应时间 < 200ms
## 测试结果
| 指标 | 值 | 目标 | 是否达标 |
|------|-----|------|----------|
| 总请求数 | 50000 | - | - |
| 成功率 | 99.8% | > 99% | ✅ |
| QPS | 1167 | > 1000 | ✅ |
| 平均响应时间 | 85ms | < 200ms | ✅ |
| 95%响应时间 | 156ms | < 300ms | ✅ |
| 99%响应时间 | 234ms | < 500ms | ✅ |
| 最大响应时间 | 567ms | - | - |
## 性能瓶颈
1. 数据库查询较慢 (平均 50ms)
2. 网络延迟 (平均 15ms)
## 优化建议
1. 添加数据库索引
2. 启用查询缓存
3. 使用 CDN 加速静态资源
自动化测试脚本
// test-api.js
const axios = require('axios');
const BASE_URL = 'https://api.example.com';
const results = [];
// 测试用例
const testCases = [
{
name: '获取用户列表',
method: 'GET',
url: '/users',
expectedStatus: 200,
validateResponse: (data) => Array.isArray(data.data)
},
{
name: '创建用户',
method: 'POST',
url: '/users',
data: { name: '测试用户', email: 'test@example.com' },
expectedStatus: 201,
validateResponse: (data) => data.success === true
},
{
name: '获取不存在的用户',
method: 'GET',
url: '/users/99999',
expectedStatus: 404
}
];
// 运行测试
async function runTests() {
console.log('开始测试...\n');
for (const testCase of testCases) {
try {
const startTime = Date.now();
const response = await axios({
method: testCase.method,
url: BASE_URL + testCase.url,
data: testCase.data
});
const duration = Date.now() - startTime;
// 验证状态码
const statusMatch = response.status === testCase.expectedStatus;
// 验证响应内容
let responseValid = true;
if (testCase.validateResponse) {
responseValid = testCase.validateResponse(response.data);
}
const passed = statusMatch && responseValid;
results.push({
name: testCase.name,
passed,
duration,
status: response.status,
message: passed ? '✅ 通过' : '❌ 失败'
});
console.log(`${testCase.name}: ${results[results.length - 1].message} (${duration}ms)`);
} catch (error) {
const expectedError = error.response?.status === testCase.expectedStatus;
results.push({
name: testCase.name,
passed: expectedError,
status: error.response?.status || 'Error',
message: expectedError ? '✅ 通过' : '❌ 失败',
error: error.message
});
console.log(`${testCase.name}: ${results[results.length - 1].message}`);
}
}
// 输出汇总
console.log('\n=== 测试汇总 ===');
const passed = results.filter(r => r.passed).length;
const total = results.length;
console.log(`通过: ${passed}/${total}`);
console.log(`成功率: ${(passed / total * 100).toFixed(2)}%`);
}
runTests();
💡 最佳实践
1. 测试用例设计
// 边界值测试
const testCases = [
{ input: -1, expected: 'error' }, // 负数
{ input: 0, expected: 'error' }, // 零
{ input: 1, expected: 'success' }, // 最小正数
{ input: 100, expected: 'success' }, // 正常值
{ input: 999, expected: 'success' }, // 最大值
{ input: 1000, expected: 'error' } // 超过最大值
];
// 异常情况测试
const errorTestCases = [
{ desc: '缺少必填参数', data: {} },
{ desc: '参数类型错误', data: { id: 'abc' } },
{ desc: '参数格式错误', data: { email: 'invalid' } },
{ desc: '请求体过大', data: 'x'.repeat(10000000) }
];
2. 测试环境隔离
// 配置不同环境
const environments = {
dev: {
baseURL: 'http://localhost:3000',
timeout: 10000
},
test: {
baseURL: 'https://test-api.example.com',
timeout: 5000
},
prod: {
baseURL: 'https://api.example.com',
timeout: 3000
}
};
const env = process.env.NODE_ENV || 'dev';
const config = environments[env];
const api = axios.create(config);
3. 持续集成
# .github/workflows/api-test.yml
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Upload test results
if: always()
uses: actions/upload-artifact@v2
with:
name: test-results
path: test-results/
📝 小结
这一章我们学习了:
✅ 接口测试工具:Postman、cURL、自动化脚本
✅ 抓包分析:Chrome DevTools、Wireshark、Charles
✅ 性能测试:ab、JMeter、压力测试
✅ Mock 测试:Mock Server、数据模拟
✅ 异常测试:弱网模拟、超时测试、错误场景
✅ 测试报告:结果分析、自动化测试
🎯 下一步
最后一章,学习如何排查和解决常见的网络问题!
