(非常全面的干货)Python接口自动化测试框架实战开发

3 阅读5分钟

Python接口自动化测试框架实战 从设计到开发

接口自动化测试是保障服务质量的基石。很多测试同学会写用例脚本,但当用例量激增时,如何管理用例、如何封装请求、如何生成优雅的测试报告,往往显得手忙脚乱。

市面上有很多成熟的测试框架,但“造轮子”是理解自动化精髓的最佳路径。本文将带你从零开始,基于 Python + Pytest + Requests,手撸一个轻量级、企业级的接口自动化测试框架。通过代码实战,你将掌握数据驱动、封装设计等核心技能。

一、 框架架构设计

一个专业的自动化框架,目录结构必须清晰,职责必须分离。

复制

auto_test_framework/
├── config/             # 配置文件
│   └── config.yaml
├── api/               # 接口封装层(业务逻辑)
│   └── user_api.py
├── cases/             # 测试用例层
│   └── test_user_login.py
├── utils/             # 工具类(封装请求、读取文件)
│   ├── client.py      # 核心:二次封装 Requests
│   └── logger.py      # 日志工具
├── data/              # 测试数据(JSON/YAML)
│   └── login_data.json
└── reports/           # 测试报告生成目录

核心思路

  1. API 层:封装具体的业务接口,如登录、下单。
  2. Client 层:底层封装 requests,统一处理 Session、Cookie、日志、异常。
  3. Case 层:使用 Pytest,结合数据驱动,只写测试逻辑。

二、 核心:二次封装 Requests 会话

直接在用例里写 requests.get() 会导致代码冗余且难以维护。我们需要一个统一的 HTTP 客户端,集成日志记录和 Session 管理。

1. utils/client.py 封装实现

python

复制

import requests
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class HttpClient:
    def __init__(self, base_url):
        self.base_url = base_url
        # 使用 Session 自动管理 Cookies,保持会话状态
        self.session = requests.Session()

    def request(self, method, endpoint, **kwargs):
        """
        统一请求方法封装
        :param method: 请求方法 GET/POST/PUT/DELETE
        :param endpoint: 接口地址(不包含域名)
        :param kwargs: requests 支持的所有参数
        :return: 响应对象
        """
        url = f"{self.base_url}{endpoint}"
        
        # 打印请求信息,方便调试
        logger.info(f"Request: {method} {url}")
        logger.info(f"Params: {kwargs.get('params') or kwargs.get('json')}")

        try:
            # 发送请求
            res = self.session.request(method, url, **kwargs)
            
            # 记录响应信息
            logger.info(f"Response Status: {res.status_code}")
            logger.info(f"Response Body: {res.text}")
            
            return res
        except requests.exceptions.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise

三、 业务层:API 接口封装

将具体的接口调用逻辑与测试脚本剥离。如果在多个用例中都需要“登录”,只需要在这里维护一份代码。

2. api/user_api.py 用户接口封装

python

复制

from utils.client import HttpClient

class UserAPI:
    def __init__(self, client: HttpClient):
        self.client = client

    def login(self, username, password):
        """
        登录接口
        """
        payload = {
            "username": username,
            "password": password
        }
        # 发送 POST 请求
        return self.client.request("POST", "/api/v1/login", json=payload)

    def get_user_info(self, user_id):
        """
        获取用户信息接口
        """
        params = {"id": user_id}
        return self.client.request("GET", "/api/v1/user/info", params=params)

四、 数据驱动:Pytest 参数化实战

为了实现“一份代码测多组数据”,我们将测试数据存储在 JSON 文件中,通过 Pytest 的 @pytest.mark.parametrize 加载。

3. data/login_data.json 测试数据

json

复制

[  {    "desc": "正确账号密码登录",    "username": "admin",    "password": "123456",    "expected_code": 200,    "expected_msg": "success"  },  {    "desc": "密码错误",    "username": "admin",    "password": "wrong_pwd",    "expected_code": 401,    "expected_msg": "Unauthorized"  },  {    "desc": "用户名为空",    "username": "",    "password": "123456",    "expected_code": 400,    "expected_msg": "Bad Request"  }]

4. cases/test_user_login.py 测试用例编写

python

复制

import pytest
import json
from utils.client import HttpClient
from api.user_api import UserAPI

# 读取测试数据
def load_json_data(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        return json.load(f)

# 初始化 API 客户端(这里假设本地 Mock 服务,实际应为配置文件读取环境变量)
client = HttpClient(base_url="http://127.0.0.1:5000")
user_api = UserAPI(client)

# 获取测试数据
test_data = load_json_data("../data/login_data.json")

class TestUserLogin:

    @pytest.mark.parametrize("case", test_data)
    def test_login(self, case):
        """
        测试登录功能
        :param case: 从 json 加载的每一行数据字典
        """
        # 1. 步骤:调用接口
        res = user_api.login(case["username"], case["password"])

        # 2. 断言:校验状态码
        assert res.status_code == case["expected_code"], \
            f"{case['desc']}:状态码不一致,期望 {case['expected_code']},实际 {res.status_code}"

        # 3. 断言:校验返回体 (这里简单演示,实际可能需要校验 JSON 结构)
        res_json = res.json()
        assert res_json.get("message") == case["expected_msg"], \
            f"{case['desc']}:提示信息不一致"
        
        print(f"✅ 用例通过:{case['desc']}")

五、 框架进阶:Hook 与 配置

为了让框架更专业,我们需要在 Pytest 中使用 conftest.py 来做全局的前后置处理,比如初始化日志、清理测试数据、生成 Allure 报告。

5. conftest.py 钩子函数

python

复制

import pytest
from utils.client import HttpClient

@pytest.fixture(scope="session")
def global_client():
    """
    Session 级别的 Fixture:
    在整个测试会话开始时创建 Client,结束后销毁
    可以在这里实现登录获取 Token,并注入到 Client 中
    """
    print("\n[Setup] 初始化全局 HTTP 客户端...")
    client = HttpClient(base_url="http://127.0.0.1:5000")
    
    # 例子:预先登录,获取 token 并放入 session headers
    # login_res = client.request("POST", "/api/v1/login", json={"u":"a","p":"b"})
    # token = login_res.json().get("token")
    # client.session.headers.update({"Authorization": f"Bearer {token}"})
    
    yield client # 将 client 传递给用例
    
    print("\n[Teardown] 清理资源...")

然后在 test_user_login.py 中修改测试类,使用 fixture 注入:

python

复制

class TestUserLogin:

    def test_login(self, global_client, case): # Pytest 自动注入 global_client
        # 使用注入的 client 实例化 API
        api = UserAPI(global_client)
        res = api.login(case["username"], case["password"])
        assert res.status_code == case["expected_code"]

六、 总结

通过以上实战,我们搭建了一个具备以下特性的专业框架:

  1. 分层架构:API 层与 Case 层分离,维护成本大幅降低。
  2. 统一封装HttpClient 解决了日志记录和会话管理问题,代码复用性高。
  3. 数据驱动:JSON + Pytest 参数化,实现了“代码逻辑”与“测试数据”的彻底分离。

这就摆脱了“脚本小子”的范畴,迈向了测试开发的领域。掌握这套思路,无论是 Python 还是 Java,你都能轻松构建出适合自己团队的自动化测试平台。