Pytest+Requests从零搭建接口自动化测试框架完整指南

发布时间:2026/9/3 22:57:59
Pytest+Requests从零搭建接口自动化测试框架完整指南 做接口测试这么多年我发现一个很有意思的现象很多人用 Postman 调接口特别熟练一说到自动化就不知道从哪儿下手。网上教程不少但大部分只教你怎么用 Requests 发一个 GET 请求然后打印一下返回值就结束了。等你真到了工作中发现项目几十个接口、几十套测试数据、还要出测试报告那点代码根本撑不住。这就是为什么很多人买了课、看了视频最后还是不会搭接口自动化框架。不是 Pytest 难也不是 Requests 难而是缺少一条从“会发请求”到“工程化落地”的完整链路。本文想帮你一次打通这条链路。我会从零开始带你搭建一个基于 Pytest Requests 的接口自动化测试框架包含环境准备、用例编写、数据分离、Fixture 管理、报告生成和常见问题排查。全程围绕一个真实场景用户注册 登录 获取用户信息。学完你不仅能跑通 Demo还能把它用到自己的项目里。1. 为什么接口自动化框架值得自己搭一遍先聊一个底层问题接口自动化测试的核心到底是什么有人说是 Requests 库有人说是断言有人说是持续集成。我的判断是核心是测试用例的工程化管理能力。Requests 只是个发请求的工具Pytest 才是让用例能够组织、运行、统计、报告的框架。二者合在一起才能形成一套能复用的自动化体系。如果你只是临时调试一个接口Postman 完全够用。但当你遇到下面这些场景手工工具就不够了新版本上线前需要一个命令跑完几百条接口用例接口返回值变动你要在几分钟内定位到是哪条用例挂了开发改了字段名你要知道除了功能测试还有多少自动化用例需要同步更新领导要看测试报告你不能只截一段控制台日志。这些场景背后体现的是接口自动化的三层能力能用代码发请求、能用框架组织用例、能持续支撑项目迭代。Pytest Requests 这个组合恰恰是这三层能力里学习成本最低、生态最成熟的一条路径。另外Pytest 还有一个天然优势它的断言就是 Python 原生的assert不需要像 JUnit 那样记一堆assertEquals、assertTrue之类的 API。这对 Python 新手足够友好对老手来说又足够灵活。所以这篇教程不是单纯教“怎么用 Requests 调接口”而是带你搭一个真正能落地到项目里的最小框架。你学会之后可以把它套用到任意 HTTP 接口项目上。2. Pytest 与 Requests 的核心概念与适用场景在写代码之前先把两个主角讲清楚。很多人卡住不是因为代码不会写而是因为对框架的运作方式没有概念。2.1 Requests把 HTTP 请求变成三行代码Requests 是 Python 里最常用的 HTTP 请求库。它的价值在于把 HTTP 协议的那些细节封装成了直观的 Python 方法。import requests resp requests.get(https://httpbin.org/get) print(resp.status_code) print(resp.json())这段代码做的事情在底层相当于你用浏览器地址栏输入了一个网址并回车。但在自动化测试里你还需要设置请求头、请求体、超时时间、代理等参数。Requests 的核心操作就是五类请求方法get、post、put、delete、patch。加上统一的参数结构url、headers、params、json、data、timeout基本能覆盖日常接口测试的大部分场景。2.2 Pytest把散落的测试代码变成测试工程Pytest 是一个 Python 测试框架。它的核心价值有三点自动发现测试用例只要文件命名为test_*.py函数命名为test_*Pytest 就能自动收集并运行。Fixture 管理机制用pytest.fixture处理前置准备和清理工作比如创建测试数据、登录获取 token。丰富的插件生态Allure 报告、xdist 并发、依赖控制等都有现成插件。很多人把 Pytest 理解成“一个能跑测试的工具”其实它是“一套测试工程的标准”。有了它你不需要自己写测试器、收集器、报告器只需要关心测试用例本身。2.3 两者结合之后的框架边界Pytest Requests 的常见分工如下职责工具发送 HTTP 请求Requests测试用例组织和执行Pytest前置数据准备和清理Pytest Fixture断言校验Python 原生 assert测试报告Allure、pytest-html参数化批量测试Pytest 参数化数据驱动YAML、JSON、Excel 配合 Pytest需要提醒的是Requests 本身不是测试框架它不负责“这条用例过了没有”。Pytest 也不是 HTTP 客户端它不知道接口协议细节。只有两者配合才构成接口自动化测试框架。2.4 新手最容易误解的点很多人会问是不是学会了 Pytest Requests就不用学 Postman 了不是的。Postman 适合做接口调试和手工验证Pytest Requests 适合做自动化回归。两者是互补关系。你在 Postman 里调试通过的接口把参数和请求头搬到自动化用例里这个过程不是简单的“翻译”而是要把接口的输入、输出、依赖关系重新梳理一遍。这恰恰是接口自动化最花时间的地方也是它最有价值的地方。3. 环境准备与前置条件下面开始实操。先准备好环境这一步做不好后面所有代码都可能出现奇怪的问题。3.1 基础环境清单本次搭建使用以下环境配置操作系统Windows 10/11、macOS 或 Linux 都可以本文命令以 Windows 示例macOS 和 Linux 把python -m venv的用法保持一致即可Python 版本建议 3.9 及以上请以你本机实际安装版本为准本文演示的是通用思路包管理工具pip配合虚拟环境使用IDE推荐 PyCharm 或 VS Code。3.2 创建虚拟环境虚拟环境的核心价值是隔离项目依赖。不同项目可能依赖不同版本的 Requests 或 Pytest如果不隔离很容易出现“这个项目能跑那个项目一装就冲突”。打开终端进入你的项目目录mkdir pytest_requests_demo cd pytest_requests_demo python -m venv venv激活虚拟环境# Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活成功后命令行提示符会出现(venv)前缀。这一步很关键如果后面安装依赖后仍然提示找不到模块绝大多数情况下都是因为虚拟环境没有激活。3.3 安装依赖pip install pytest requests pytest-html如果你后面想用 Allure 报告还需要额外安装 Allure 命令行工具这个放到报告章节再讲。安装完成后可以用下面的命令验证pytest --version能输出 Pytest 版本号说明环境已经就绪。4. 核心流程拆解在写完整示例之前先拆解接口自动化框架的核心流程。理解了流程代码就只是流程的翻译。一个完整的接口自动化执行流程如下准备测试数据读取配置文件、准备测试账号、构造请求参数。执行前置操作例如先注册用户再登录获取 token。发送 HTTP 请求调用 Requests 对应方法传入 URL、请求头、请求体。接收响应获取状态码、响应头、响应体。断言校验判断响应是否符合预期。输出结果收集测试通过/失败数据生成报告。清理数据删除测试产生的临时数据避免影响下次执行。其中第 1 步和第 2 步在 Pytest 中通常用 Fixture 实现第 3 步和第 4 步用 Requests 实现第 5 步用assert实现第 6 步用 Pytest 插件实现第 7 步也用 Fixture 的 teardown 逻辑实现。这个流程清晰了后面的代码就只是落地而已。5. 从最小用例到完整框架我建议你不要一上来就写一堆封装类。先写一个最小用例跑通流程再一步步改造这样出问题时你能明确知道是哪一层出了问题。5.1 第一个最小测试用例在项目目录下创建test_demo.py# 文件路径test_demo.py import requests def test_get_user_list(): url https://httpbin.org/get params {page: 1, size: 10} resp requests.get(url, paramsparams, timeout10) assert resp.status_code 200 assert resp.json()[args][page] 1运行pytest test_demo.py -v预期输出包含PASSED。这个用例本身很简单但它已经包含了接口用例的三个核心要素请求、响应、断言。5.2 工程化第一步把配置抽离出来真实项目里接口地址、超时时间、环境标识不应该硬编码在测试用例里。否则切换环境时你需要在几百条用例里做全局替换。在项目目录下创建config.py# 文件路径config.py # 公共配置按环境区分时可扩展为读取环境变量 BASE_URL https://httpbin.org TIMEOUT 10 HEADERS { Content-Type: application/json, User-Agent: pytest-requests-demo/1.0, }创建api_client.py封装 Requests 请求# 文件路径api_client.py import requests from config import BASE_URL, TIMEOUT, HEADERS class ApiClient: 对 Requests 的简单封装统一处理请求头、超时和异常 def __init__(self, base_urlBASE_URL, headersNone): self.base_url base_url.rstrip(/) self.session requests.Session() self.session.headers.update(HEADERS) if headers: self.session.headers.update(headers) def get(self, path, paramsNone): url f{self.base_url}/{path.lstrip(/)} return self.session.get(url, paramsparams, timeoutTIMEOUT) def post(self, path, jsonNone, dataNone): url f{self.base_url}/{path.lstrip(/)} return self.session.post(url, jsonjson, datadata, timeoutTIMEOUT)这里用到了requests.Session它能保持连接池和请求头状态。多次请求同一个服务器的场景下Session 比每次新建requests.get更高效也更接近真实业务调用方式。5.3 工程化第二步用 Fixture 管理前置操作接下来模拟一个更真实的场景先注册用户再登录获取 token最后用 token 获取用户信息。创建conftest.py# 文件路径conftest.py import pytest from api_client import ApiClient from config import BASE_URL pytest.fixture(scopesession) def api_client(): 提供一个全局可复用的 API 客户端实例 client ApiClient(base_urlBASE_URL) yield client pytest.fixture(scopesession) def auth_token(api_client): 注册并登录返回登录后携带的 token username test_user_001 password 123456 # 注册 register_payload { username: username, password: password, email: f{username}example.com, } register_resp api_client.post(/register, jsonregister_payload) assert register_resp.status_code in (200, 201), 注册失败 # 登录 login_payload {username: username, password: password} login_resp api_client.post(/login, jsonlogin_payload) assert login_resp.status_code 200, 登录失败 token login_resp.json().get(token) assert token, 登录响应中没有 token 字段 yield token # 后置清理删除测试用户 delete_resp api_client.post(/delete_user, json{username: username}) assert delete_resp.status_code 200, 清理测试用户失败这里有几个容易踩坑的点Fixture 的scopesession表示整个测试会话只执行一次登录而不是每条用例登录一次。这对测试效率很重要。后置清理写在yield之后即使测试用例失败Pytest 也会尽量执行 teardown 逻辑。登录和注册的接口路径需要根据你实际项目的接口调整这里使用的是通用路径示意。5.4 工程化第三步编写业务测试用例创建test_user_flow.py# 文件路径test_user_flow.py import pytest def test_get_user_info_with_token(api_client, auth_token): 使用登录 token 获取用户信息验证接口鉴权链路是否正常 headers {Authorization: fBearer {auth_token}} # 实际项目中 token 一般通过 api_client 封装中的 headers 传递 resp api_client.get(/user/info, params{userId: 1}) assert resp.status_code 200 body resp.json() assert body[code] 0, 业务返回码不为 0 assert body[data] is not None, 用户数据为空 def test_get_user_info_without_token(api_client): 未携带 token 时接口应返回 401 或业务错误码 resp api_client.get(/user/info, params{userId: 1}) assert resp.status_code 401这里只演示了最基本的写法。如果你的请求头需要动态携带 token建议在auth_tokenFixture 中直接把 client 的请求头更新好而不是在每条用例里手动加 headers。改进版pytest.fixture(scopesession) def auth_client(api_client, auth_token): 带登录态的 API 客户端 api_client.session.headers.update({Authorization: fBearer {auth_token}}) return api_client这样业务用例只需要传auth_client不需要关心 token 是怎么塞进请求头的。5.5 工程化第四步参数化与数据驱动真实项目中一个接口要测多组数据。比如登录接口至少要覆盖正常密码、错误密码、空用户名、空密码、不存在用户等情况。如果每组数据写一条用例代码冗余且难以维护。使用 Pytest 的参数化功能# 文件路径test_login_param.py import pytest from api_client import ApiClient pytest.mark.parametrize( username,password,expected_code,expected_msg, [ (valid_user, 123456, 200, success), (valid_user, wrong, 400, invalid password), (, 123456, 400, username required), (valid_user, , 400, password required), ] ) def test_login_cases(api_client, username, password, expected_code, expected_msg): resp api_client.post(/login, json{username: username, password: password}) assert resp.status_code expected_code body resp.json() assert body[code] expected_code assert expected_msg in body[message]如果你想做到数据和代码完全分离可以把测试数据放在data/login_cases.json中然后用json.load读取再传给parametrize。这就是所谓的数据驱动。创建data/login_cases.json[ { username: valid_user, password: 123456, expected_code: 200, expected_msg: success }, { username: valid_user, password: wrong, expected_code: 400, expected_msg: invalid password }, { username: , password: 123456, expected_code: 400, expected_msg: username required } ]然后在测试文件中读取# 文件路径test_login_data_driven.py import json import pytest from api_client import ApiClient def load_login_cases(): with open(data/login_cases.json, encodingutf-8) as f: return json.load(f) pytest.mark.parametrize( case, load_login_cases(), idslambda case: f{case[username]}-{case[expected_code]} ) def test_login_data_driven(api_client, case): resp api_client.post(/login, json{ username: case[username], password: case[password], }) body resp.json() assert resp.status_code case[expected_code] assert case[expected_msg] in body[message]5.6 工程化第五步pytest.ini 与日志配置pytest.ini是 Pytest 的配置文件可以指定测试路径、过滤规则、命令行参数等。创建pytest.ini[pytest] testpaths test_cases python_files test_*.py python_classes Test* python_functions test_* addopts -v -s --tbshort --strict-markers这个配置的含义testpaths指定测试用例目录避免 Pytest 去扫描无关文件python_files指定测试文件命名规则addopts默认追加的命令行参数-v显示详细结果-s显示 print 输出--tbshort缩短错误堆栈。另外接口测试过程中生产级别的日志很重要。建议加一个logger.py统一日志格式但注意不要把所有请求和响应都打出来避免日志文件过度膨胀。关键信息打出来即可请求路径、状态码、耗时、业务码。6. 运行与效果验证框架搭好之后运行方式要清晰。6.1 运行全部用例pytest预期输出类似collected 8 items test_user_flow.py::test_get_user_info_with_token PASSED test_user_flow.py::test_get_user_info_without_token PASSED test_login_data_driven.py::test_login_data_driven[valid_user-200] PASSED ...6.2 生成 HTML 报告使用 pytest-html 插件pytest --htmlreport.html --self-contained-html--self-contained-html参数会把 CSS 和 JavaScript 内嵌到 HTML 文件中方便直接发送给别人查看。生成后项目目录下会出现report.html用浏览器打开即可看到用例统计、失败详情、耗时等信息。6.3 生成 Allure 报告如果你所在团队对测试报告要求更高可以使用 Allure。第 1 步安装 Allure 命令行工具和 pytest-allure-adapter。pip install allure-pytestAllure 命令行工具需要单独安装不同操作系统方式不同请参考 Allure 官方文档进行安装。第 2 步运行用例并生成 result 文件。pytest --alluredirallure-results第 3 步生成并打开报告。allure serve allure-resultsAllure 报告比 pytest-html 更细腻支持按功能模块分组、历史趋势对比、失败重试信息等。到了大项目阶段建议优先使用 Allure。6.4 如何判断框架跑通了判断标准很简单用例能自动发现并执行成功用例显示 PASSED失败用例显示 FAILED失败用例能看清是请求失败、状态码不符还是响应体断言不符报告能正常生成更换环境时只需要修改配置不需要改用例。只要这五条成立你的框架就已经具备最基本的落地能力。7. 常见问题与排查思路下面这些问题是接口自动化项目里最高频的几类。我把它们按“现象-原因-排查-解决”整理成表方便你收藏后查阅。7.1 高频问题排查表问题现象可能原因排查方式解决方案运行时提示ModuleNotFoundError: No module named requests虚拟环境未激活或依赖安装到了其他环境执行pip list查看是否有 requests激活当前项目虚拟环境后重新安装依赖接口返回 404接口路径拼接错误或 base_url 被重复拼接打印实际请求 URL检查 ApiClient 中的 url 拼接逻辑去掉重复的/接口返回 401没有携带 token或 token 已过期查看请求头中是否有 Authorization检查 token Fixture 的作用域和传递方式必要时在请求前强制刷新 token用例之间相互影响Fixture 没有正确设置作用域或测试数据冲突单独运行一条用例确认是否通过使用函数级 Fixture 隔离数据或清理测试数据断言报错但不知道响应内容测试代码未打印响应体临时加print(resp.text)或用-s运行在断言前封装一个统一的日志输出运行结果中文乱码控制台编码不是 UTF-8检查终端编码设置Windows 执行chcp 65001或代码中设置输出编码调用第三方接口频繁报429 too many requests请求频率超出接口限流策略查看响应头中的Retry-After字段在请求封装中加入重试机制并且尊重限流要求避免造成对目标服务的压力切换环境后部分用例仍访问旧环境配置被硬编码在用例中全局搜索域名或 IP所有环境相关信息收拢到 config 或配置文件7.2 一个典型的定位思路如果你遇到一条用例失败不要直接改代码。先按这个顺序定位单独运行这条用例确认是否能稳定复现用-s参数运行查看打印的请求 URL、请求头和响应体对比 Postman 中手工请求成功的数据检查是参数问题、数据问题、环境问题还是断言条件问题修复后重新运行并跑一遍全量用例确保没有影响其他用例。8. 最佳实践与工程建议框架能跑通只是第一步。真正考验人的是框架在团队和项目中的长期维护。下面这些建议来自实际项目中的长期迭代经验。8.1 用例命名规范要统一接口测试用例建议统一命名规则让人一眼看出在测什么。推荐格式test_模块_接口_场景例如test_user_login_successtest_user_login_wrong_passwordtest_user_get_info_without_token这样看报告时即使不查代码也知道是哪条链路的哪个场景出了问题。8.2 数据与代码分离测试数据不要硬编码在用例中。建议按以下目录组织project/ ├── api_client.py ├── config.py ├── conftest.py ├── data/ │ ├── login_cases.json │ └── user_cases.yaml ├── test_cases/ │ ├── test_login.py │ └── test_user.py ├── pytest.ini └── requirements.txt数据分离的好处是测试人员不需要是 Python 专家也能维护测试数据。擅长写代码的人负责框架和封装擅长测试的人负责补充数据和场景。8.3 环境切换要设计好实际项目中往往有 dev、test、staging、prod 等多套环境。推荐用环境变量控制# config.py import os BASE_URL os.getenv(API_BASE_URL, https://httpbin.org)运行时动态指定环境set API_BASE_URLhttps://test-api.example.com pytest这样就避免了测试代码里散落大量环境域名。8.4 token 管理要讲究很多接口需要登录 token而 token 有过期时间。建议在 Fixture 中做以下设计使用scopesession的 token Fixture减少重复登录在请求封装中统一设置 Authorization 头不要在每条用例里手动加token 过期时执行一次重新登录逻辑而不是让整轮测试全部失败不要把真实生产环境的 token 提交到代码仓库中。8.5 请求要有超时和重试策略Requests 请求不加timeout在网络异常时可能一直等待下去。建议在 ApiClient 中统一设置timeout并在适当场景下加入重试机制。注意重试要控制次数还要考虑接口的幂等性。如果接口本身不是幂等操作重试可能会造成重复下单、重复注册等问题。这是接口自动化比较容易忽略的一个风险点。8.6 安全与权限边界这一条非常重要。接口自动化框架运行在测试环境时接触的数据往往是测试数据问题不大。但一旦接到生产环境或含有真实用户数据的环境安全和合规问题就必须重视。建议遵守以下原则最小权限原则测试账号只授予测试所需的权限不要用管理员账号跑所有用例敏感信息不入库数据库密码、token 密钥等不要硬编码在代码或配置里使用环境变量或专门的配置中心管理销毁测试数据测试产生的用户、订单等数据用后即清理涉及删除、修改类操作时先在测试环境验证再进行后续操作不要把测试网络的扫描、压测类脚本指向生产环境。8.7 报告与通知要形成闭环自动化跑完不是结束结果要能被团队看到才有价值。建议把 Allure 报告集成到 CI 流水线中比如在 Jenkins、GitLab CI 或 GitHub Actions 中执行接口自动化任务。任务失败时把报告链接发到团队群。8.8 从小处开始不要过度设计最后也是最重要的建议不要一上来就追求大而全。很多初学者搭框架时喜欢把什么功能都封装一遍Session、并发、数据驱动、CI 全上。结果框架搭了一周真实用例没写几条。我的建议是先写 5 条最小用例跑通流程再抽公共配置和请求封装再加入 Fixture 处理登录态然后接入报告最后根据项目需要加入数据驱动、并发、CI。每一步都验证能跑通再进入下一步。这样框架是长出来的不是一次性堆出来的。长期维护时你会更清楚每一层代码为什么存在。9. 总结与后续学习方向到这里一个基于 Pytest Requests 的接口自动化测试框架已经搭建完成。你从最小用例开始经历了配置分离、请求封装、Fixture 管理、数据驱动和报告生成的全过程。这一套流程的核心收益在于你不再只是会“用 Python 调接口”而是能把接口用例组织成一个可持续运行、可维护、可汇报的测试工程。如果你想继续深入以下几个方向值得花时间深入学习 Pytest 的高级特性fixture 作用域、conftest 继承、mark 标记、插件开发引入 YAML 或 Excel 作为测试数据源结合 Pytest 的参数化机制做更灵活的数据驱动把框架接入 Jenkins 等 CI 工具实现定时触发和失败通知学习接口性能测试用 Locust 等工具扩展测试视角研究 Mock 服务让自动化测试不依赖第三方接口的稳定性。真正把接口自动化做好不是因为用了某个厉害的工具而是因为你清楚每一条用例的价值也清楚框架每一层的边界。先跑通最小流程再在真实项目里不断迭代。用不了多久你就会发现自己已经无痛进入接口自动化测试的实战阶段。