十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零搭建接口自动化测试框架:pytest+requests+Allure实战

从零搭建接口自动化测试框架:pytest+requests+Allure实战 接口自动化测试这件事说简单也简单一个 requests 发请求、一个 assert 断言就能跑起来说复杂也复杂当用例从 10 条涨到 500 条当接口依赖从 1 个变成 20 个当团队里五个人都在往里加代码的时候没有一套像样的框架维护成本会指数级上升。我从最早用 Postman 手动点、到写一堆散落的 Python 脚本、再到后来沉淀出一套 pytest requests Allure 的组合中间踩过的坑足够写一本小册子。这篇内容就是把这套框架从零搭起来的过程完整拆开包括目录怎么设计、请求怎么封装、数据怎么驱动、报告怎么生成、CI 里怎么跑以及那些只有真正跑过几百条用例之后才会遇到的问题。1. 为什么是 pytest requests Allure 这个组合1.1 三个组件各自解决什么问题先把这三个东西的定位说清楚不然后面封装的时候容易职责混乱。requests负责的是发请求这一层。它把 HTTP 协议里那些繁琐的东西——连接池、编码、重定向、Cookie 管理——全部封装成了几个方法调用。你写requests.get(url, paramsparams)的时候底层帮你处理了 TCP 连接、HTTP 报文拼装、响应解码。接口自动化里它扮演的是执行器的角色只关心请求发出去、响应拿回来。pytest负责的是组织用例这一层。它管的是哪些函数是测试用例、用例之间怎么共享前置条件、参数化怎么传、失败怎么重跑、哪些用例可以并行。它不关心你请求怎么发只关心用例的生命周期。这是它和 unittest 最大的区别——pytest 是框架管调度你管逻辑unittest 是你既要管调度又要管逻辑。Allure负责的是呈现结果这一层。它把 pytest 跑出来的原始结果一个 XML 文件渲染成带步骤、带附件、带分类的可视化报告。测试报告的价值不在于好看而在于当用例失败时你能一眼看到是哪个请求、哪个参数、返回了什么而不是对着一行AssertionError发呆。三者组合起来的分工是requests 干活pytest 指挥Allure 汇报。理解了这个分工后面所有的封装设计都会顺理成章。1.2 和 unittest、Postman 方案的真实对比很多人一开始会纠结用 unittest 还是 pytest。我两个都用过说几个实际感受对比维度unittestpytestPostman/Newman用例写法必须继承 TestCase 类普通函数即可图形界面配置参数化需要 ddt 库辅助原生pytest.mark.parametrize需要写脚本前置后置setUp/tearDown 层级固定fixture 按需组合pre-request script失败重跑需插件pytest-rerunfailures内置报告HTMLTestRunnerAllure 生态成熟自带但定制难代码复用类继承耦合高函数级 fixture解耦好脚本片段复用差调试体验一般--pdb直接断点界面调试方便Postman 适合接口调试和少量冒烟一旦用例上规模、需要和代码仓库一起版本管理、需要接入 CI它的劣势就暴露了。unittest 不是不能用但它的类继承模型在复杂 fixture 场景下会写得很别扭。pytest 的 fixture 机制是我最终选它的核心理由——它让前置条件变成了可组合、可复用、可作用域控制的积木。1.3 这套框架适合什么样的团队不是所有项目都需要这套框架。如果你的接口就十几个、每次上线前手动点一遍就行那用 Postman 更划算。这套框架真正发挥价值是在这些场景接口数量超过 50 个且核心链路有依赖关系需要每天定时跑、或者每次提交代码自动跑测试数据需要多套正常、边界、异常反复验证团队多人协作用例需要统一规范需要把测试结果沉淀下来做趋势分析如果你符合其中两条以上那投入时间搭这套框架是值得的。下面进入实操。2. 环境搭建与项目骨架设计2.1 Python 环境与依赖安装的坑Python 版本建议 3.8 以上3.10 和 3.11 都实测没问题。安装本身没什么好说的但有几个细节值得提醒第一务必用虚拟环境。我见过太多人把所有包装在全局环境里结果 A 项目要 requests 2.25B 项目要 requests 2.31互相打架。用 venv 或者 conda 都行python -m venv venv # Windows venv\Scripts\activate # macOS/Linux source venv/bin/activate第二依赖版本要锁死。不要只写requests要写requests2.31.0。接口测试框架最怕的就是某天 CI 上突然因为某个依赖升级跑挂了。用pip freeze requirements.txt生成锁定版本。核心依赖清单pytest7.4.3 requests2.31.0 allure-pytest2.13.2 pytest-rerunfailures12.0 pytest-xdist3.5.0 pytest-html4.1.1 PyYAML6.0.1 jsonschema4.20.0 Faker20.1.0这里解释几个不那么常见的pytest-xdist用来并行跑用例用例多了之后串行跑太慢jsonschema用来做响应结构校验比一个个字段断言优雅得多Faker用来生成随机测试数据避免用例之间数据污染。Allure 本身还需要装命令行工具这个不是 pip 能搞定的。Windows 上可以用 scoop 装macOS 用 brewLinux 下载压缩包解压后配 PATH。装完执行allure --version能输出版本号就 OK。2.2 目录结构一开始就要想清楚目录结构是框架的地基改起来成本极高。我推荐这套分层api_test_framework/ ├── config/ # 配置层 │ ├── config.yaml # 环境配置 │ └── settings.py # 配置读取 ├── common/ # 公共层 │ ├── request_client.py # 请求封装 │ ├── assert_util.py # 断言封装 │ ├── logger.py # 日志 │ └── data_util.py # 数据处理 ├── testdata/ # 测试数据 │ ├── login.yaml │ └── order.yaml ├── testcases/ # 用例层 │ ├── conftest.py │ ├── test_login.py │ └── test_order.py ├── reports/ # 报告输出 ├── logs/ # 日志输出 ├── conftest.py # 全局 fixture ├── pytest.ini # pytest 配置 └── requirements.txt这个分层的核心思想是关注点分离配置归配置、请求逻辑归公共层、数据归数据、用例只写业务断言。很多人把请求代码直接写在用例里一开始爽后面改一个 header 要改几十个文件。2.3 pytest.ini 的关键配置pytest.ini是 pytest 的全局配置几个必配项[pytest] testpaths testcases python_files test_*.py python_classes Test* python_functions test_* addopts -v -s --alluredir./reports/allure-results --clean-alluredir --reruns1 --reruns-delay2 markers smoke: 冒烟用例 regression: 回归用例 p0: 最高优先级 p1: 高优先级--reruns1表示失败重跑一次--reruns-delay2表示重跑前等 2 秒。这个配置在接口测试里非常有用因为网络抖动导致的偶发失败太常见了。但要注意重跑只能掩盖环境问题不能掩盖逻辑问题如果一个用例稳定失败重跑一百次也没用。markers定义了自定义标记后面可以用pytest -m smoke只跑冒烟用例。这是分层测试的基础。3. 请求封装让用例只关心业务3.1 为什么不能直接用 requests新手最容易犯的错就是在用例里直接写requests.post(url, jsondata, headersheaders)。这样写有几个致命问题第一重复代码爆炸。每个用例都要拼 URL、拼 header、处理 token改一处要改一片。第二无法统一处理异常。网络超时、连接错误、SSL 错误每个用例都要 try-except代码丑陋且容易漏。第三日志和报告无法统一。Allure 报告里想看到每个请求的详情散落的 requests 调用没法统一挂载。第四环境切换困难。测试环境、预发环境、生产环境只读的域名不同散落的 URL 没法统一管理。所以必须封装一层 RequestClient。3.2 RequestClient 的完整实现import requests import allure from common.logger import logger from config.settings import settings class RequestClient: def __init__(self): self.session requests.Session() self.base_url settings.BASE_URL self.timeout settings.TIMEOUT self.session.headers.update(settings.DEFAULT_HEADERS) def _request(self, method, url, **kwargs): full_url self.base_url url if not url.startswith(http) else url kwargs.setdefault(timeout, self.timeout) # 把请求详情挂到 Allure 报告 allure.attach( f{method} {full_url}\n fHeaders: {kwargs.get(headers, {})}\n fParams: {kwargs.get(params, {})}\n fBody: {kwargs.get(json, kwargs.get(data, {}))}, name请求详情, attachment_typeallure.attachment_type.TEXT ) logger.info(f请求: {method} {full_url}) try: resp self.session.request(method, full_url, **kwargs) except requests.exceptions.Timeout: logger.error(f请求超时: {full_url}) raise except requests.exceptions.ConnectionError: logger.error(f连接失败: {full_url}) raise logger.info(f响应: {resp.status_code} {resp.text[:500]}) allure.attach( fStatus: {resp.status_code}\n fBody: {resp.text}, name响应详情, attachment_typeallure.attachment_type.TEXT ) return resp def get(self, url, **kwargs): return self._request(GET, url, **kwargs) def post(self, url, **kwargs): return self._request(POST, url, **kwargs) def put(self, url, **kwargs): return self._request(PUT, url, **kwargs) def delete(self, url, **kwargs): return self._request(DELETE, url, **kwargs)这个封装有几个设计点值得展开说。用 Session 而不是每次 requests.get。Session 会自动保持 Cookie、复用 TCP 连接。接口测试里经常需要先登录拿到 Cookie后续请求自动带上Session 天然支持。而且连接复用能显著提升大量用例的执行速度。base_url 拼接逻辑。判断url.startswith(http)是为了兼容某些需要请求第三方完整 URL 的场景。日常用例只写相对路径环境切换时只改配置。Allure 附件挂载。这是让报告好用的关键。请求和响应都 attach 进去失败时在报告里直接能看到发了什么、回了什么不用去翻日志。异常分类捕获。超时和连接错误分开处理因为这两种错误的排查方向完全不同——超时可能是服务慢连接错误可能是服务挂了或者域名配错了。3.3 配置管理一套代码跑多环境配置用 YAML 管理比 JSON 可读性好比 ini 支持嵌套# config/config.yaml test: base_url: https://test-api.example.com timeout: 10 default_headers: Content-Type: application/json User-Agent: AutoTest/1.0 staging: base_url: https://staging-api.example.com timeout: 15 default_headers: Content-Type: application/json读取配置的 settings.pyimport yaml import os class Settings: def __init__(self): env os.getenv(TEST_ENV, test) config_path os.path.join( os.path.dirname(__file__), config.yaml ) with open(config_path, encodingutf-8) as f: all_config yaml.safe_load(f) current all_config[env] self.BASE_URL current[base_url] self.TIMEOUT current[timeout] self.DEFAULT_HEADERS current[default_headers] settings Settings()这样切换环境只需要export TEST_ENVstaging代码一行不用改。CI 里通过环境变量注入本地通过命令行设置非常灵活。注意配置文件里绝对不要写生产环境的写操作地址。接口自动化测试应该只针对测试环境生产环境最多做只读的监控探测这是底线。4. 用例组织与数据驱动4.1 fixture 的正确打开方式pytest 的 fixture 是这套框架的灵魂。先看一个典型的登录 fixture# conftest.py import pytest from common.request_client import RequestClient pytest.fixture(scopesession) def client(): return RequestClient() pytest.fixture(scopesession) def token(client): resp client.post(/api/login, json{ username: autotest, password: test123456 }) assert resp.status_code 200 return resp.json()[data][token] pytest.fixture(scopefunction) def auth_client(client, token): client.session.headers.update({Authorization: fBearer {token}}) return client这里的关键是scope 的选择。token用session级别整个测试会话只登录一次避免每个用例都登录导致接口被限流。auth_client用function级别保证每个用例拿到的都是干净的 header 状态。我踩过的一个坑早期把 token 设成 function 级别结果 200 条用例登录了 200 次直接把测试环境的登录接口打挂了触发了风控。后来改成 session 级别登录次数降到 1 次。这个教训很深刻——fixture 的 scope 不只是性能问题有时候是可用性问题。4.2 参数化一套逻辑跑多组数据pytest 的参数化是它相比 unittest 最大的优势之一。看一个实际例子import pytest pytest.mark.parametrize(username,password,expected_code,expected_msg, [ (autotest, test123456, 200, success), (autotest, wrongpwd, 401, 密码错误), (, test123456, 400, 用户名不能为空), (nonexist, test123456, 401, 用户不存在), ]) def test_login(auth_client, username, password, expected_code, expected_msg): resp auth_client.post(/api/login, json{ username: username, password: password }) assert resp.status_code expected_code assert resp.json()[msg] expected_msg一个函数覆盖了正常、密码错误、参数缺失、用户不存在四种场景。Allure 报告里会显示成四个独立的用例每个都有自己的参数。但参数化数据写在代码里有个问题数据量大了之后代码会很长而且非技术人员没法维护。所以更好的做法是把数据外置到 YAML# testdata/login.yaml - case_name: 正常登录 username: autotest password: test123456 expected_code: 200 expected_msg: success - case_name: 密码错误 username: autotest password: wrongpwd expected_code: 401 expected_msg: 密码错误然后用一个工具函数读取import yaml def load_yaml(path): with open(path, encodingutf-8) as f: return yaml.safe_load(f) pytest.mark.parametrize(case, load_yaml(testdata/login.yaml), idslambda c: c[case_name]) def test_login(auth_client, case): resp auth_client.post(/api/login, json{ username: case[username], password: case[password] }) assert resp.status_code case[expected_code]ids参数让 Allure 报告里显示的是正常登录而不是一长串参数可读性提升明显。4.3 接口依赖用 fixture 串联业务流真实业务里接口是有依赖的。比如下单要先登录、要先有商品、要先有库存。这种依赖用 fixture 串联最自然pytest.fixture def product_id(auth_client): resp auth_client.post(/api/product/create, json{ name: 测试商品, price: 99.9, stock: 100 }) pid resp.json()[data][id] yield pid # 用例结束后清理 auth_client.delete(f/api/product/{pid}) pytest.fixture def order_id(auth_client, product_id): resp auth_client.post(/api/order/create, json{ product_id: product_id, quantity: 1 }) return resp.json()[data][order_id] def test_order_detail(auth_client, order_id): resp auth_client.get(f/api/order/{order_id}) assert resp.status_code 200 assert resp.json()[data][order_id] order_id注意product_id里用了yield这是 fixture 的清理机制——yield 之前是前置yield 之后是后置。这样创建的商品在用例结束后会自动删除不会污染测试环境。测试数据自清理是接口自动化的基本素养否则跑几次之后测试环境就全是垃圾数据了。5. 断言与响应校验的进阶做法5.1 从硬编码断言到 schema 校验新手写断言通常是这样的assert resp.json()[data][name] 张三 assert resp.json()[data][age] 25 assert resp.json()[data][email] zhangsanexample.com这种写法的问题是字段一多就写不完而且接口新增字段时断言不会失败这其实是好事但结构变化时又发现不了。更好的做法是分层校验状态码 关键业务字段 整体结构。整体结构用 jsonschemafrom jsonschema import validate USER_SCHEMA { type: object, required: [code, msg, data], properties: { code: {type: integer}, msg: {type: string}, data: { type: object, required: [id, name, email], properties: { id: {type: integer}, name: {type: string}, email: {type: string, format: email} } } } } def test_get_user(auth_client): resp auth_client.get(/api/user/1) assert resp.status_code 200 validate(instanceresp.json(), schemaUSER_SCHEMA) assert resp.json()[data][id] 1schema 校验能保证接口返回的结构符合契约字段类型正确、必填字段存在。这在接口联调阶段特别有用能第一时间发现后端改了字段类型或者漏了字段。5.2 封装一个顺手的断言工具把常用断言封装成工具函数用例里调用更简洁class AssertUtil: staticmethod def assert_code(resp, expected200): assert resp.status_code expected, \ f状态码错误: 期望{expected}, 实际{resp.status_code} staticmethod def assert_json_value(resp, key_path, expected): actual resp.json() for k in key_path.split(.): actual actual[k] assert actual expected, \ f字段{key_path}错误: 期望{expected}, 实际{actual} staticmethod def assert_schema(resp, schema): validate(instanceresp.json(), schemaschema)用例里就变成def test_create_order(auth_client): resp auth_client.post(/api/order, json{...}) AssertUtil.assert_code(resp, 200) AssertUtil.assert_json_value(resp, data.status, created)断言失败时的报错信息要足够清晰这是很多人忽略的点。assert actual expected默认的报错信息很模糊加上自定义 message 之后报告里一眼就能看出哪个字段不对。5.3 数据库校验接口测完再查库有些场景光看接口返回不够还要确认数据真的落库了。比如下单接口返回成功但订单表里到底有没有这条记录这时候需要连数据库校验import pymysql pytest.fixture(scopesession) def db(): conn pymysql.connect( hostsettings.DB_HOST, usersettings.DB_USER, passwordsettings.DB_PASSWORD, databasesettings.DB_NAME, charsetutf8mb4 ) yield conn conn.close() def test_order_created_in_db(auth_client, db, order_id): cursor db.cursor() cursor.execute(SELECT status FROM orders WHERE id %s, (order_id,)) result cursor.fetchone() assert result is not None, 订单未落库 assert result[0] created数据库校验是接口自动化的进阶能力能发现一些接口返回正常但数据没写对的隐蔽 bug。但要注意数据库校验会让用例和表结构强耦合表结构一变用例就挂。所以只对核心业务表做校验不要滥用。6. Allure 报告的定制与优化6.1 让报告说人话feature、story、stepAllure 报告默认只显示用例名信息量不够。加上装饰器之后报告会变成按业务模块组织的结构import allure allure.feature(用户模块) allure.story(登录功能) allure.title(正常登录-返回token) allure.severity(allure.severity_level.CRITICAL) def test_login_success(auth_client): with allure.step(步骤1: 发送登录请求): resp auth_client.post(/api/login, json{...}) with allure.step(步骤2: 校验状态码): assert resp.status_code 200 with allure.step(步骤3: 校验返回token): assert token in resp.json()[data]feature和story是两级分类报告里会显示成树状结构。step会把用例执行过程拆成步骤失败时能精确定位到哪一步挂了。severity标记优先级报告里可以按优先级筛选。这套装饰器用下来报告的可读性会有质的提升。测试报告是给团队看的不是给自己看的让开发和产品能看懂报告是自动化测试价值的体现。6.2 失败截图与请求日志的自动挂载前面 RequestClient 里已经挂了请求和响应详情。但还可以更进一步——失败时自动挂载更多上下文pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: # 失败时把用例的 fixture 数据也挂上去 if auth_client in item.funcargs: client item.funcargs[auth_client] allure.attach( str(client.session.headers), name失败时会话Headers, attachment_typeallure.attachment_type.TEXT )这个 hook 会在用例失败时自动执行把当前会话的 header 挂到报告里。排查问题时特别有用——有时候失败就是因为 token 过期了看一眼 header 就知道了。6.3 报告生成与查看的完整流程pytest 跑完之后结果在reports/allure-results目录里是一堆 JSON 文件。要生成 HTML 报告需要执行allure generate ./reports/allure-results -o ./reports/allure-report --clean allure open ./reports/allure-report--clean会清空旧报告避免历史数据混淆。allure open会启动一个本地服务打开报告。如果要在 CI 里跑通常是生成报告后打包成 artifact 上传或者部署到静态服务器。Allure 报告是纯静态的 HTML JS扔到 nginx 里就能访问。提示allure-results目录不要提交到 git它是每次跑都会变的中间产物。在.gitignore里加上reports/和logs/。7. 那些只有跑过几百条用例才会遇到的问题7.1 用例之间的数据污染这是最常见也最隐蔽的问题。比如用例 A 创建了一个用户名为 test001 的用户用例 B 也创建 test001如果 A 没清理B 就会因为用户名重复而失败。更坑的是这种失败是顺序相关的——单独跑 B 能过A 和 B 一起跑就挂。解决方案有三个层次第一数据自清理。前面说的 fixture yield 机制用例结束自动删数据。第二数据唯一化。用 Faker 或者时间戳生成唯一数据from faker import Faker fake Faker(zh_CN) def test_create_user(auth_client): username fauto_{fake.user_name()}_{int(time.time())} resp auth_client.post(/api/user, json{username: username})第三用例隔离。每个用例用独立的测试账号或者用独立的租户空间。这个成本最高但最彻底。我的经验是核心用例用方案三普通用例用方案二配合方案一兜底。7.2 接口限流与重试策略测试环境通常有限流跑几百条用例很容易触发 429。前面提到的--reruns能缓解但治标不治本。更根本的做法是登录等高频接口用 session 级 fixture只调一次用例之间加适当延迟time.sleep(0.1)有时候是必要的用 pytest-xdist 并行时控制并发数-n 4而不是-n auto对 429 响应做特殊处理等待后重试import time def _request_with_retry(self, method, url, retries3, **kwargs): for i in range(retries): resp self.session.request(method, url, **kwargs) if resp.status_code 429: wait int(resp.headers.get(Retry-After, 2 ** i)) logger.warning(f触发限流等待{wait}秒后重试) time.sleep(wait) continue return resp return resp这个重试逻辑要谨慎使用只对 429 和 5xx 重试不要对 4xx 重试因为 4xx 是客户端错误重试多少次都一样。7.3 并行执行带来的新问题pytest-xdist 能把执行时间从 10 分钟压到 3 分钟但会引入新问题session 级 fixture 在每个 worker 里都会执行一次。如果登录接口有限流4 个 worker 就是 4 次登录。用例执行顺序不再确定。依赖执行顺序的用例会随机失败。共享资源竞争。多个 worker 同时操作同一条数据会冲突。所以并行之前先确保用例是无状态、无顺序依赖的。做不到就先别并行或者只对独立模块并行。7.4 断言失败信息的可读性assert resp.json()[data][id] 1失败时报错是AssertionError没有上下文。改成actual resp.json()[data][id] assert actual 1, f用户ID错误: 期望1, 实际{actual}, 完整响应: {resp.text}把完整响应带上排查时不用再去翻日志。这个习惯看起来小但在用例多了之后能省大量时间。8. 接入 CI 与日常维护8.1 Jenkins/GitLab CI 里的执行脚本CI 里的执行脚本要处理几件事装依赖、跑用例、生成报告、处理退出码。#!/bin/bash set -e python -m venv venv source venv/bin/activate pip install -r requirements.txt # 跑用例失败不中断继续生成报告 pytest --alluredir./reports/allure-results || true # 生成报告 allure generate ./reports/allure-results -o ./reports/allure-report --clean # 根据结果决定构建状态 if grep -q status:failed ./reports/allure-results/*.json 2/dev/null; then echo 存在失败用例 exit 1 fi|| true是为了让 pytest 失败后脚本继续执行否则报告生成不了。最后再根据结果决定构建是否失败。8.2 用例分层与执行策略不是每次都要跑全部用例。合理的分层是层级用例范围执行时机耗时冒烟核心链路 20 条每次提交1 分钟回归全量用例每日定时10 分钟全量含边界异常发版前30 分钟用 marker 区分pytest -m smoke # 只跑冒烟 pytest -m not smoke # 跑非冒烟 pytest -m p0 or p1 # 跑高优先级8.3 框架本身的维护框架代码也是代码需要维护。几个建议公共层要有单元测试。RequestClient、AssertUtil 这些工具函数自己也要测否则工具出 bug 会污染所有用例。定期清理失效用例。接口下线了对应用例要删掉不要留着当僵尸。报告要有趋势。Allure 支持历史趋势把每次的报告结果保留下来能看到通过率的变化曲线。文档要跟上。新人接手时一份 README 能省很多沟通成本。9. 一些零散但重要的经验写到这里框架的主体已经完整了。最后分享几个零散但很实用的点。关于 token 过期。session 级 fixture 的 token 如果过期了后续所有用例都会失败。可以在 RequestClient 里加一个拦截如果响应是 401自动重新登录刷新 token。但这会让框架变复杂简单项目手动控制 token 有效期更省事。关于测试数据准备。有些接口依赖特定的数据状态比如已支付的订单才能退款。这种数据用接口创建成本高可以直接在数据库里 insert。但要注意直接操作数据库的数据测试环境重建后会丢失所以要有数据初始化脚本。关于环境健康检查。跑用例之前先探测一下测试环境是否可用不可用就直接跳过避免一堆无意义的失败。可以在 conftest 里加一个 session 级的健康检查 fixture。关于日志级别。本地调试用 DEBUGCI 里用 INFO。日志太多会拖慢执行速度太少又排查不了问题。RequestClient 里请求和响应都打 INFO详细的 header 和 body 打 DEBUG。关于用例命名。test_login_success比test_001好一百倍。用例名要能表达测试意图报告里一眼就能看出测的是什么。中文用例名也可以pytest 支持Allure 显示也没问题。关于版本控制。测试数据和用例代码一起提交配置文件的敏感信息密码、密钥用环境变量注入不要硬编码。.gitignore里排除 reports、logs、venv、__pycache__。这套框架我从最初的几十行脚本迭代到现在支撑上千条用例中间重构过三次。最大的体会是框架的价值不在于用了多少高级特性而在于它能不能让写用例的人只关心业务逻辑。当新增一个接口的测试只需要写一个 YAML 文件加一个函数的时候这套框架就成功了。
返回列表