资讯动态

Python+requests接口自动化测试框架从0到1搭建指南

发布时间:2026/10/13 8:50:08 来源:尧图企业网站定制
1. 为什么接口自动化我推荐Pythonrequests做了几年的接口测试前后用过Postman、JMeter也带过团队搞过JavaRestAssured的方案但最后兜兜转转还是回到了Pythonrequests这套组合。原因很简单这套方案解决了我最头疼的几个问题写用例的门槛足够低新来的测试同学看两天文档就能上手requests库封装得像瑞士军刀想用什么功能基本都有再加上Python生态里pytest、allure这些测试报告工具链非常成熟组合起来就是一套私人定制版的接口自动化测试框架比起开源的平台更灵活也不会被供应商绑架。接口自动化测试这件事本质上是在做一件“用脚本模拟真实用户请求”的事情。你手工在Postman里点一百次请求和用代码发一百次请求背后的核心逻辑是一样的——构造请求、发送请求、校验响应。只不过代码可以重复跑、可以排期跑、可以半夜跑完给你发一封邮件告诉你哪个接口挂了。它的核心价值不在于“快”而在于“省心”解放的是测试人员反复做回归测试的时间。如果你现在处于这三种场景之一那Pythonrequests基本就是你的最优解团队里没有专门的测试开发测试人员主要用Python做脚本接口数量多变更频繁需要一套轻量级、改起来快的用例集想从纯手工测试转向自动化但又不想一上来就上重型的自动化测试平台。另外很多人会纠结选Python还是Java。如果你所在团队全是Java后端且测试团队本身有较强的编码能力JavaRestAssured也算合理。但就我观察大多数情况下Python的学习曲线更平缓同样的接口测试用例Python写出来的代码量可能只有Java的一半。尤其在排查线上问题、临时造数据、跑一个定时巡检脚本这些场景Python的快速迭代优势是碾压级的。后面我会结合一个实际的接口测试项目把整个搭建过程、核心代码、踩坑记录都讲清楚感兴趣的话照着做就行。2. 环境准备与requests安装避坑2.1 Python环境和IDE怎么选老生常谈的问题但确实有不少人栽在这里。首先Python版本建议装3.9以上3.6以下的老版本就别考虑了很多新版库已经放弃兼容装完跑起来全是语法报错。如果你用的是Windows去Python官网下载安装包的时候注意把“Add Python to PATH”那个复选框勾上不然装完在命令行里敲python会提示找不到命令。IDE的话我推荐VSCode配Python插件就够了轻量、启动快、调试顺手。PyCharm虽然功能更全但启动慢、吃内存对于接口自动化这种小体量项目来说有点杀鸡用牛刀。第一次用VSCode跑Python的时候记得打开命令面板选一下Python解释器不然你装好的requests库VSCode可能根本不认识。这里说个新手常见场景你在命令行里执行pip install requests成功了但在VSCode里import requests却报ModuleNotFoundError。十有八九是VSCode选中的解释器和命令行用的不是同一个Python。解决方案很简单在VSCode界面右下角点击Python版本号弹出来的列表里选择你命令行里那个解释器路径就行。2.2 requests库安装与pip告警解读requests库的安装没什么复杂的命令行执行pip install requests如果你遇到提示Defaulting to user installation because normal site-packages is not writeable这种信息先别慌这不算报错只是pip在告诉你当前环境没有权限写系统级site-packages目录所以装到了当前用户目录下。大多数情况下这样已经可以正常使用了不必跟它较劲。真正值得注意的坑在镜像源。因为网络环境的原因直接走官方PyPI源下载经常慢到怀疑人生。安装的时候建议顺手换成国内镜像清华源或者阿里云源都行实测下载速度快了一个量级pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple如果你希望以后pip默认就走镜像源可以执行一次全局配置pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple多提一句requests库作为一个第三方库它的底层依赖了urllib3负责HTTP连接池管理装requests的时候pip会自动把urllib3也装上。但这两个库有时会出现版本兼容问题如果你跑脚本的时候看到IncompleteRead之类的报错大概率是urllib3版本太高导致的流式读取问题可以尝试固定版本解决pip install urllib31.26.x。2.3 还需要装哪些配套库requests只是帮你发HTTP请求但做接口自动化测试还需要其他帮手pytest测试用例的管理和执行框架断言失败会直接告诉你期望值和实际值差在哪。pytest-html或allure-pytest生成测试报告allure的报告颜值高后面细讲。pyyaml解析YAML格式的测试数据。openpyxl读写Excel格式的测试数据。安装命令组装到一起就是pip install requests pytest allure-pytest pyyaml openpyxl建议在项目里单独建一个虚拟环境不污染全局Python环境。项目目录下执行python -m venv venvWindows下激活命令是venv\Scripts\activateMac/Linux下是source venv/bin/activate。激活之后环境变量前面会出现(venv)的标识这时候pip安装的库只在这个项目里生效换个项目互不干扰。3. requests库的核心用法与接口测试实战基础3.1 GET请求参数传递和响应处理的正确姿势requests库的GET请求最简单的写法import requests url https://api.example.com/search params {keyword: python, page: 1, size: 20} resp requests.get(url, paramsparams)这里params参数会自动拼接到URL后面变成https://api.example.com/search?keywordpythonpage1size20。有一个新手经常踩的坑手动拼接URL字符串时如果参数里有特殊字符比如中文、空格、符号不编码的话服务器可能解析不到甚至把请求打废。而用params参数传requests会自动帮你做URL编码所以能用params就少自己拼。响应处理方面核心就三个方法print(resp.status_code) # 状态码比如200、404、500 print(resp.text) # 响应体字符串形式 print(resp.json()) # 如果响应是JSON直接转成Python字典这里有个重要的坑resp.json()如果响应体不是合法的JSON格式会抛出异常。所以如果你的被测接口有可能返回纯文本或者HTML错误页比如Nginx的502页面用resp.json()前最好先try一下或者判断content-type再解析。实测中很多测试脚本在联调阶段一切正常上线后某个接口被网关挡了返回了一个XML格式的错误脚本直接崩溃这种问题排查起来挺费时间的。3.2 POST请求的data、json参数到底怎么选做接口自动化POST请求是绝对主力。requests的POST请求有五种传参方式最容易搞混的是data和jsonimport requests # 方式一表单格式Content-Type: application/x-www-form-urlencoded payload {username: test, password: 123456} resp requests.post(https://api.example.com/login, datapayload) # 方式二JSON格式Content-Type: application/json payload {username: test, password: 123456} resp requests.post(https://api.example.com/login, jsonpayload)两者最大的区别在于请求头Content-Type。用json参数时requests会自动把字典转成JSON字符串并设置Content-Type为application/json用data参数时传字符串会原样发送传字典会编码成表单格式。如果你的接口是标准的Spring Boot后端大概率要求JSON格式。还有一种场景是传文件。比如测试上传头像的接口files {file: open(avatar.png, rb)} resp requests.post(https://api.example.com/upload, filesfiles)用files参数requests会自动构造multipart/form-data格式的请求体这个在日常接口测试中经常用到很多系统的导入功能、文件上传功能都属于这类。再看一个容易忽视的小技巧如果你需要在POST请求里同时传数据和文件requests是允许data和files参数共存的这在某些复杂业务接口里特别实用。3.3 Session会话保持处理登录态和Cookie接口测试最烦的事情之一就是登录态。有些接口不登录不能调用你需要在用例执行前登录一次拿到session id或者token后面的请求都带着它。requests库的Session对象就是为这个场景设计的session requests.Session() # 先登录Session自动保存了服务端下发的Cookie login_data {username: testuser, password: password123} resp session.post(https://api.example.com/login, jsonlogin_data) # 后续请求直接带登录态 resp session.get(https://api.example.com/userinfo) # 如果接口要求的是Token而不是Cookie手动塞进headers也可以之后统一处理 session.headers.update({Authorization: Bearer xxxxx})用Session的优势在于同一个会话内会自动处理Cookie的存取和headers的继承。如果你用原生requests.get每次手动塞Cookie不仅代码冗余而且遇到Cookie在请求过程中被更新的情况你根本不知道什么时候该更新Cookie。Session对象内部是一个urllib3的连接池还会复用底层的TCP连接频繁调接口时速度也更快这是实测下来感知比较明显的一个优化点。3.4 超时设置、重试机制和SSL证书处理很多测试脚本写的太“天真”发个请求不设超时结果被测环境网络抖动一下测试任务挂在那儿半小时不往下走。requests的timeout参数应该养成习惯性写上resp requests.get(https://api.example.com/slow, timeout5)注意timeout传一个值代表建连和读超时都是5秒传元组(3, 10)更精准第一个是连接超时第二个是读取超时。写接口自动化测试时我强烈建议用元组方式因为连接超时和读取超时是两码事一个可能连不上另一个可能是后端处理慢。重试机制在这两年尤其重要。以前测试环境的接口基本稳定重试的紧迫感不强现在在前面加网关、限流的场景越来越多一个请求打过去偶尔就是429或者5xx重试几乎是必须的。urllib3自带了Retry类requests可以通过HTTPAdapter挂载它from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter retry_strategy Retry( total3, # 总重试次数 backoff_factor1, # 退避系数第一次等1s第二次等2s第三次等4s status_forcelist[429, 500, 502, 503, 504], allowed_methods[GET, POST] ) session requests.Session() adapter HTTPAdapter(max_retriesretry_strategy) session.mount(https://, adapter) session.mount(http://, adapter)这套配置翻译成人话就是当请求遇到429或5xx状态码时自动重试最多3次每次重试的间隔按1倍、2倍、4倍递增。这个机制在应对“429 too many requests”这种限流场景时特别有用重试几轮往往就能拿到正常响应了。但重试策略要克制不要盲目把total设得特别大否则接口本来就挂脚本还在那儿一遍遍傻等浪费时间浪费资源。SSL证书问题也经常冒出来。比如你被测环境的HTTPS证书是企业内部自签的requests默认会验证证书导致报错。如果你确认环境安全可控可以加上verifyFalse临时跳过验证。但要压住这个异常避免被warning刷屏import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) resp requests.get(https://self-signed.example.com/api, verifyFalse)如果被测接口有正式的CA证书更推荐传verify参数指定证书路径verify/path/to/ca.pem这样既校验了证书又不会报错。4. 接口自动化测试框架怎么从零搭起来4.1 从“用requests调接口”到“框架”还差什么很多人写过几个requests脚本就把自己做的事叫“框架”。我的理解里一个能落地的接口自动化测试框架至少要满足下面这几个条件测试用例可维护、测试数据可配置、执行结果可报告、失败原因可排查。具体到代码设计层面就是分层请求层封装requests的get/post方法统一处理超时、重试、日志和异常捕获。接口层把每个被测接口定义为独立的方法参数化输入返回响应对象和解析后的业务数据。用例层用pytest编写具体测试场景组织入参和预期结果调用接口层的方法完成断言。数据层把测试用例的输入数据和预期结果外置到Excel、YAML或者JSON里避免改用例改代码。加上公共的配置管理环境地址、账号密码、数据库连接串和日志记录这个框架才算“能拿出去用”。下面我拆开讲每个部分怎么实现。4.2 封装一个通用的请求客户端这个封装的核心思路是把所有requests调用的共性逻辑收敛到一个模块里之后所有接口测试用例都不直接碰requests库只跟这个封装类打交道。import time import logging import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter logger logging.getLogger(api_client) class ApiClient: def __init__(self, base_url, timeout10, max_retries3): self.base_url base_url.rstrip(/) self.timeout timeout self.session requests.Session() retry_strategy Retry( totalmax_retries, backoff_factor1, status_forcelist[429, 500, 502, 503, 504], allowed_methods[GET, POST, PUT, DELETE] ) adapter HTTPAdapter(max_retriesretry_strategy) self.session.mount(https://, adapter) self.session.mount(http://, adapter) def request(self, method, path, **kwargs): 统一请求入口 url self.base_url path kwargs.setdefault(timeout, self.timeout) # 记录请求信息 logger.info(f[REQUEST] {method.upper()} {url} params{kwargs.get(params)} json{kwargs.get(json)} data{kwargs.get(data)}) start time.time() resp self.session.request(method, url, **kwargs) cost round((time.time() - start) * 1000, 2) logger.info(f[RESPONSE] status{resp.status_code} cost{cost}ms body{resp.text[:500]}) return resp def get(self, path, **kwargs): return self.request(GET, path, **kwargs) def post(self, path, **kwargs): return self.request(POST, path, **kwargs) def put(self, path, **kwargs): return self.request(PUT, path, **kwargs) def delete(self, path, **kwargs): return self.request(DELETE, path, **kwargs)这样封装的好处第一是统一了请求出口加日志、加耗时统计、加重试都是一处改动全网生效。第二是测试用例的代码干净了只需要关注接口的业务参数。在排查一个“偶发超时”问题时这个日志帮过大忙——通过观察所有请求的平均耗时和重试次数能快速定位是哪张表慢查询拖垮了接口而不是无头苍蝇一样试接口。4.3 用pytest写用例断言的艺术pytest组织用例完全不需要复杂机制就是写函数加标记import pytest from api_wrapper import ApiClient client ApiClient(https://api.example.com, timeout5) def test_get_user_info_success(): 正常获取用户信息 resp client.get(/user/10086) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][id] 10086 assert body[data][username] is not None assert isinstance(body[data][email], str)断言这块要注意的是要“断现状”不断“断实现”。比如你断言body[code] 0这是业务协议层面的约定没问题。但如果你断言一个ID字段永远等于某个固定值下次数据一变用例就挂了这种断言毫无价值。建议只断三类内容状态码接口是否可用。业务码业务逻辑是否正确执行。关键字段非空、类型、长度、取值范围。另外一个实用思路是“断言独立于请求参数”的通用校验比如所有接口的响应不能是HTML错误页、不能包含PHP警告信息、响应时间不能超过3秒。这种通用规则写到pytest的conftest.py里做autouse fixture每个用例执行后自动校验非常省心pytest.fixture(autouseTrue) def check_response_sanity(record_testsuite_property): yield if resp_holder[response] is not None: resp resp_holder[response] assert resp.status_code ! 502, 网关错误 assert Warning: not in resp.text, 响应包含PHP警告如果团队规模大一点还可以引入数据驱动用pytest的parametrize装饰器直接驱动多组测试数据import pytest test_data [ (valid_user, test001, 123456, 0), (invalid_pwd, test001, wrong, 1001), (nonexist_user, ghost, 123456, 1002), ] pytest.mark.parametrize(case_name,username,password,expected_code, test_data) def test_login(case_name, username, password, expected_code): resp client.post(/login, json{username: username, password: password}) body resp.json() assert body[code] expected_code这样做的好处是增加一条测试数据只需要往列表里加一个元素测试代码本身不需要动。实际项目里这个列表通常是从Excel或YAML文件里读出来的测试人员和开发人员只要维护数据文件就行完全不看代码。4.4 数据驱动Excel维护用例数据当接口多到一定程度把测试数据和代码耦合在一起会非常痛苦。业务的同学想加一条用例要翻代码开发一改字段名测试全红这种痛苦经历过的人都懂。用openpyxl读Excel把每行当成一条测试用例列是接口路径、请求方式、请求参数、预期状态码、预期业务码等from openpyxl import load_workbook def load_test_cases_from_excel(file_path, sheet_nameSheet1): wb load_workbook(file_path) ws wb[sheet_name] cases [] for row in ws.iter_rows(min_row2, values_onlyTrue): case { case_name: row[0], method: row[1], path: row[2], params: eval(row[3]) if row[3] else {}, json: eval(row[4]) if row[4] else {}, expected_status: row[5], expected_code: row[6], } cases.append(case) return cases这里我用了eval把Excel里的字符串还原成Python对象这是图省事的写法。生产环境建议用json.loads代替eval因为eval有安全隐患如果Excel被他人恶意篡改脚本可能执行任意Python代码。我自己在写工具类的时候也踩过这个坑后来统一改用json解析虽然录入数据时要求严格一点但安全性和可靠性高很多。然后pytest里把读出来的用例数据当成parametrize的入参集合一条Excel记录就是一条自动执行的测试用例。配合人员分工测试执行同学只需要维护Excel完全不需要碰代码。这是接口自动化能长期落地而不是走样成“自娱自乐”的关键。4.5 测试报告生成allure和pytest-html写接口自动化测试报告是给领导看、给其他人用的。什么都不生成每次跑完只有一片绿色的pytest输出说服力大打折扣。pytest-html是最快能出成果的方案pip install pytest-html pytest --htmlreport.html --self-contained-html--self-contained-html会把CSS嵌入HTML文件方便直接发给别人打开不需要依赖网络。生成的报告里有测试通过率、每个用例的持续时间、失败原因和traceback。如果团队对报告颜值和交互有要求allure更值得投入。它生成的是带分类、带历史趋势、带失败重试记录的专业报告。流程分两步pip install allure-pytest pytest --alluredirallure-results然后合并结果并生成HTML展示allure generate allure-results -o allure-report --clean allure open allure-reportallure报告的漂亮之处在于每个用例的请求和响应报文会自动收集到step里前提是你在用例里把requests调用包装了一层并记录日志失败的时候一眼就能看到是入参错了、断言没匹配上还是响应超时省去了翻日志的麻烦。4.6 把用例接入定时任务和CI自动化测试最大的价值在于它“自动跑”。不接入定时调度和持续集成人工每天执行一遍那跟手工测试的区别就是把手动点击换成了跑脚本效率提升有限。最简单的定时方式是利用操作系统的cronLinux/Mac或任务计划程序Windows固定每天凌晨执行一次然后把allure报告部署到内网服务器上团队早上来打开网页看结果就行。更进一步的做法是接CI平台比如Jenkins或GitLab CI在代码合并到主干时自动触发测试集有问题直接阻断发布流程。这个思路的完整链路是开发提交代码触发CI任务的构建。构建完成后自动拉取接口测试仓库跑pytest。测试不通过则切片日志和allure报告发给相关人员。测试通过则继续后续部署流程。工作中实测下来定期巡检接口比人工用Postman偶尔点一遍要靠谱太多。有一次凌晨某云服务商的一个下游接口悄悄变更了返回参数格式我们的定时任务在当天早上5点就通过邮件报警发现了等业务同学上班来反馈“这里怎么挂了”的时候问题早就已经定位完了。这就是自动化带来的实实在在的安心感。5. 接口测试场景里那些高频坑和排查方法5.1 限流与429 too many requests最近遇到类似报错越来越多exceeded retry limit, last status: 429 too many requests。这个报错的本质是服务端对你的请求做了限流常见触发场景就是你在短时间内高频请求接口。热词里反复出现429说明很多人都在跟限流做斗争。429的处理思路不同场景不同方案触发场景应对策略测试环境单个账号高频调用被接口层的RateLimiter限流多个测试账号轮换使用接口本身有QPS上限比如20次/秒用time.sleep在请求间加间隔或者用一个带间隔的调度器网关层如Nginx默认开启了漏桶限流请求间退避重试加上面配置的Retry机制并发测试时打得太猛降低并发数或者错峰启动线程如果确认是测试环境自己触发的限流最直接的方案是加sleep。比如跑数据准备脚本的时候给每个请求之间加0.2秒的间隔加上重试机制基本不会再触发限流。有一种错误做法是遇到429就把重试次数调大调成10次20次这样限流没解除反而因为频繁重试可能把账号拉黑最后代价更大。另外每个请求之间是不是必须sleep不一定。HTTP长连接复用的情况下同一个session连续发送请求的资源开销很小限流主要限制的是短时间内的连接数量和频率。拿捏的原则是能复用session就复用session该sleep就sleep并发测试这种高压力场景直接交给压测工具别在功能自动化里堆并发。5.2 requests响应乱码和编码问题中文乱码是所有做接口测试的人都会遇到的恶心事。接口返回的JSON里包含中文说明信息打印出来是一堆\uXXXX或者直接显示成问号乱码。常见处理思路# 手动指定文本编码 resp.encoding utf-8 text resp.text # 或者直接处理字节流再解码 raw_text resp.content.decode(utf-8) # 如果JSON中包含unicode转义符直接json.loads即可自动解析 body resp.json()\uXXXX这类不是乱码只是Python在repr时会把非ASCII字符显示成转义序列直接print到日志里看着像乱码。实际上这是合法的JSON表示方式json.loads之后已经是正常的中文字符串了。如果确实需要在日志里以可读形式输出处理方式是import json print(json.dumps(body, ensure_asciiFalse, indent2))如果resp.text打印出来直接是问号或者奇怪的方块符号大概率是响应体用的编码不是UTF-8而是GBK、GB2312或者Latin-1。这时候用resp.content拿到原始字节流再手动指定正确的解码方式即可。5.3 pip安装库时的网络与版本冲突热词里有一条很典型c:\users\lenovopip install requests defaulting to user installation because...。前面提到过这是pip的用户级安装逻辑但如果你确实需要装到系统级site-packages比如项目部署环境要求统一管理包可以加上--user参数显式指定或不加参数强制系统安装pip install --user requests版本冲突是另一个常见问题。比如你的项目里某个依赖库要求urllib3必须是1.26.x但另一个库强依赖2.xpip会尝试自动解析解析不了就直接报错。这时最好的方案是查清楚到底是谁依赖了谁用pip show urllib3查看当前版本用pip list查看已安装的包依赖关系。实在解决不了就开一个干净的虚拟环境一个一个装依赖装一个测一个快速定位冲突源。5.4 避免假阳性环境差异和断言脆弱性接口自动化测试最怕的不是报错而是“假装成功”。有次跑完测试一看180个用例全绿结果实际上被测环境网络断了requests库的请求超时后重试了几次中间有一次成功了最终响应是一个502页面——但是我们的断言只验证了状态码是200根本没检查响应内容于是一条本来应该失败的用例就这样“成功”了。从那以后我的封装类里多了一条规则请求返回后先校验响应体是否包含关键资产字段比如success、data这些业务标记再进行后续断言。或者反过来断言里设置“禁止通配符”比如断言响应体不得以html开头这样即使状态码是200如果返回的是HTML错误页也会被拦截下来。另一个假阳性的来源是测试环境的数据污染。比如同样一条新增用例第一次跑通过第二次跑失败这是因为第一条用例已经写入了一条数据重复插入触发了唯一键冲突。解决办法是采用“测试数据自带前缀”的原则每个用例模块创建一个独立的测试账号和独立测试数据并在用例结束时做数据清理teardown。或者用pytest的fixture作用域来隔离函数级的fixture比模块级fixture隔离更彻底代价是执行时间稍长。5.5 超时时间配置的取舍超时时间设短了接口业务复杂处理超过阈值时会误报失败设长了一旦接口挂掉脚本要等很长时间才响应。我的经验值是读超时看接口业务复杂度普通查询接口设5~10秒导入导出接口、报表接口设30~60秒连接超时统一3秒因为连不上就说明服务有问题没必要等。如果是批量跑几百条用例在脚本里全局控制超时时间同样重要。估算总耗时的公式大致是用例数 × 平均单接口耗时 × 重试概率。假设100条用例平均耗时0.5秒遇到5%的接口需要重试每次重试指数退避124秒总耗时可能在50秒到100秒之间浮动。定时任务的时间窗口要留出至少这个估算值的2倍余量不然会出现在任务还没跑完时被下一次定时任务重复触发的混乱。6. 一个完整的实际操作流程从写脚本到跑出第一份报告为了把前面这些内容串起来我给你一条完整的可操作路径照着走一遍就能看到全貌。6.1 项目目录搭建建议按下面的结构组织代码api-test/ ├── config/ │ └── settings.py # 环境地址、测试账号等全局配置 ├── api/ │ ├── __init__.py │ ├── client.py # ApiClient封装类 │ ├── user_api.py # 用户相关接口定义 │ └── order_api.py # 订单相关接口定义 ├── testcases/ │ ├── conftest.py # pytest的fixture和钩子 │ ├── test_login.py │ └── test_user.py ├── data/ │ └── test_login_data.xlsx ├── report/ # 测试报告输出目录 ├── logs/ # 运行日志 ├── requirements.txt └── pytest.ini这个结构的分工很明确config放配置api放接口层封装testcases放测试用例data放测试数据report和logs放产物。只要坚持这个分层项目规模扩大到几百个接口也不会乱。6.2 定义接口层以用户模块为例# api/user_api.py from .client import ApiClient class UserApi: def __init__(self, client: ApiClient): self.client client def login(self, username, password): return self.client.post(/login, json{ username: username, password: password }) def get_user_info(self, user_id): return self.client.get(f/user/{user_id}) def update_user_email(self, user_id, email): return self.client.put(f/user/{user_id}, json{email: email})这里把接口动作抽成了类的普通方法好处是以后接口参数变化时只需改这个文件测试用例层不用动。如果你的接口数量特别多还可以按模块拆成UserApi、OrderApi、PaymentApi每个模块一个文件。6.3 conftest.py统一fixturepytest的conftest.py是共享fixture的地方。在这里初始化客户端和实例化接口对象import pytest from api.client import ApiClient from api.user_api import UserApi pytest.fixture(scopesession) def base_url(): from config.settings import BASE_URL return BASE_URL pytest.fixture(scopesession) def client(base_url): return ApiClient(base_url, timeout10, max_retries3) pytest.fixture(scopesession) def user_api(client): return UserApi(client)scopesession意味着整个测试会话只初始化一次客户端。这里有一个容易踩的坑Session对象的连接池复用确实快但如果被测服务的IP会变化比如测试环境重启后IP变了复用的旧连接实际已经失效请求会报ConnectionError。此时可以在封装类里加入连接失效后的自动重建逻辑except requests.exceptions.ConnectionError: logging.warning(连接失败重建session重试一次) self.session.close() self.session requests.Session() # 重新挂载重试adapter然后retry一次6.4 测试用例编写# testcases/test_user.py import pytest def test_login_success(user_api): resp user_api.login(test001, 123456) assert resp.status_code 200 body resp.json() assert body[code] 0 assert token in body[data] def test_get_user_info(user_api): resp user_api.get_user_info(10086) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][id] 10086如果你不想写死用户ID也可以先调用登录接口拿到token再动态获取用户IDdef test_get_user_info_dynamic(user_api): login_resp user_api.login(test001, 123456) user_id login_resp.json()[data][user_id] resp user_api.get_user_info(user_id) assert resp.status_code 200 assert resp.json()[data][id] user_id这算是一个比较典型的业务联动场景后续接口的参数依赖前一个接口的返回值。再复杂一点的场景比如下单接口需要商品ID、库存和价格字段都从多个接口获取pytest的fixture也能串联起来。保持每个fixture只做一件事情的粒度可以避免用例之间的关系变成一团乱麻。6.5 跑完出报告项目根目录执行pytest testcases -s -v --htmlreport/report.html --self-contained-html或者用allurepytest testcases -s -v --alluredirreport/allure-results allure generate report/allure-results -o report/allure-report --clean allure open report/allure-report第一次跑通之后你会感受到这套组合拳的威力。修改接口字段、增加新用例、调试失败用例都比你直接用原生requests写一堆零散脚本要舒服太多。7. 接口自动化测试的下一步扩展方向当你把前面这套基础框架跑顺了以后有几个方向可以继续深化。第一个方向是契约测试。现在很多团队都在搞前后端分离前端和后端各有一套接口文档。requests可以配合openapi规范做契约校验每次后端接口变更自动同步接口定义与测试数据避免文档和实现脱节。这个方向的价值在于它把接口自动化从“测实现”提升到“测契约”前端依赖的字段后端一改接口测试马上就能发现。第二个方向是流量录制与回放。生产环境的真实请求流量是最宝贵的测试数据来源。思路是通过网关或日志采集真实用户请求过滤后回放到测试环境配合requests模拟发出请求验证新版本接口行为是否与生产版本一致。这个思路比手工构造用例覆盖更全面尤其适合老系统重构时的回归测试。第三个方向是测试数据工厂。接口自动化对测试数据的需求往往比想象中更复杂比如注册新用户、创建新订单、造优惠券这些前置数据每次重复调用业务接口去准备太慢。更好的方案是用requests直连内部管理接口或数据库工具类来批量造数据把造数能力沉淀成独立模块。这样用例脚本本身专注于验证逻辑而不是花大部分时间在准备数据上。第四个方向是性能与功能结合。requests虽然不适合做高并发压测但可以用它快速验证某个接口在给定并发下的响应时间变化趋势。比如用线程池模拟20个用户同时刷新列表页观察接口的耗时时长是否显著上涨如果上涨明显说明后端的连接池、缓存或数据库层面存在瓶颈。这个思路适合功能自动化团队排查性能前兆真到压测环节还是要上专业的压测工具。从我个人的体会来看接口自动化测试这座冰山requests只是浮出水面的那一小块。真正的价值在于你想清楚这套体系需要解决什么业务问题、如何组织用例和数据、如何在团队里推广和保持用例的健康度。技术选型反而没那么复杂Pythonrequests框架的组合对大多数团队来说已经是性价比最高的起点。如果你还在手工点接口不妨从今天这个框架开始动手先跑通一遍流程再逐步往更高级的方向扩展。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑