📘 接口自动化-aom设计模式介绍- 来自ai,个人留存

4 阅读4分钟

来自 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() 或自定义错误
不用 Sessionrequests.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