ARTICLE DETAIL

资讯详情

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

接口自动化测试框架搭建实战:从分层设计到CI集成

接口自动化测试框架搭建实战:从分层设计到CI集成 1. 动手前的三个问题框架的边界、选型和演进路径1.1 框架到底要解决什么问题先说个场景。我见过不少团队接口测试框架写了一堆代码Postman里的用例转成了Python脚本requests.get一调print一打就算跑通了。结果用了一个月没人维护了。为什么因为那不是框架那是脚本堆积。接口自动化测试框架本身不是一个工具而是一套约定加机制的组合。它要解决的核心问题不是能调接口而是下面这几件事用例怎么组织才能让不同水平的人都能看懂、能上手写测试数据怎么管理才不会让数据硬编码在代码里改个环境就要翻源码接口之间的依赖怎么处理token怎么传递才不会写出一堆全局变量满天飞的代码失败的时候怎么定位是断言失败、超时、还是环境挂了跑完之后结果怎么反馈能不能直接在CI里看到趋势。如果你搭的框架回答不了这几个问题那它只是个壳。想清楚这一点再动手比什么都重要。1.2 技术选型Python系和Java系怎么选现在做接口自动化市面上基本就是两大流派Python pytest requests和Java TestNG/JUnit5 RestAssured HttpClient。选哪个不完全取决于哪个更好而取决于团队的现状。对比维度Python pytestJava TestNG/RestAssured上手门槛低语法简洁几行代码就能跑通一条用例偏高工程化能力强适合已有Java技术栈的团队用例编写效率高装饰器和fixture写起来很顺手中等注解和链式调用需要一点适应成本生态丰富度requests、pytest、allure、yaml、pydantic全家桶很全RestAssured、TestNG、Maven/Surefire、allure也够用与CI集成简单命令行直接跑也简单Maven插件触发适合场景中小团队、快速迭代、测试人员以功能测试转自动化的场景大型Java研发团队、需要和Spring生态深度集成的场景我的建议很直接如果团队里没有人写过Java或者测试组平时主要在写Python脚本那就直接走Python路线不要为了Java工程师更好招去选Java。工具链是其次团队能长期维护才是第一位的。如果确实要走Java路线骨架思路是一样的差异在表达层。TestNG的DataProvider对应pytest的pytest.mark.parametrizeRestAssured的given().when().then()对应requests的session.request()依赖管理从pip换成Maven/Gradle而已。框架设计和分层逻辑完全不冲突。1.3 搭框架的演进路径别一上来就整重的很多人搭框架喜欢一步到位上来就搞分布式执行、平台化、可视化最后项目还没上线框架先凉了。我的经验是分三步走先把一条全链路用例跑通登录拿token带token调业务接口断言业务码和关键字段再把公共能力抽出来请求封装、数据驱动、报告、日志最后才考虑平台化、并发执行、环境隔离这些东西。这个顺序不要反。你第一步都没走完的时候根本不知道自己的项目有哪些奇葩接口——比如有的接口返回code0表示成功有的返回code200有的字段是下划线命名有的是驼峰。这些实际情况会直接影响你怎么设计断言和参数处理提前构想的架构到落地时大概率要返工。2. 目录结构与分层设计第一天就该想清楚的骨架2.1 一套被验证过的目录结构我搭过好几个接口自动化项目用到现在最顺手、也最容易让新人上手的是下面这套结构api_test_framework/ ├── config/ # 全局配置 │ ├── __init__.py │ ├── settings.py # 环境配置、超时、重试次数等 │ └── env.yaml # 不同环境的host、账号、密钥 ├── core/ # 框架核心封装 │ ├── __init__.py │ ├── http_client.py # requests会话封装 │ ├── assertion.py # 断言封装 │ ├── log.py # 日志模块 │ └── yaml_loader.py # yaml配置加载 ├── api/ # 每个接口一个类/模块 │ ├── __init__.py │ ├── auth_api.py # 登录、刷新token等 │ └── order_api.py # 订单相关接口 ├── testcases/ # 测试用例 │ ├── __init__.py │ ├── conftest.py # pytest fixture │ ├── test_login.py │ └── test_order.py ├── data/ # 测试数据 │ ├── login_data.yaml │ └── order_data.yaml ├── reports/ # 测试报告和日志输出 │ ├── allure-results/ │ └── logs/ ├── utils/ # 通用工具 │ ├── __init__.py │ ├── common_utils.py # 随机数、时间戳、加解密等 │ └── db_utils.py # 数据库查询用于数据准备和断言 ├── pytest.ini └── requirements.txt这个结构不是拍脑袋定的。核心思路是把代码和数据分开框架能力和业务用例分开。新人接手时写用例的人只需要打开api/和testcases/看几个现成的例子就能照着写改环境配置的去config/env.yaml底层请求出了问题的去core/里查。不会出现一个文件几百行、改一处崩一片的情况。2.2 为什么要在分层上花这么多心思有过维护经验的人都懂接口自动化项目死掉绝大多数不是因为跑不通而是因为改不起。改一个接口的URL要全局搜索替换十几处改一个字段名用例全部红加一个环境代码里硬编码的host要逐个改。这就是不分层的代价。分层的核心规则就一句话每一层只能依赖它下面的一层不能跨层调用。用例层testcases可以调用接口层api和工具层utils但不能直接操作requests接口层api只负责发请求、收响应不负责断言断言逻辑统一走core/assertion.py不要在用例里写了20个不同的assert姿势所有外部依赖环境地址、账号密码走配置不写死在代码里。这样做的好处是将来接口改版了你只需要改api/层对应的那个方法断言规则变了改core/assertion.py环境变了改env.yaml。你不需要在一百个用例文件里做人肉批量替换。3. 核心能力逐块实现请求封装、数据驱动与断言体系3.1 请求会话封装别再用裸的requests了很多初始项目是直接在用例里requests.post(url, jsondata)写请求。这样写短平快问题是超时、重试、请求头、日志、token自动携带这些事每个用例都要重复处理。一旦接口服务抖动一下超时了你看到的是一整屏的ConnectionError根本分不清是网络问题还是接口问题。所以框架里第一件要做的事是封装一个基于requests.Session的HTTP客户端。为什么要用Session因为Session会自动保存Cookie同时我们可以把公共请求头、Token注入、超时和重试都放在里面用例代码会变得非常干净。import logging import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry logger logging.getLogger(__name__) class HttpClient: 基于requests.Session的统一请求客户端 def __init__(self, base_url, timeout10, retry_times3): self.session requests.Session() self.base_url base_url.rstrip(/) self.timeout timeout # 配置重试策略连接错误和503/502这类服务不可用错误时重试 retry_strategy Retry( totalretry_times, backoff_factor0.5, # 退避因子等待时间是0.5*2^n秒 status_forcelist[500, 502, 503, 504], allowed_methods[GET, POST, PUT, DELETE] ) adapter HTTPAdapter(max_retriesretry_strategy) self.session.mount(http://, adapter) self.session.mount(https://, adapter) # 默认请求头后续可以在具体接口里覆盖 self.session.headers.update({ Content-Type: application/json, Accept: application/json, User-Agent: api-test-framework/1.0 }) def request(self, method, path, **kwargs): url f{self.base_url}{path} # 超时兜底防止某个用例漏传timeout导致一直卡住 kwargs.setdefault(timeout, self.timeout) # 记录请求日志 logger.info(f[REQUEST] {method.upper()} {url} params{kwargs.get(params)} data{kwargs.get(json)}) try: resp self.session.request(method, url, **kwargs) logger.info(f[RESPONSE] status{resp.status_code} body{resp.text[:500]}) return resp except requests.exceptions.Timeout: logger.error(f[TIMEOUT] {url} 超过 {self.timeout}s 未响应) raise except requests.exceptions.ConnectionError: logger.error(f[CONNECT_ERROR] {url} 连接失败请检查网络或服务状态) raise def post(self, path, **kwargs): return self.request(POST, path, **kwargs) def get(self, path, **kwargs): return self.request(GET, path, **kwargs) def update_token(self, token): 统一更新Authorization头用例里不需要关心token怎么传 self.session.headers.update({Authorization: fBearer {token}})这里有几个细节值得展开讲。首先是超时kwargs.setdefault(timeout, self.timeout)这一步很关键它保证即使调用方忘了传timeout也会有兜底值。其次是重试策略backoff_factor的含义是重试间隔会递增第一次等0.5秒第二次等1秒第三次等2秒这比固定间隔更合理能避免服务刚好在恢复窗口期又被你的密集重试压垮。最后是日志每发一个请求都记录请求参数和响应前500个字符线上排查问题的时候你会发现这个习惯能救你的命。3.2 数据和用例分离用yaml驱动用例数据驱动听起来很高级但实际上核心思想就一句话把测试数据从代码里拿出来放到配置文件里。这样做的好处是测试人员修改数据不需要动代码也不用重新发布而且同一套用例可以针对不同数据集反复跑。我比较推荐用yaml做数据文件因为它可读性比JSON好支持注释写起来也简洁。比如一个登录接口的测试数据# data/login_data.yaml base: # 正常场景 - case: 正确用户名密码登录成功 username: admin password: admin123 expect_code: 0 expect_msg: 登录成功 - case: 密码错误提示正确 username: admin password: wrong_pass expect_code: 1001 expect_msg: 用户名或密码错误 edge: # 边界和异常场景 - case: 用户名为空 username: password: admin123 expect_code: 1002 expect_msg: 用户名不能为空然后在用例里通过pytest的参数化机制加载import pytest import yaml import os from api.auth_api import AuthApi pytest.mark.parametrize(test_data, yaml.safe_load(open(os.path.join(os.path.dirname(__file__), ../data/login_data.yaml), encodingutf-8))[base]) def test_login(base_url, test_data): api AuthApi(base_url) resp api.login(test_data[username], test_data[password]) assert resp[code] test_data[expect_code] assert resp[msg] test_data[expect_msg]这里有一个关键点parametrize的参数名test_data和用例函数的参数名必须一致pytest才能正确注入。我见过不少新手在这上面卡住报了fixture test_data not found一脸懵。实际上pytest会先从fixture里找同名参数找不到再去parametrize里找所以一旦重名或者参数个数对不上就会报fixture错误。数据驱动真正的价值是让你把大量参数不同、逻辑相同的用例从代码中解放出来。后续如果接口新增了一个边界值测试你只需要往yaml里加一条数据用例不用动。3.3 断言体系别只会assert等于断言是接口测试的灵魂也是最容易写出问题的地方。最常见的问题有两个断太浅和断太死。断太浅的例子只判断HTTP状态码等于200。问题在于很多系统不管业务成不成功HTTP状态码都返回200你测了个寂寞。断太死的例子对返回的某个字段做了精确匹配结果别人在接口返回里多加了一个时间戳字段你的断言就挂了。这不是接口问题是你的断言问题。我的建议是把断言分成几个层次针对不同场景使用class ResponseAssert: 响应断言封装 staticmethod def assert_status_code(resp, expected200): 第一层HTTP状态码 assert resp.status_code expected, \ fHTTP状态码不符合预期, 期望:{expected}, 实际:{resp.status_code}, 响应:{resp.text[:300]} staticmethod def assert_biz_code(resp, expected_code): 第二层业务状态码 body resp.json() actual_code body.get(code) or body.get(status) # 兼容不同命名 assert actual_code expected_code, \ f业务码不符合预期, 期望:{expected_code}, 实际:{actual_code}, 响应:{resp.text[:300]} staticmethod def assert_field_value(resp, field_path, expected_value): 第三层具体字段值field_path支持点路径比如 data.order.status body resp.json() # 用点路径逐级取字段避免写一堆try except keys field_path.split(.) value body for key in keys: if isinstance(value, dict) and key in value: value value[key] else: raise AssertionError(f字段 {field_path} 不存在, 响应:{resp.text[:300]}) assert value expected_value, \ f字段 {field_path} 值不符合预期, 期望:{expected_value}, 实际:{value} staticmethod def assert_field_exists(resp, field_path): 只验证字段存在不关心值——适合那些取值动态变化的字段 body resp.json() keys field_path.split(.) value body for key in keys: if isinstance(value, dict) and key in value: value value[key] else: raise AssertionError(f字段 {field_path} 不存在, 响应:{resp.text[:300]})为什么要点路径取字段而不是直接body[data][order][status]因为接口返回的字段经常有嵌套如果中间某层没有这个key直接下标访问会抛KeyError排查起来还得去翻响应体。用点路径的方式断言失败时能直接告诉你哪个字段不存在。很多项目还有一类断言是数据库断言——接口返回成功不算成功得看数据库里的数据落地了才叫成功。这种断言适合写在关键链路上比如下单接口、支付回调接口。做法很简单接口调用成功后去数据库查对应订单的状态from utils import db_utils def test_order_create(login_token, base_url): api OrderApi(base_url, login_token) resp api.create_order(...) ResponseAssert.assert_biz_code(resp, 0) # 数据库断言订单状态应该是待支付 order db_utils.query_one(SELECT status FROM t_order WHERE order_no %s, (order_no,)) assert order[status] PENDING, f订单状态不正确, 数据库里是: {order[status]}但要提醒一句数据库断言要克制别每个用例都查库。查库意味着你的测试用例和数据库结构强绑定一旦表结构改了用例也跑不起来。我的经验是只在核心资金链路和状态流转这种数据错了后果严重的接口上用普通查询接口做业务断言就够了。3.4 接口依赖与token传递管理好全局状态做过接口测试的人都知道很多业务接口需要登录态而且token有有效期。怎么在框架里优雅地处理这些依赖是很影响使用体验的环节。常用方案是pytest的conftest.py里定义一个login_token的fixture作用于整个测试会话# testcases/conftest.py import pytest import yaml import os from core.http_client import HttpClient pytest.fixture(scopesession) def base_url(): 读取当前环境的host env os.getenv(TEST_ENV, test) # 支持通过环境变量切换 with open(os.path.join(os.path.dirname(__file__), ../config/env.yaml), encodingutf-8) as f: config yaml.safe_load(f) return config[env][host] pytest.fixture(scopesession) def login_token(base_url): 登录一次整个测试会话复用token client HttpClient(base_url) resp client.post(/api/auth/login, json{username: admin, password: admin123}) resp_json resp.json() assert resp_json[code] 0, f登录失败: {resp.text} return resp_json[data][token]scopesession的意思是整个测试会话只登录一次所有测试类共享这个token。这样既保证了效率又保证了用例之间的数据一致性。更复杂一点的场景是有的接口返回的字段是动态的比如创建订单后返回order_id下一个接口要用这个order_id。这种依赖不能用token那种只登录一次的方式解决更合理的做法是在用例里实现而不是在fixture里硬编码def test_order_pay(login_token, base_url): # 第一步创建订单拿到order_id api OrderApi(base_url, login_token) create_resp api.create_order(product_id1001, amount99.5) ResponseAssert.assert_biz_code(create_resp, 0) order_id create_resp.json()[data][order_id] # 第二步用order_id去支付这就是接口依赖 pay_resp api.pay_order(order_idorder_id, pay_methodalipay) ResponseAssert.assert_biz_code(pay_resp, 0)这条用例是完整的业务链路它把创建订单和支付订单两个接口串起来测比单独测两个接口更能发现真实问题。框架要做的是把这种链路的写法变得顺畅而不是为了解耦硬生生拆掉它。4. 报告、日志与通知跑完不是终点看得懂才是4.1 测试报告Allure还是pytest-html很多初学框架的人会忽略报告这一环觉得pytest -v打印了一堆pass/fail就够了。真到了项目里你需要给领导看结果、给开发定位问题、给团队做回归趋势分析终端输出根本不够用。现在主流的报告方案两个pytest-html和Allure。pytest-html胜在轻量一条命令就能生成一个自包含的HTML文件打开就能看结果适合快速演示和小项目。Allure功能更强可以按功能模块、严重程度、缺陷类型等多维度统计还有历史趋势图适合做长期维护的框架。缺点是环境搭建稍微多一点需要安装allure命令行工具。我的建议是用Allure原因就一个它支持用例标记和步骤拆解这对于接口自动化这种一条用例多个步骤的场景太重要了。import allure allure.feature(订单模块) allure.story(创建订单) allure.title(创建订单成功-支付宝支付) allure.severity(allure.severity_level.CRITICAL) def test_create_order(login_token, base_url): with allure.step(调用创建订单接口): api OrderApi(base_url, login_token) resp api.create_order(product_id1001, amount99.5) with allure.step(校验返回状态): ResponseAssert.assert_biz_code(resp, 0)跑完之后生成报告pytest testcases/test_order.py --alluredir reports/allure-results allure serve reports/allure-results报告里能直接看到每个接口的请求参数、响应结果、断言结果以及步骤级别的时间消耗。开发排查问题的时候不需要再问你这个用例传了什么参数打开报告自己看就行。4.2 日志配置别让print毁掉你的排查效率框架里的日志和普通脚本的print是两码事。print只能往控制台输出而且没有级别、没有时间戳、没有来源定位。日志模块要能做到控制台看实时输出文件里查历史记录错误信息带堆栈。一个够用的日志配置长这样# core/log.py import logging import os import time from logging.handlers import TimedRotatingFileHandler def setup_logger(nameapi_test, log_dirreports/logs): os.makedirs(log_dir, exist_okTrue) logger logging.getLogger(name) if logger.handlers: # 防止重复初始化 return logger logger.setLevel(logging.DEBUG) fmt logging.Formatter( %(asctime)s [%(levelname)s] %(name)s - %(filename)s:%(lineno)d - %(message)s ) # 控制台输出 console_handler logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(fmt) # 文件输出按天切割保留7天 file_handler TimedRotatingFileHandler( os.path.join(log_dir, test.log), whenmidnight, backupCount7, encodingutf-8 ) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(fmt) logger.addHandler(console_handler) logger.addHandler(file_handler) return logger这里有个小技巧用TimedRotatingFileHandler按天切割日志文件避免一个log文件跑几个月变成几个GB打开都卡。保留7天的量对于排查回归问题完全够用。在HttpClient里我用的就是这套logger。实际运行中一条用例失败时你能在日志里看到请求发了什么参数、响应返回了什么、在哪里断言的、断言预期是什么。排查效率比看黑压压的traceback高一个数量级。4.3 失败通知让接口挂了这件事被看见接口自动化测试如果没有通知机制那就是跑了但没人看。理想状态是每天定时跑一次回归跑完把结果推送到群里有失败就相关人。现在主流的通知渠道是飞书、钉钉、企业微信的机器人原理都一样往webhook地址POST一条JSON消息。以飞书自定义机器人为例import requests import json def send_feishu_message(title, content, webhook_url): 给飞书群发送消息 payload { msg_type: interactive, card: { header: { title: {tag: plain_text, content: title}, template: red if 失败 in title else green }, elements: [ {tag: div, text: {tag: lark_md, content: content}} ] } } resp requests.post(webhook_url, jsonpayload, timeout5) return resp.json()配合pytest的钩子函数可以在测试会话结束后自动统计结果并推送# conftest.py 中追加 def pytest_sessionfinish(session, exitstatus): 测试结束后的钩子统计结果并推送 from core.log import setup_logger logger setup_logger() passed session.testscollected - session.testsfailed - session.testsfailed # 简化计数 failed session.testsfailed content f 执行环境{os.getenv(TEST_ENV, test)} 通过用例{session.testscollected - failed} 失败用例{failed} 执行耗时{time.time() - start_time:.2f}s try: send_feishu_message(接口自动化回归结果, content, webhook_url) except Exception as e: logger.error(f飞书通知发送失败: {e})注意pytest_sessionfinish是pytest提供的内置钩子函数在测试全部跑完后自动调用不用注册也不用标记放在conftest.py里就生效。这里的webhook_url不要写死放到env.yaml或者环境变量里防止泄露。有了通知机制接口自动化就不再是自娱自乐了。每天早上一睁眼手机上就能看到昨晚的回归结果这种确定性对于把控项目质量至关重要。5. 踩坑实录这几条经验是跑过上百个项目才换来的5.1 环境隔离测试环境的数据污染是头号杀手接口自动化最大的敌人不是代码bug而是环境数据不稳定。今天跑得好好的用例明天突然挂了查了半天发现是测试环境有人手动改了数据或者上一个用例把数据状态改掉了。应对思路用例设计时就假设环境是不干净的然后从策略上规避。第一招构造数据时用随机后缀。比如注册一个新用户用户名用test_{time.time()}这种避免和别人撞数据。第二招对依赖初始状态的用例用例开始前先做数据准备。比如测试订单支付先通过SQL插入一条待支付的订单再调用支付接口不依赖刚好有一条没人动过的订单。第三招核心业务数据做独享环境。如果你在一个共享环境上跑关键链路的自动化大概率会被别人搞崩。有条件的话搭一套仅供自动化使用的独立测试环境能把各类问题减少一半以上。5.2 断言过强与过弱怎么把握那个度断言这个问题我前面提过几句这里专门展开讲因为它直接决定了测试的可信度。断言过弱的问题好理解——assert resp.status_code 200接口返回一堆错误码照样放行等于白测。断言过强的问题很多人没意识到。比如有接口返回{code:0,data:{login_time:2024-01-01 12:00:00}}你对login_time做了精确匹配断言等于2024-01-01 12:00:00。下次跑的时候登录时间变了断言失败。这根本不是接口出问题是你断言了一个动态字段。正确的姿势是区分接口的稳定字段和动态字段字段类型断言策略示例业务状态码固定值精确匹配code0关键业务字段精确匹配或正则匹配订单金额、订单状态时间戳、随机数只验证存在性或断言格式login_time列表数据验证长度、匹配关键元素列表非空包含目标ID我的经验是断言的目的是确认这次调用的核心业务逻辑没有发生变化不是验证接口输出和上次完全一样。抓住这个核心就不会在断言的强弱上反复摇摆。5.3 用例之间的隐性依赖踩过最大的坑假设你有两条用例用例A创建一个商品用例B查询并修改这个商品。你图省事在用例B里直接用了用例A创建的商品的ID通过模块级的全局变量传递。第一天跑没问题。第二天跑用例A失败了用例B跟着挂。这就是级联失败。最坑的是排查时你得先看A为什么挂再推测B是不是被连累的反复确认。解决这个问题的两个方向方向一用例完全独立每个用例自己准备数据。用例B如果需要商品自己创建一个自己用跑完自己清理。代价是执行时间变长但稳定性和定位速度大幅提升。方向二如果必须共享数据比如创建商品的开销很大就把它放到session级别的fixture里而不是用例里的全局变量。这样即使某个用例挂了fixture管理的对象不会因为作用域问题变成半初始化状态。我个人的做法是混合式核心链路用例独立准备数据开销大的公共数据用session级fixture。既保证了稳定性也保证了执行效率。5.4 超时时间你以为的接口bug可能只是默认值太短很多人在封装HttpClient的时候requests的timeout参数直接用默认值默认是None即无限等待。一旦网络抖动或者服务端处理慢用例会卡住很久你以为服务挂了实际上是超时根本没生效。反过来有些人习惯把超时设置成3秒。接口本身的响应时间是2.8秒结果服务完全正常你的用例却因为超时挂了白白误报。合理的做法是分场景设置基础接口登录、鉴权超时时间短一点比如5秒业务链路接口下单、支付回调超时时间长一点比如15秒。而且超时时间不要硬编码在代码里放到配置里方便按环境调整。我在HttpClient里默认设置是10秒并支持每个接口单独覆盖。不管是本地联调还是CI环境都比较稳。5.5 和CI的集成从我跑到系统跑框架搭好之后接上CI才是完整形态。以GitLab CI为例配置一个定时执行的pipeline# .gitlab-ci.yml stages: - test api_test: stage: test script: - pip install -r requirements.txt - pytest testcases/ --alluredirreports/allure-results --maxfail5 artifacts: when: always paths: - reports/ expire_in: 7 days rules: - if: $CI_PIPELINE_SOURCE schedule这里几个细节--maxfail5表示最多累积到5个失败就停止执行防止一个环境故障导致几百个用例全都等于白跑既浪费时间又浪费日志空间artifacts可以保存测试结果方便后续在CI页面直接查看rules限定只有定时任务触发才跑避免每次提交代码都跑全量回归。定时回归可以设置在每天凌晨跑一次跑完了把结果推送到群里。这样整个团队每天早上都能看到最新一次全量回归的结果而不是等有人手动点一下才跑。6. 框架搭建之外的建议真正让框架活下来的是纪律框架写完了能跑了报告也漂亮了但这只是开始。我见过太多项目框架写得花团锦簇三个月后没人用了。原因往往不是技术问题而是组织问题。第一用例评审要跟上。自动化用例写出来要有测试负责人整体过一遍看覆盖的场景是否合理、断言是否有效。不然就会出现一百条用例全是重复登录这种凑数的情况。第二跑挂的用例要当天处理。今天失败了不查原因明天再跑又累加两个失败。一周之后你看到20个失败根本无从下手。我的执行纪律是通知推送到群里接到通知的人当日处理要么修问题要么删用例绝不允许带病运行。第三框架的演进要跟得上业务。接口自动化不是一次性的不是搭完就结束了。业务在变接口在变框架也需要持续调整。我的经验是每迭代一个版本就顺手优化一层框架结构哪怕只是一点点。最后再分享一个小技巧。我的框架里有一个冒烟模式的开关接在pytest的marker上# pytest.ini markers smoke: 冒烟测试用例 P0: 核心链路用例 P1: 重要功能用例 P2: 一般功能用例跑法区别很大# 日常提交代码时的冒烟回归1分钟跑完 pytest testcases -m smoke # 每晚的全量回归10到20分钟 pytest testcases -m P0 or P1 or P2 # 上线前的核心链路回归 pytest testcases -m P0这个分级机制让我在快速反馈和全面覆盖之间找到了平衡。开发改了个小功能跑冒烟就够了大版本上线前跑全量。不搞一刀切框架才能真正嵌入研发流程。接口自动化测试框架的搭建七分在架构设计三分在代码实现。把本文里的分层思想、请求封装、数据驱动、断言分层、通知闭环这几个核心模块吃透再结合自己项目的实际情况微调一个能长期存活、真正发挥作用的框架并不难。难的是后续每一天的维护和纪律但那已经不是技术问题了。
返回列表