来自 qwen3-max-网页版-2025年10月16日21:09:15
API Object Model(AOM)设计模式,这是接口自动化测试中非常经典、实用的一种设计思想。
🧩 一、先打个比方:为什么需要 AOM?
想象你是一个外卖平台的测试员,每天要测试:
- 用户登录
- 搜索餐厅
- 下单
- 支付
- 查看订单状态
如果你每次都这样写测试代码:
# ❌ 原始写法(没有 AOM)
def test_order():
# 1. 登录
resp = requests.post("https://api.eat.com/login", json={"user": "tom", "pwd": "123"})
token = resp.json()["token"]
# 2. 搜索餐厅
headers = {"Authorization": f"Bearer {token}"}
resp = requests.get("https://api.eat.com/restaurants?keyword=pizza", headers=headers)
restaurant_id = resp.json()[0]["id"]
# 3. 下单
resp = requests.post("https://api.eat.com/orders", json={"restaurant_id": restaurant_id}, headers=headers)
order_id = resp.json()["order_id"]
# 4. 支付...
# ...
你会发现:
- 重复代码太多(每次都要拼 URL、加 headers)
- URL 写死在测试里,一旦接口地址变了,所有测试都要改
- 业务逻辑和测试逻辑混在一起,难读难维护
🎯 AOM 就是为了解决这些问题而生的!
🏗️ 二、什么是 API Object Model(AOM)?
AOM = 把每个 API 封装成一个“对象”(类),测试时只调用这个对象的方法。
就像你开车不用知道发动机怎么工作,只要会踩油门(调用方法)就行。
✅ AOM 的核心思想:
“测试用例只关心业务流程,不关心 HTTP 细节”
🧱 三、AOM 长什么样?—— 举个真实例子
我们继续用外卖平台举例,设计几个 API 对象:
1️⃣ 先建一个基础类:BaseAPI
# apis/base_api.py
import requests
from typing import Optional, Dict, Any
class BaseAPI:
"""所有 API 的基类,封装通用逻辑"""
def __init__(self, base_url: str, token: Optional[str] = None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
if token:
self.session.headers.update({"Authorization": f"Bearer {token}"})
def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
"""统一发送请求,自动拼接 URL"""
url = f"{self.base_url}{endpoint}"
response = self.session.request(method, url, timeout=10, **kwargs)
response.raise_for_status() # 自动抛异常
return response
2️⃣ 登录 API:AuthAPI
# apis/auth_api.py
from .base_api import BaseAPI
class AuthAPI(BaseAPI):
"""登录相关接口"""
def login(self, username: str, password: str) -> str:
"""登录并返回 token"""
resp = self._request("POST", "/auth/login", json={
"username": username,
"password": password
})
return resp.json()["token"]
3️⃣ 餐厅 API:RestaurantAPI
# apis/restaurant_api.py
from .base_api import BaseAPI
class RestaurantAPI(BaseAPI):
"""餐厅相关接口"""
def search(self, keyword: str) -> list:
"""搜索餐厅"""
resp = self._request("GET", f"/restaurants?keyword={keyword}")
return resp.json()["restaurants"]
def get_restaurant(self, restaurant_id: str) -> dict:
"""获取餐厅详情"""
resp = self._request("GET", f"/restaurants/{restaurant_id}")
return resp.json()
4️⃣ 订单 API:OrderAPI
# apis/order_api.py
from .base_api import BaseAPI
class OrderAPI(BaseAPI):
"""订单相关接口"""
def create_order(self, restaurant_id: str, items: list) -> str:
"""创建订单,返回 order_id"""
resp = self._request("POST", "/orders", json={
"restaurant_id": restaurant_id,
"items": items
})
return resp.json()["order_id"]
def get_order(self, order_id: str) -> dict:
"""查询订单"""
resp = self._request("GET", f"/orders/{order_id}")
return resp.json()
🧪 四、测试用例怎么写?—— 超清爽!
# test_order_flow.py
import pytest
from apis.auth_api import AuthAPI
from apis.restaurant_api import RestaurantAPI
from apis.order_api import OrderAPI
BASE_URL = "https://api.eat.com"
def test_user_can_order_pizza():
# 1. 登录
auth_api = AuthAPI(BASE_URL)
token = auth_api.login("tom", "123456")
# 2. 搜索披萨店
restaurant_api = RestaurantAPI(BASE_URL, token=token)
restaurants = restaurant_api.search("pizza")
assert len(restaurants) > 0
pizza_shop = restaurants[0]
# 3. 下单
order_api = OrderAPI(BASE_URL, token=token)
order_id = order_api.create_order(
restaurant_id=pizza_shop["id"],
items=[{"product_id": "pizza_001", "quantity": 1}]
)
# 4. 验证订单
order = order_api.get_order(order_id)
assert order["status"] == "created"
assert order["restaurant"]["name"] == pizza_shop["name"]
print(f"✅ 成功下单!订单ID: {order_id}")
✅ 五、AOM 带来的好处(对比原始写法)
| 问题 | 原始写法 | AOM 写法 |
|---|---|---|
| URL 硬编码 | 到处写 "https://..." | 集中在 BaseAPI |
| 重复 headers | 每次手动加 token | 自动注入 |
| 请求逻辑重复 | 每次写 requests.post(...) | 封装在 _request |
| 接口变更 | 所有测试文件都要改 | 只改对应 API 类 |
| 可读性 | 像写脚本 | 像写业务故事 |
| 复用性 | 几乎为 0 | 多个测试共享 API 对象 |
🛠️ 六、进阶技巧(生产环境必备)
1. 结合 pytest fixture
# conftest.py
@pytest.fixture(scope="session")
def auth_api():
return AuthAPI(BASE_URL)
@pytest.fixture(scope="session")
def token(auth_api):
return auth_api.login("test_user", "test_pass")
测试用例变得更简洁:
def test_order(token):
restaurant_api = RestaurantAPI(BASE_URL, token=token)
# ...
2. 支持 Mock(单元测试用)
你可以让 BaseAPI 支持传入 mock 参数,方便单元测试不调真实接口。
3. 日志 & 报告集成
在 _request 方法里加日志:
import logging
logger = logging.getLogger(__name__)
def _request(self, method, endpoint, **kwargs):
logger.info(f"→ {method} {self.base_url}{endpoint}")
resp = self.session.request(...)
logger.info(f"← {resp.status_code} {resp.text[:100]}...")
return resp
🚫 七、常见误区
| 误区 | 正确做法 |
|---|---|
| 把所有 API 放在一个大类里 | 按业务模块拆分(AuthAPI, UserAPI, OrderAPI...) |
| 在 API 类里写断言 | API 类只负责调用,不负责验证,断言留在测试用例 |
| 忽略异常处理 | 一定要 raise_for_status() 或自定义错误 |
| 不用 Session | 用 requests.Session() 复用连接,提升性能 |
🎁 八、总结:AOM 是什么?
AOM 就是给你的 API 套上一层“外衣”,让测试人员像调用普通方法一样使用接口,而不用关心底层是 GET 还是 POST、URL 是什么、要不要加 token。
它就像:
- 给汽车装上方向盘(你不用懂齿轮怎么转)
- 给手机装上触摸屏(你不用懂电路怎么工作)
📦 附:项目结构建议
project/
├── apis/
│ ├── __init__.py
│ ├── base_api.py
│ ├── auth_api.py
│ ├── user_api.py
│ └── order_api.py
├── tests/
│ ├── conftest.py
│ └── test_order_flow.py
└── requirements.txt