接口自动化零基础落地:Requests + Pytest + Jenkins 实战
发布时间:2026/9/3 5:54:09 作者:尧图编辑部 阅读量:1,286

先回答一个经常被问的问题接口自动化到底要不要先找一套“成熟框架”学起来答案很直接不要。中小团队和大多数接口测试场景最稳的路线不是去背复杂框架而是用 Python Requests 把请求发出去用 Pytest 把用例组织起来再把报告和持续集成接上。这套技术栈可替换、可维护、可扩展学会了以后再去接触其他框架基本是降维看。这次这篇文章就按“最快能落地的路线”来写Python Requests Pytest从环境搭建、接口发起、用例编写到测试数据和断言封装再到 Jenkins 持续集成自动触发。最后会给出常见报错排查和项目落地的合规注意事项。由于接口自动化依赖被测服务文中不会编造一套“万能测试平台”的假数据而是用本地模拟接口作为示例保证每一步都可以真实运行验证。看完你至少能带走三样东西一台机器上从零跑通的接口自动化测试项目、一套可复制的 Requests/Pytest 工程结构、一份可以接到 Jenkins 的自动执行脚本。如果你是测试工程师、刚转后端的开发或者团队正准备把手动接口校验转成自动化回归这篇文章可以按章节直接照着做。1. 接口自动化核心能力速览与学习路线在做接口自动化之前先确认一个前提你想测的是 HTTP 接口不是 UI也不是复杂的浏览器交互。接口自动化的本质是“直接对服务端发请求根据响应判断业务逻辑是否正确”。它比 UI 自动化稳定比手工回归高效适合后端频繁迭代时的快速验证。这条路线涉及的能力总结如下学习模块在项目中解决什么问题实践产出Python 基础语法能写脚本能理解变量、函数、类可运行的 Python 脚本Requests 库发送 HTTP 请求处理响应能调用 GET/POST 接口并取值Pytest 测试框架管理用例、断言、前置条件、参数化pytest 可收集执行的测试集测试封装与数据管理不要把 URL、Token 散落在每个用例里可复用的 API Client、配置模块测试报告结果可视化方便回归追溯HTML 报告 / Allure 报告持续集成提交代码后自动跑接口测试Git Jenkins 流水线如果你的目标只是“先跑起来”优先做前三行环境 Requests Pytest。这个阶段相当于给后续所有自动化项目打地基。跑通基础后再慢慢补上封装和持续集成。整体时间分配可以借鉴标题里的“6 小时”思路第 1 小时环境安装、项目初始化、跑通第一个接口请求。第 2 小时Requests 库的常用方法、JSON 断言、异常处理。第 3 小时Pytest 断言、fixture、参数化。第 4 小时请求层封装、Token 管理、测试数据设计。第 5 小时报告输出与 Jenkins 配置。第 6 小时自己独立完成一个简单接口的自动化回归验证。2. 适用场景与工程化边界这套技术组合适合哪些场景最典型的是后端接口回归测试。比如接口新增字段、修改校验逻辑、数据库表结构调整跑一遍自动化用例就能看到哪些业务请求失败。其次是数据构造和清理通过脚本批量调用接口创建测试数据再在用例结束后删除。再有是服务健康检查定时对核心接口发起最小请求确认返回状态码和时间是否符合预期。但要区分清楚接口自动化并不是所有测试问题的银弹。不适合的场景至少有这些第一UI 还原程度验证页面布局和控件交互必须交给前端测试或 E2E 框架第二高并发性能压测Requests 默认是同步请求Pytest 也不会自动提升并发能力如果要验证上万 QPS应该使用 Locust、JMeter 这类专用工具第三接口文档不稳定的项目。如果接口三天两头改 URL、改请求参数又没有人维护文档自动化用例维护成本会明显上升这时候先推动接口文档标准化比写脚本更优先。还有一类场景必须单独提示如果你准备调用的接口不属于自己公司、没有获得授权不要把它当成自动化测试对象。Requests 可以发请求不代表可以对别人的公共服务做高频扫描或轮询。网络上经常出现“429 Too Many Requests”就是短时间内请求超出服务端限额的典型情况。在自己的测试环境或获得授权的环境中遇到 429是在验证限流功能在未经授权的公共接口上遇到 429应该立刻停止相关操作。3. 环境准备从 Python 安装到项目依赖接口自动化的环境门槛不高Windows、Linux、macOS 都可以跑。Python 版本建议使用目前较新的 Python 3 稳定版Pytest 和 Requests 对 Python 3 的支持都很成熟。第一次安装 Python 时有一个容易踩的坑Windows 安装界面记得勾选 Add Python to PATH否则命令行里敲 python 会提示找不到命令。安装完成后先确认版本python --version pip --version如果 python 命令提示不存在但安装目录里有 python.exe可以手动把安装目录加入系统 PATH。Linux 系统上通常还需要安装 python3-venv 或 python3-pip。接下来创建一个实践项目目录并开启虚拟环境mkdir api-auto-demo cd api-auto-demo # 创建虚拟环境 python -m venv venv # Windows 激活虚拟环境 venv\Scripts\activate # Linux / macOS 激活虚拟环境 source venv/bin/activate虚拟环境激活后命令行提示符前面会出现 (venv) 标记。这样后续安装的依赖只影响当前项目不会污染系统 PythonGitLab CI 或 Jenkins 里也能更清晰地复现环境。安装本次需要的核心依赖pip install requests pytest pytest-html allure-pytest安装完成后建议生成 requirements.txt便于后续部署和持续集成pip freeze requirements.txtrequirements.txt 内容类似requests pytest pytest-html allure-pytest从工程角度建议把项目结构分成独立目录而不是把所有脚本放在一个文件里api-auto-demo/ ├── venv/ # 虚拟环境 ├── config/ # 全局配置base_url、超时、账号信息 ├── common/ # 请求封装、日志封装、公共断言 ├── testcases/ # pytest 测试用例 ├── test_data/ # 测试数据 JSON / YAML / CSV ├── reports/ # 测试报告和 allure 结果 └── requirements.txt # 依赖清单如果说环境是地基那接下来要解决的就是核心问题怎么把请求发出去并拿到正确的返回值。4. Requests 入门发请求、看响应、处理异常Requests 是 Python 里最常用的 HTTP 请求库接口自动化基本都会用到。它解决的问题很直接用 Python 帮你构造 HTTP 请求自动管理 URL 编码、请求头、JSON 序列化并把服务端响应封装成可以简单取值的对象。4.1 一个最简接口调用为了不依赖公网服务先本地起一个可练习用的模拟接口。这里用 Flask 是很直观的选择但注意它不进入自动化测试的核心依赖只是测试目标端。先用 pip 安装pip install flask保存为mock_server.pyfrom flask import Flask, jsonify, request app Flask(__name__) app.post(/api/user) def create_user(): data request.get_json() return jsonify({code: 0, msg: ok, data: {user_id: 1, name: data.get(name)}}) app.get(/api/user/int:user_id) def get_user(user_id): return jsonify({code: 0, msg: ok, data: {user_id: user_id, name: tester}}) if __name__ __main__: app.run(host127.0.0.1, port8000)启动服务python mock_server.py服务起来后再打开一个终端继续在虚拟环境里用 Requests 发起第一个 POST 请求import requests url http://127.0.0.1:8000/api/user payload {name: tester01} headers {Authorization: Bearer test-token} resp requests.post(url, jsonpayload, headersheaders, timeout5) print(状态码:, resp.status_code) print(响应 JSON:, resp.json())预期能看到 200 状态码以及{code: 0, msg: ok, data: {user_id: 1, ...}}这样的返回值。第一次跑通这个例子说明环境、Requests、被测服务三大件都已经正常工作了。4.2 Session 与会话复用同一个接口往往要做多轮验证比如先登录拿 Token再带着 Token 查询用户信息。如果每次都重新创建 requests 请求连接不会复用效率不高。更好的方式是使用requests.Session。import requests session requests.Session() session.headers.update({Content-Type: application/json}) session.headers.update({Authorization: Bearer test-token}) for user_id in [1, 2, 3]: resp session.get(fhttp://127.0.0.1:8000/api/user/{user_id}, timeout5) print(user_id, resp.status_code)Session 会自动保存 Cookie并复用底层 TCP 连接。在做多步骤接口自动化时这是一个很实用的优化点。不过要注意 Session 不能帮你自动处理 Token 过期如果登录态失效还需要重新获取并更新请求头。4.3 超时、限流与退避重试Requests 发请求时如果不对 Timeout 做限制遇到服务端长时间没有响应代码会一直卡住。实际项目中几乎每一个请求都要设置 timeout避免单条用例拖垮整个测试任务。try: resp requests.get(http://127.0.0.1:8000/api/user/1, timeout3) resp.raise_for_status() except requests.exceptions.Timeout: print(请求超时) except requests.exceptions.HTTPError as e: print(HTTP 错误:, e) except requests.exceptions.ConnectionError as e: print(连接失败:, e)再回到前文提到的 429 问题。现在很多公共服务都会做限流单位时间内超过额定次数会返回 429 Too Many Requests或者像部分 SDK 报错一样提示exceeded retry limit。在接口自动化里遇到 429 时要区分两种情况如果被测系统本身就是自己公司的服务429 代表限流逻辑被触发要结合业务去判断该接口的设计是否合理如果是在调用第三方服务或临时演示 API那就应该用指数退避降低频率而不是继续重试。一个适合脚本调用的退避重试函数可以这样写import time import requests def request_with_retry(method, url, max_retries3, **kwargs): for attempt in range(max_retries): try: response requests.request( method, url, timeoutkwargs.pop(timeout, 5), **kwargs, ) if response.status_code 429: time.sleep(2 ** attempt) continue return response except requests.exceptions.RequestException: if attempt max_retries - 1: raise time.sleep(2 ** attempt) return response在这个函数里第 429 次触发后会让程序进入指数退避第一次退避 2 秒第二次退避 4 秒。对于低频脚本它是一种防御手段不建议对无授权的外部接口使用。5. Pytest 测试框架从 assert 到测试工具Requests 解决的是请求层的通用能力Pytest 则是把脚本从“能跑”变成“能回归”的关键。Pytest 能收集测试函数、执行断言、输出清晰失败信息也支持 fixture 管理和参数化。5.1 测试函数与断言新建testcases/test_user_api.pyimport requests BASE_URL http://127.0.0.1:8000 def test_create_user_success(): resp requests.post( f{BASE_URL}/api/user, json{name: tester01}, timeout5, ) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][user_id] 0 assert body[data][name] tester01在项目根目录执行python -m pytest testcases -vPytest 会自动发现testcases目录下以test_开头或用例函数名以test_开头的测试。如果断言失败控制台会打印出期望值和实际值。测试数量多了以后这个能力非常关键定位失败不需要每次都人工看日志。5.2 fixture把前置条件抽出来接口测试中经常会出现“每次执行前创建测试用户”或“登录后拿 Token”。如果每个用例都写一遍初始化逻辑后期维护很痛苦。Pytest fixture 就是用来解决这类问题。import pytest import requests BASE_URL http://127.0.0.1:8000 pytest.fixture def session(): s requests.Session() s.headers.update({Authorization: Bearer test-token}) yield s s.close() def test_get_user_info(session): resp session.get(f{BASE_URL}/api/user/1, timeout5) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][user_id] 1yield 之前的代码会先执行作为前置准备yield 之后的代码在执行完用例后执行可以作为清理操作。为了让所有测试用例共享同一个 fixture通常会把这个 fixture 放在testcases/conftest.py中Pytest 会自动识别。5.3 参数化一组数据驱动一个用例接口测试经常需要验证多种参数组合比如不同 user_id、不同请求参数、不同的预期状态码。用参数化可以减少大量重复函数。import pytest import requests BASE_URL http://127.0.0.1:8000 pytest.mark.parametrize( user_id, expected_code, [ (1, 200), (2, 200), (3, 200), ], ) def test_get_user_by_param(user_id, expected_code): resp requests.get(f{BASE_URL}/api/user/{user_id}, timeout5) assert resp.status_code expected_codePytest 会为每个参数组合生成一条独立测试记录。上例跑完会产生 3 条测试结果而不是 1 条。这种方式在数据驱动测试中很常见测试数据可以从 JSON 文件里读取再传给参数化。6. 接口自动化项目落地封装、鉴权、数据与批量单文件脚本适合学习但工程落地就不够用了。真实项目会有多个模块、不同接口、环境切换、登录态获取等需求需要把请求层封装成可复用的客户端。6.1 请求层封装在common/api_client.py中做一个轻量封装import requests class ApiClient: def __init__(self, base_url, tokenNone): self.base_url base_url.rstrip(/) self.session requests.Session() if token: self.session.headers.update({Authorization: fBearer {token}}) def request(self, method, path, **kwargs): url f{self.base_url}/{path.lstrip(/)} resp self.session.request(method, url, timeoutkwargs.pop(timeout, 5), **kwargs) return resp def get_user(self, user_id): return self.request(GET, f/api/user/{user_id}) def create_user(self, payload): return self.request(POST, /api/user, jsonpayload) def close(self): self.session.close()这样测试用例里不需要反复拼接 URL也不容易写错鉴权头。对应 fixture 可以写成import pytest from common.api_client import ApiClient pytest.fixture def client(): api ApiClient(http://127.0.0.1:8000, tokentest-token) yield api api.close() def test_get_user_with_client(client): resp client.get_user(1) assert resp.status_code 200 assert resp.json()[data][user_id] 16.2 鉴权与登录态管理很多接口并非裸奔状态而是需要先登录再调用。处理方式通常是创建一个 login 方法把用户名密码发送给登录接口。从响应中取出 token。更新 ApiClient 的 session 请求头。在用例前置条件中判断 token 是否过期。一个简单的实现思路api ApiClient(http://127.0.0.1:8000) auth_response api.request(POST, /api/login, json{username: admin, password: 123456}) token auth_response.json()[data][token] api.session.headers.update({Authorization: fBearer {token}})如果登录接口返回的 token 有有效期建议在 fixture 中维护 token 的时间戳。如果 token 过期自动重新登录而不要在每条用例里手写登录。这样的好处是接口测试用例本身只关注业务逻辑登录态是基础设施。6.3 测试数据与批量执行数据驱动是接口自动化批量执行的核心。多人合作时用例和测试数据应该分开存放。例如在test_data/create_user.json中[ {name: user01, expected_code: 200}, {name: user02, expected_code: 200}, {name: , expected_code: 500} ]然后在用例里读取这个文件并传给 Pytest 参数化import json import os import pytest from common.api_client import ApiClient def load_test_data(file_name): base_dir os.path.dirname(__file__) file_path os.path.join(base_dir, .., test_data, file_name) with open(file_path, r, encodingutf-8) as f: return json.load(f) pytest.mark.parametrize(case, load_test_data(create_user.json)) def test_create_user_by_data(case): api ApiClient(http://127.0.0.1:8000) resp api.create_user({name: case[name]}) assert resp.status_code case[expected_code]批量执行时Pytest 默认会把testcases目录下所有test_*.py文件一并执行。如果只是临时想跑某一条用例可以这样指定python -m pytest testcases/test_user_api.py::test_get_user_by_param -v需要实现多环境切换时把 base_url 放到配置文件里而不是硬编码在测试函数里。后续接持续集成也是通过参数化的环境变量来控制执行环境。7. 测试报告与持续集成从本地报告到自动流水线接口自动化的价值要通过报告体现出来。没有报告跑了多少用例、失败在哪一步都只能靠命令行日志不方便团队阅读。7.1 生成 HTML 报告与 Allure 报告Pytest 社区常用的报告方案有两种pytest-html 轻量适合临时查看python -m pytest testcases \ --htmlreports/pytest_report.html \ --self-contained-html \ -v生成后直接打开reports/pytest_report.html即可查看结果。如果后续要接持续集成并且希望展示趋势图、失败重试信息、历史报告Allure 更合适。先通过 pytest 参数生成结果目录python -m pytest testcases \ --alluredirreports/allure-results \ --clean-alluredir \ -v如果本机已经安装 Allure 命令行工具再执行allure serve reports/allure-results需要提醒一点allure-pytest 只负责生成结果 JSON真正把 JSON 渲染成网页需要额外安装 Allure Commandline。如果 Jenkins 环境没装 Allure 插件流水线里只配置--alluredir最后是拿不到可视化报告的。如果想先快速查看本地效果用 pytest-html 更省事因为它不需要额外安装命令行工具。7.2 接入 Jenkins 持续集成流水线持续集成要解决的核心问题是代码更新后自动拉取代码、安装依赖、执行测试、生成报告。推荐在项目根目录维护一个run_tests.sh脚本本地和 CI 都调用同一个入口#!/usr/bin/env bash set -e python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt pytest testcases \ --alluredirreports/allure-results \ --clean-alluredir记得给脚本加上可执行权限chmod x run_tests.shJenkins 新建一个 Pipeline 任务后在 Jenkinsfile 里写pipeline { agent any options { timeout(time: 10, unit: MINUTES) } stages { stage(拉取代码) { steps { checkout scm } } stage(安装依赖并执行接口自动化) { steps { sh bash run_tests.sh } } } post { always { allure includeProperties: true, jdk: , reportBuildPolicy: ALWAYS, results: [[path: reports/allure-results]] } } }如果使用 GitLab CI思路是一样的。在.gitlab-ci.yml中定义 job安装依赖后执行 pytest再把 Allure 报告作为 artifact 保留。持续集成的核心不是某个具体工具而是“测试入口统一、执行环境可复制、报告可追溯”这三件事。关于 CI 运行节点的选择建议使用独立构建机或 Docker agent不要在开发人员的笔记本上长期运行定时任务。否则本机 IP 变化、依赖冲突、环境变量缺失都会导致测试结果不稳定。8. 资源占用与执行效率观察接口自动化的资源占用比 AI 模型、视频推理低很多一般不需要专门准备高配 GPU 机器。普通 CPU、2GB 以上内存就能跑起多数 pytest requests 项目。但这并不意味着不用关注效率。接口自动化在实际执行时最明显的时间消耗往往不是 CPU而是网络等待和服务端处理时间。如果某个接口响应慢每条用例都要等满超时时间测试集规模一大整个批次的执行时间会被显著拉长。用 Pytest 自带 duration 统计可以快速定位慢用例python -m pytest testcases --durations10 -q执行后Pytest 会把最慢的 10 条用例列出来。如果发现某一条用例耗时特别久先看是断言逻辑问题、网络代理问题还是被测服务本身响应慢。要继续提高执行效率可以关注以下方向使用 Session 复用连接减少重复 TCP 握手。避免在每条用例中都重新登录优先通过 fixture 共享登录态。优化测试数据如果必须批量创建数据考虑调用批量接口而不是循环单个创建。合理选择并发粒度。Pytest 默认是逐个串行执行需要使用并发时可以结合 pytest-xdist 控制 worker 数。但并发不是越高越好如果被测服务有限流策略过高的并发会把正常业务流量打满。排查资源问题时建议分三块观察观察被测服务所在机器的 CPU、内存、网络连接数。观察执行机本身的 Python 进程占用确认是否存在连接未释放导致的句柄增长。观察接口响应耗时趋势而不是只盯着本地脚本执行时间。从常见网络报错经验看429 和限流最容易在执行频率过高时出现。自动化任务合理设置退避重试比无限重试更有工程价值。9. 常见问题排查与合规红线接口自动化上手阶段会碰到很多重复问题下面直接以排障表格形式给出处理思路。问题现象可能原因排查方式解决方案python 命令找不到未加入 PATH执行python --version看报错重新安装 Python 并勾选 Add to PATHpip 安装依赖很慢网络源不稳定查看 pip 日志切换为国内 PyPI 镜像或企业内部源Flask 以python mock_server.py启动失败默认 5000 端口被占用查看启动日志修改app.run(port8001)或先杀掉占用进程requests 请求报 ConnectionError被测服务未启动或端口不对浏览器访问该地址确认 mock_server 已启动检查 base_url请求总是超时未设置 timeout 或网络策略限制用 curl 验证同一接口增加超时参数或检查目标服务可达性收到 429 Too Many Requests单次执行频率过高或触发限流查看服务端限流日志降温重试使用指数退避不盲目提高并发pytest 没有收集到用例文件名、函数名不符合规则查看 pytest 收集信息命名改为test_*.py和def test_*allure: command not found缺少 Allure 命令行执行allure --version安装 allure 命令行再运行 pytest 收集命令HTML 报告打不开报告文件缺失或路径错误检查 reports 目录生成结果先确认用例执行完毕再查看 reports 下的文件除了技术排查还要时刻守住合规红线。接口自动化测试的合法使用边界通常包括被测接口来自自己所在公司、或被测试方已经明确授权测试数据不能包含真实用户敏感信息尤其是手机号、身份证、银行卡信息对第三方公共接口只做低频必要的连通性验证不做批量抓取和压测涉及登录爆破、权限绕过、扫描未授权接口等内容不属于接口自动化的正常工作范围。另外要注意版权和隐私保护。从测试环境拿到的用户数据、业务数据都应该脱敏后使用不能随手粘贴到外部平台或日志系统。写完的测试脚本如果需要开源也要先确认里面没有硬编码的密码、Token、内部域名和数据库连接串。10. 总结与下一步回到最开始的问题6 小时能不能搞懂接口自动化如果“搞懂”指的是能独立搭起一套 Requests Pytest 报告 持续集成的小工程那完全可以。技术学习能不能快速见效关键不在于工具多不多而在于主线是否聚焦。这篇文章给出的主线非常直接先用本地模拟接口跑通 Requests 请求再用 Pytest 把请求变成可回归的用例接下来用封装解决登录态、测试数据和代码复用问题最后接上 Allure 和 Jenkins 让回归自动化。按这个顺序执行你并不会碰到大量“屠龙之术”每一步都服务于当前阶段最需要的能力。落地时最容易踩的坑有三个第一在没跑通本地接口的情况下急于研究复杂框架第二把请求 URL、Token 写死在用例函数中后面没法维护第三在不清楚被测系统限流策略的情况下随意高并发批量执行。这三个坑都能通过“先小规模验证、再横向扩展”的方式避免。如果再往下走后续可以补充的方向有引入 pytest-xdist 做分布式并发执行、把测试数据放到 YAML/Excel 并支持多环境切换、使用 Locust 验证被测系统的限流阈值、在 CI 中增加失败用例自动重试和通知。接口自动化的核心价值是多轮迭代后仍能快速回归底层技术并不复杂真正决定项目能走多远的是任务拆分、数据隔离和报告设计这些工程习惯。如果你刚开始搭建自己的接口自动化基础设施建议先收藏这篇作为起点今天先完成第 3 节的环境安装再照着第 4 节让本地接口返回一次 JSON后面就很快了。