API 测试(API Testing)
一句话总结 :测试应用程序接口的功能、性能、安全性和可靠性。
🌟 快速理解(小白入门) 用生活化类比 就像测试餐厅的点餐系统 :
前端(UI) = 顾客看到的菜单和服务员
API = 厨房和前台之间的对讲机
后端 = 厨房
API 测试就是测试对讲机 :
1 2 3 4 5 6 7 8 顾客点餐:"一份宫保鸡丁" ↓ 服务员通过对讲机告诉厨房:"一份宫保鸡丁" ↓ 厨房收到:"一份宫保鸡丁" ✅ 厨房做好后通过对讲机回复:"宫保鸡丁做好了" ↓ 服务员收到:"宫保鸡丁做好了" ✅
API 测试要验证 :
✅ 对讲机能否正常通话(功能测试)
✅ 对讲机响应速度(性能测试)
✅ 对讲机是否加密(安全测试)
✅ 对讲机能否同时处理多个订单(并发测试)
真实场景 电商系统 API 测试 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 用户操作:点击"加入购物车"按钮 ↓ 前端发送 API 请求: POST /api/cart/add { "product_id": 123, "quantity": 1 } ↓ 后端处理并返回: { "success": true, "cart_count": 5, "message": "商品已加入购物车" } ↓ 前端显示:"购物车(5)"
API 测试要验证 :
✅ 请求格式正确
✅ 响应格式正确
✅ 数据库更新正确
✅ 响应时间 < 500ms
✅ 错误处理正确
📌 核心概念 什么是 API? API(Application Programming Interface) :应用程序接口,是不同软件系统之间通信的桥梁。
通俗解释 :
前端 :用户看到的界面(网页、App)
后端 :服务器、数据库
API :前端和后端之间的”翻译官”
什么是 API 测试? API 测试 :测试 API 的功能、性能、安全性和可靠性。
关键特征
特征
说明
示例
黑盒测试
不关心内部实现
只测试输入输出
独立于 UI
不需要界面
直接测试接口
快速
比 UI 测试快 10 倍
毫秒级响应
易于自动化
适合 CI/CD
每次提交自动测试
早期测试
前端开发前就能测试
并行开发
🎯 为什么需要 API 测试? 真实案例 案例 1:Cloudflare 宕机事件 时间 :2020年7月17日
问题 :API 配置错误导致全球网站宕机。
原因 :
API 测试不充分
配置变更未经测试
错误的路由规则
影响 :全球数百万网站无法访问,持续27分钟
教训 :API 测试必须包含配置测试。
时间 :2023年7月
问题 :API 限流导致第三方应用无法使用。
原因 :
API 限流策略变更
未充分测试限流逻辑
未通知第三方开发者
影响 :数千个第三方应用停止工作
教训 :API 变更必须充分测试并提前通知。
行业数据
研究机构
数据
说明
SmartBear
82% 的公司使用 API
API 是现代软件的核心
Postman
API 调用量每年增长 143%
API 使用量快速增长
Google
API 测试比 UI 测试快 10 倍
API 测试更高效
✅ API 测试的优势 优势 1:快速反馈 ⚡ 通俗解释 :
就像直接问厨师菜做好了没有,而不是等服务员端上来。
专业说明 :
UI 测试:需要启动浏览器、加载页面、点击按钮(慢)
API 测试:直接发送 HTTP 请求(快)
代码示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import requestsimport timedef test_ui_add_to_cart (): driver = webdriver.Chrome() driver.get("https://example.com" ) driver.find_element(By.ID, "login" ).click() def test_api_add_to_cart (): response = requests.post("https://api.example.com/cart/add" , json={ "product_id" : 123 , "quantity" : 1 }, headers={"Authorization" : "Bearer token" }) assert response.status_code == 200
优势 2:早期测试 🔍 通俗解释 :
就像在装修前先测试水电,而不是等装修完再测试。
专业说明 :
前端开发前就能测试 API
前后端并行开发
早期发现问题,修复成本低
代码示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def test_user_registration_api (): """测试用户注册 API""" response = requests.post("https://api.example.com/register" , json={ "username" : "testuser" , "email" : "test@example.com" , "password" : "password123" }) assert response.status_code == 201 assert "user_id" in response.json() assert "token" in response.json() user = db.query(f"SELECT * FROM users WHERE email = 'test@example.com'" ) assert user is not None assert user.username == "testuser"
优势 3:易于自动化 🤖 通俗解释 :
就像设置自动回复,而不是每次都手动回复。
专业说明 :
API 测试容易集成到 CI/CD
每次代码提交自动测试
快速发现回归问题
代码示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 name: API Tests on: [push , pull_request ]jobs: api-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.9 - name: Install dependencies run: pip install -r requirements.txt - name: Run API tests run: pytest tests/api/ -v - name: Generate coverage report run: pytest tests/api/ --cov=api --cov-report=html - name: Upload coverage uses: codecov/codecov-action@v2
🔄 API 测试的类型 1. 功能测试 定义 :验证 API 的功能是否正确。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def test_get_product (): """测试获取商品详情""" response = requests.get("https://api.example.com/products/123" ) assert response.status_code == 200 assert response.json()["id" ] == 123 assert response.json()["name" ] == "iPhone 15" assert response.json()["price" ] == 5999 def test_create_order (): """测试创建订单""" response = requests.post("https://api.example.com/orders" , json={ "product_id" : 123 , "quantity" : 1 , "address" : "北京市朝阳区xxx" }, headers={"Authorization" : "Bearer token" }) assert response.status_code == 201 assert "order_id" in response.json() assert response.json()["status" ] == "pending"
🛠️ API 测试工具推荐 Python 生态
工具
类型
特点
推荐度
Requests
库
简单、易用
⭐⭐⭐⭐⭐
Pytest
框架
强大、灵活
⭐⭐⭐⭐⭐
Tavern
框架
YAML 配置
⭐⭐⭐⭐
Locust
性能测试
易用、可扩展
⭐⭐⭐⭐⭐
JavaScript 生态
工具
类型
特点
推荐度
Axios
库
简单、Promise
⭐⭐⭐⭐⭐
Jest
框架
快速、易用
⭐⭐⭐⭐⭐
Supertest
库
Express 测试
⭐⭐⭐⭐
K6
性能测试
现代、强大
⭐⭐⭐⭐⭐
GUI 工具
工具
类型
特点
推荐度
Postman
GUI
易用、功能强大
⭐⭐⭐⭐⭐
Insomnia
GUI
简洁、美观
⭐⭐⭐⭐
SoapUI
GUI
SOAP/REST
⭐⭐⭐⭐
Pytest + Requests 示例(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import pytestimport requestsBASE_URL = "https://api.example.com" class TestProductAPI : @pytest.fixture(scope="class" ) def auth_token (self ): """获取认证 token""" response = requests.post(f"{BASE_URL} /login" , json={ "email" : "test@example.com" , "password" : "password123" }) return response.json()["token" ] def test_get_products (self, auth_token ): """测试获取商品列表""" response = requests.get( f"{BASE_URL} /products" , headers={"Authorization" : f"Bearer {auth_token} " } ) assert response.status_code == 200 assert "products" in response.json() assert len (response.json()["products" ]) > 0 def test_get_product_by_id (self, auth_token ): """测试获取商品详情""" response = requests.get( f"{BASE_URL} /products/123" , headers={"Authorization" : f"Bearer {auth_token} " } ) assert response.status_code == 200 product = response.json() assert product["id" ] == 123 assert "name" in product assert "price" in product def test_create_product (self, auth_token ): """测试创建商品""" response = requests.post( f"{BASE_URL} /products" , json={ "name" : "Test Product" , "price" : 99.99 , "stock" : 100 }, headers={"Authorization" : f"Bearer {auth_token} " } ) assert response.status_code == 201 product = response.json() assert product["name" ] == "Test Product" assert product["price" ] == 99.99 def test_update_product (self, auth_token ): """测试更新商品""" response = requests.put( f"{BASE_URL} /products/123" , json={"price" : 199.99 }, headers={"Authorization" : f"Bearer {auth_token} " } ) assert response.status_code == 200 product = response.json() assert product["price" ] == 199.99 def test_delete_product (self, auth_token ): """测试删除商品""" response = requests.delete( f"{BASE_URL} /products/123" , headers={"Authorization" : f"Bearer {auth_token} " } ) assert response.status_code == 204
Postman 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 products" , function ( ) { var jsonData = pm.response .json (); pm.expect (jsonData).to .have .property ('products' ); pm.expect (jsonData.products ).to .be .an ('array' ); pm.expect (jsonData.products .length ).to .be .above (0 ); }); pm.test ("Product has required fields" , function ( ) { var jsonData = pm.response .json (); var product = jsonData.products [0 ]; pm.expect (product).to .have .property ('id' ); pm.expect (product).to .have .property ('name' ); pm.expect (product).to .have .property ('price' ); });
📊 最佳实践 1. 使用测试金字塔 🔺 1 2 3 4 5 6 7 /\ /E2E\ 10% - 端到端测试 /------\ /API测试\ 30% - API 测试 ← 重点 /----------\ / 单元测试 \ 60% - 单元测试 /--------------\
原则 :
60% 单元测试
30% API 测试
10% E2E 测试
2. 遵循 REST 规范 📝 HTTP 方法 :
方法
用途
示例
GET
获取资源
GET /products
POST
创建资源
POST /products
PUT
更新资源(全量)
PUT /products/123
PATCH
更新资源(部分)
PATCH /products/123
DELETE
删除资源
DELETE /products/123
状态码 :
状态码
含义
使用场景
200
成功
GET、PUT、PATCH 成功
201
已创建
POST 成功
204
无内容
DELETE 成功
400
请求错误
参数错误
401
未授权
未登录
403
禁止访问
无权限
404
未找到
资源不存在
500
服务器错误
服务器异常
3. 编写清晰的测试用例 ✍️ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 def test_create_order_with_valid_data (): """ 测试用例:使用有效数据创建订单 前置条件: - 用户已登录 - 商品库存充足 测试步骤: 1. 发送创建订单请求 2. 验证响应状态码为 201 3. 验证订单 ID 存在 4. 验证订单状态为 pending 5. 验证库存扣减 预期结果: - 订单创建成功 - 库存正确扣减 """ response = requests.post(f"{BASE_URL} /orders" , json={ "product_id" : 123 , "quantity" : 1 }, headers={"Authorization" : f"Bearer {token} " }) assert response.status_code == 201 order = response.json() assert "order_id" in order assert order["status" ] == "pending" product = requests.get(f"{BASE_URL} /products/123" ).json() assert product["stock" ] == initial_stock - 1
4. 使用数据驱动测试 📊 1 2 3 4 5 6 7 8 9 10 @pytest.mark.parametrize("product_id,expected_status" , [ (123 , 200 ), (999 , 404 ), (-1 , 400 ), ("abc" , 400 ), ] )def test_get_product_by_id (product_id, expected_status ): """测试获取商品详情(数据驱动)""" response = requests.get(f"{BASE_URL} /products/{product_id} " ) assert response.status_code == expected_status
5. 集成到 CI/CD 🔄 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 name: API Tests on: [push , pull_request ]jobs: api-test: runs-on: ubuntu-latest services: postgres: image: postgres:14 env: POSTGRES_DB: test_db POSTGRES_USER: test_user POSTGRES_PASSWORD: test_pass options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.9 - name: Install dependencies run: pip install -r requirements.txt - name: Run migrations run: python manage.py migrate - name: Run API tests run: pytest tests/api/ -v --cov=api - name: Upload coverage uses: codecov/codecov-action@v2
📊 总结对比 API 测试 vs 其他测试
维度
单元测试
API 测试
E2E 测试
测试范围
单个函数
完整接口
完整流程
测试速度
快(毫秒)
中(秒)
慢(分钟)
测试成本
低
中
高
问题定位
容易
中等
困难
测试数量
多(60%)
中(30%)
少(10%)
依赖
无
数据库
全部
维护成本
低
中
高
🎓 学习资源 书籍推荐
《RESTful Web APIs》- Leonard Richardson
《API 设计最佳实践》- Arnaud Lauret
《测试驱动开发》- Kent Beck
在线课程
官方文档
🔗 相关主题
[[单元测试]] - API 测试的基础
[[集成测试]] - API 测试的一种
[[性能测试]] - API 性能测试
[[自动化测试]] - API 测试的实现方式
💡 快速参考卡片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 API 测试速查表 ================ 定义:测试应用程序接口的功能、性能、安全性 核心优势: ✅ 快速反馈(比 UI 测试快 10 倍) ✅ 早期测试(前端开发前就能测试) ✅ 易于自动化(适合 CI/CD) 测试类型: - 功能测试(验证功能正确) - 性能测试(验证响应时间) - 安全测试(验证安全性) - 契约测试(验证符合规范) - 集成测试(验证系统集成) 推荐工具: - Python: Requests + Pytest - JavaScript: Axios + Jest - GUI: Postman HTTP 方法: - GET: 获取资源 - POST: 创建资源 - PUT: 更新资源(全量) - PATCH: 更新资源(部分) - DELETE: 删除资源 常用状态码: - 200: 成功 - 201: 已创建 - 204: 无内容 - 400: 请求错误 - 401: 未授权 - 404: 未找到 - 500: 服务器错误 最佳实践: 1. 遵循测试金字塔(30% API 测试) 2. 遵循 REST 规范 3. 编写清晰的测试用例 4. 使用数据驱动测试 5. 集成到 CI/CD 测试金字塔: 60% 单元测试 30% API 测试 ← 你在这里 10% E2E 测试
2. 性能测试 定义 :验证 API 的响应时间和吞吐量。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from locust import HttpUser, task, betweenclass APIUser (HttpUser ): wait_time = between(1 , 3 ) @task def get_products (self ): self.client.get("/products" ) @task def search_products (self ): self.client.get("/products/search?q=iPhone" )
3. 安全测试 定义 :验证 API 的安全性。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def test_unauthorized_access (): """测试未授权访问""" response = requests.get("https://api.example.com/orders" ) assert response.status_code == 401 def test_sql_injection (): """测试 SQL 注入""" response = requests.get( "https://api.example.com/products/search" , params={"q" : "'; DROP TABLE products; --" } ) assert response.status_code == 200 assert "error" not in response.json() def test_xss (): """测试 XSS 攻击""" response = requests.post("https://api.example.com/comments" , json={ "content" : "<script>alert('XSS')</script>" }, headers={"Authorization" : "Bearer token" }) comment = response.json() assert "<script>" not in comment["content" ] assert "<script>" in comment["content" ]
4. 契约测试 定义 :验证 API 是否符合约定的契约(OpenAPI/Swagger)。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import requestsfrom openapi_spec_validator import validate_specdef test_api_contract (): """测试 API 契约""" spec = requests.get("https://api.example.com/openapi.json" ).json() validate_spec(spec) response = requests.get("https://api.example.com/products/123" ) assert "id" in response.json() assert "name" in response.json() assert "price" in response.json() assert isinstance (response.json()["id" ], int ) assert isinstance (response.json()["name" ], str ) assert isinstance (response.json()["price" ], (int , float ))
5. 集成测试 定义 :验证 API 与其他系统的集成。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def test_payment_integration (): """测试支付集成""" order_response = requests.post("https://api.example.com/orders" , json={ "product_id" : 123 , "quantity" : 1 }, headers={"Authorization" : "Bearer token" }) order_id = order_response.json()["order_id" ] payment_response = requests.post( f"https://api.example.com/orders/{order_id} /pay" , json={"payment_method" : "wechat" }, headers={"Authorization" : "Bearer token" } ) assert payment_response.status_code == 200 order = requests.get( f"https://api.example.com/orders/{order_id} " , headers={"Authorization" : "Bearer token" } ).json() assert order["status" ] == "paid" product = requests.get("https://api.example.com/products/123" ).json() assert product["stock" ] == initial_stock - 1
⚠️ API 测试的挑战 挑战 1:测试数据管理 📊 问题 :
需要准备测试数据
数据之间有依赖关系
测试后需要清理数据
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import pytest@pytest.fixture(scope="function" ) def test_user (): """创建测试用户""" response = requests.post("https://api.example.com/register" , json={ "username" : f"testuser_{uuid.uuid4()} " , "email" : f"test_{uuid.uuid4()} @example.com" , "password" : "password123" }) user = response.json() yield user requests.delete( f"https://api.example.com/users/{user['user_id' ]} " , headers={"Authorization" : f"Bearer {user['token' ]} " } ) def test_create_order (test_user ): """测试创建订单""" response = requests.post("https://api.example.com/orders" , json={ "product_id" : 123 , "quantity" : 1 }, headers={"Authorization" : f"Bearer {test_user['token' ]} " }) assert response.status_code == 201
挑战 2:环境依赖 🔧 问题 :
需要数据库
需要第三方服务(支付、短信)
环境不稳定
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from unittest.mock import patch, Mock@patch('payment_service.wechat_pay' ) def test_wechat_payment (mock_wechat_pay ): """测试微信支付""" mock_wechat_pay.return_value = { "success" : True , "transaction_id" : "wx123456789" } response = requests.post("https://api.example.com/orders/123/pay" , json={ "payment_method" : "wechat" }, headers={"Authorization" : "Bearer token" }) assert response.status_code == 200 assert response.json()["payment_status" ] == "success"
挑战 3:API 版本管理 🔄 问题 :
API 版本升级
需要同时测试多个版本
向后兼容性
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @pytest.mark.parametrize("version" , ["v1" , "v2" , "v3" ] ) def test_get_products_all_versions (version ): """测试所有版本的获取商品 API""" response = requests.get(f"https://api.example.com/{version} /products" ) assert response.status_code == 200 assert "products" in response.json() if version in ["v1" , "v2" ]: assert isinstance (response.json()["products" ], list ) else : assert isinstance (response.json()["products" ], dict )