
干了几年测试开发我一直跟身边的同事说接口自动化是投入产出比最高的测试投资。UI自动化脆如玻璃今天一个id变了明天一个xpath挂了维护成本高到让人怀疑人生。但接口不一样接口是系统的骨架骨架稳了再怎么折腾UI都不至于塌方。Pythonrequests这个组合是我用过的接口自动化方案里最顺手的一套。requests库本身设计简洁、语义清晰配合Python的灵活性能在很短时间内搭出一套能跑业务链路的接口测试框架。本文不聊虚的从环境配置到框架搭建从登录鉴权到报告生成把我实际项目中验证过的方案完整拆给你看。1. 为什么是Pythonrequests接口自动化的选型思考1.1 接口自动化测试到底要解决什么问题做接口自动化的第一件事不是写代码而是想清楚目标。我见过不少团队一上来就追求大而全的平台最后平台成了摆设。接口自动化要解决的核心问题其实就三个回归成本、数据可信度、测试效率。回归成本每次版本迭代核心接口是否被改坏手工点页面验证链路太慢接口层跑一遍几十个用例几分钟出结果。数据可信度UI层的报错可能是前端问题接口层验证了后端逻辑的正确性出了问题能精准定位到服务端。测试效率开发自测、CI流水线、每日巡检都需要一个能快速触达后端逻辑的验证手段。接口自动化不需要覆盖所有接口优先覆盖核心交易链路、鉴权逻辑、数据状态流转。以支付场景为例下单、支付回调、订单状态查询这三段接口必须自动化覆盖因为它们是资金相关的高危链路任何改动都可能引发线上故障。1.2 requests库凭什么成为接口测试的首选Python的HTTP客户端有不少选择urllib、httpx、requests我最终常驻requests是有原因的。urllib是标准库功能其实够用但API设计偏底层光是一个URL编码就要处理好几步写起来繁琐不说可读性也差。httpx是后起之秀支持同步异步双模式性能确实好但在接口自动化这个场景里异步带来的收益远不如代码复杂度带来的成本。requests的API设计几乎就是望文生义GET请求就requests.get()POST请求就requests.post()参数用params和json两个关键字一目了然。更重要的是requests的生态成熟度。它在处理重定向、Cookie持久化、SSL证书校验、代理隧道这些坑时基本做到开箱即用。比如测试环境经常用自签名HTTPS证书urllib遇到这种场景要写一堆SSLContext配置而requests只需要一个verifyFalse参数。1.3 这个组合适合谁用如果你是测试工程师、测试开发或者是需要验证自己接口的后端开发Pythonrequests这套组合都值得掌握。它对机器配置的要求几乎为零Windows、macOS、Linux都能跑也不需要像Java那样装JDK配环境变量。Python脚本本身就是一种可执行的需求文档即使团队里有人不懂代码看到requests.post(url, jsondata)也能猜出七八分意图。2. 环境准备装对Python版本比装requests本身更重要2.1 Python版本怎么选很多人装Python时直接下载官网最新版这是第一个坑。接口自动化项目不是越新越好而是要稳。以我实际踩过的坑为例Python 3.12刚发布时有些第三方库还没适配装requests虽然没问题但要装pandas做数据处理、或者装allure-pytest做报告时就可能遇到二进制包编译失败的情况。我的建议是当前项目环境优先选Python 3.8到3.11之间的版本。3.8是经典版本兼容性极好即使到现在一些老项目的requirements.txt里还固定着python3.8。如果你要在这个项目里做异步处理或者用一些新语法特性3.10、3.11都很合适。具体选择可以参考下表版本特性兼容性表现适用场景Python 3.8稳定经典几乎全兼容老项目、团队环境受限Python 3.10match语法、类型联合大部分库已适配新项目首选Python 3.11性能提升明显少数库需升级追求性能的新项目Python 3.12最新特性部分库还在适配个人学习为主2.2 虚拟环境隔离比想象中重要接口自动化项目通常不止一个今天做下单链路明天做数据巡检每个项目的依赖版本可能互相冲突。我吃过一次大亏某个项目的requests版本需要2.28以上但另一个老项目要求requests固定在2.25结果两个项目的依赖在一个环境里打架搞到后来整个Python环境崩了只能重装。所以创建虚拟环境这个步骤一定不能省。Windows下直接用venvpython -m venv venv venv\Scripts\activatemacOS/Linux下python3 -m venv venv source venv/bin/activate激活后命令行前缀出现(venv)就说明已经进入独立的Python环境了。这时用pip安装的所有包都会被隔离在这个虚拟环境里不会污染全局Python。2.3 requests安装与验证安装requests只需要一行命令pip install requests国内网络环境不建议直接用官方PyPI源速度慢还容易超时。我一般使用清华镜像源pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple验证安装成功在Python交互式环境里执行import requests print(requests.__version__)能打印出版本号就说明环境OK了。另外pip install requests会自动带上urllib3、certifi、charset_normalizer、idna这些依赖库其中urllib3是requests的底层HTTP引擎certifi是用来做SSL证书校验的这些都不需要手动装。3. requests库实战GET、POST、Session会话的实际用法3.1 请求方法对照requests的API设计走的是一个方法对应一个HTTP动词的路线。最常用的就两个requests.get()和requests.post()。接口测试里PUT和DELETE用得相对少但requests也提供了对应方法。以GET请求为例最典型的写法import requests url https://api.example.com/api/v1/products params { page: 1, page_size: 20, category: electronics } response requests.get(url, paramsparams) print(response.status_code) print(response.json())POST请求则不同它通常需要提交数据。开发里最常见的约定是application/json格式requests里用json参数即可import requests url https://api.example.com/api/v1/login payload { username: tester01, password: Passw0rd123 } response requests.post(url, jsonpayload) print(response.status_code) print(response.text)json参数和data参数看起来差不多但有个关键区别json会自动把Python字典序列化成JSON字符串并设置Content-Type: application/jsondata则是按application/x-www-form-urlencoded格式传表单。如果用错了后端可能解析不到参数返回400错误。3.2 响应对象里面的关键信息发请求只是第一步更关键的是从响应里提取信息做断言。requests的Response对象里有几个我几乎天天用的字段response.status_codeHTTP状态码200表示成功401表示未认证403表示权限不足500表示服务端异常。response.headers响应头字典形式通常用来取Content-Type、Set-Cookie等。response.text响应体文本适合直接看内容或者做正则匹配。response.json()JSON反序列化后的Python字典接口返回JSON时最常用。有一个细节容易踩坑response.json()如果遇到响应体不是合法JSON会抛出requests.exceptions.JSONDecodeError。所以稳妥的做法是先判断Content-Type或者用try/except包一层。import requests response requests.get(https://api.example.com/health) try: data response.json() except requests.exceptions.JSONDecodeError: data {raw: response.text}3.3 Session会话保持登录态的正确处理方式接口测试里最常碰到的场景就是先登录拿Token然后带着Token去请求业务接口。如果把Token每次都手动传进headers里代码会非常啰嗦还容易漏传。requests的Session对象就是为解决这个问题设计的。Session在底层维护了一个连接池和Cookie存储。你在Session上登录一次后后续请求会自动带上该Session持有的Cookie还可以统一设置headers。import requests session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) }) # 登录此时Token被存储在Session的Cookie或者自定义header中 login_url https://api.example.com/login login_data {username: admin, password: 123456} response session.post(login_url, jsonlogin_data) # 后续请求Session自动携带登录态 profile_url https://api.example.com/user/profile profile_resp session.get(profile_url) print(profile_resp.json())这里有一个很实用的技巧如果登录后返回的Token是放在响应体里的而不是通过Set-Cookie设置的你可以手动把Token塞进Session的默认headers里这样后面的请求就都不用管Token的事了token response.json().get(data, {}).get(token) session.headers[Authorization] fBearer {token}3.4 超时与重试接口测试不能干等接口请求默认不会设置超时时间意味着如果后端卡死你的测试也会一直挂起。这在自动化场景里是致命的——一个用例卡住整个测试套件都得堵在那里。所以所有请求都必须显式设置timeout参数response requests.get(url, timeout(3, 5))timeout(3, 5)的意思是连接超时3秒读取超时5秒。这个配置比一个单一的timeout5更精细连接超时指建立TCP连接的时间上限读取超时指收到响应数据的时间上限。一个请求可能连接很快但响应很慢分开设置能更精准地判断问题。对于重试策略requests本身没有内置重试机制但可以通过urllib3的Retry配合HTTPAdapter实现。以下几点需要结合具体场景import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session requests.Session() retry Retry( total3, # 总重试次数 connect3, # 连接失败重试次数 read2, # 读取失败重试次数 backoff_factor0.5, # 重试间隔 backoff_factor * (2 ** retry_count) status_forcelist[500, 502, 503, 504] ) adapter HTTPAdapter(max_retriesretry) session.mount(https://, adapter)需要强调的是重试机制只适合幂等请求GET类。对于POST下单这类非幂等操作重试可能会导致重复下单必须谨慎设计最好结合接口幂等键来使用或者干脆不设置重试。4. 搭建接口自动化框架请求封装、断言封装与数据驱动4.1 目录结构设计的原则接口自动化框架不需要多花哨但结构要清晰不然用例一多就成一锅粥。我常用的目录结构如下api_test/ ├── config/ │ ├── __init__.py │ └── settings.py # 环境配置、全局变量 ├── common/ │ ├── __init__.py │ ├── http_client.py # requests二次封装 │ ├── assert_utils.py # 断言封装 │ ├── read_data.py # 数据读取 │ └── logger.py # 日志配置 ├── testcases/ │ ├── __init__.py │ ├── test_login.py # 登录模块用例 │ ├── test_order.py # 订单模块用例 │ └── test_pay.py # 支付模块用例 ├── data/ │ ├── login_cases.yaml # 登录用例数据 │ └── order_cases.json # 订单用例数据 ├── reports/ │ └── 2024-05-18/ # 按日期存放测试报告 └── requirements.txt这种结构的核心思路是配置、公共方法、用例、数据、报告分层隔离。修改环境配置不用动用例新增用例不用碰公共代码。4.2 请求封装把requests再包一层直接在每个用例里面调requests.post()不是不行但问题很多环境地址一变所有用例都要改日志记录不统一出了问题不好排查Token等公共逻辑散落在各处维护成本高。所以我习惯封装一个HttpClient类。import requests import logging from config.settings import BASE_URL, GLOBAL_TIMEOUT logger logging.getLogger(api_test) class HttpClient: def __init__(self, base_urlBASE_URL): self.base_url base_url.rstrip(/) self.session requests.Session() self.timeout GLOBAL_TIMEOUT def set_token(self, token): 设置全局Token self.session.headers[Authorization] fBearer {token} def request(self, method, url, **kwargs): 统一的请求入口所有请求都走这里 full_url f{self.base_url}/{url.lstrip(/)} kwargs.setdefault(timeout, self.timeout) if verify not in kwargs: kwargs[verify] False # 测试环境关掉证书校验 logger.info(f请求: {method.upper()} {full_url}) logger.info(f参数: {kwargs.get(json) or kwargs.get(data) or kwargs.get(params)}) response self.session.request(method, full_url, **kwargs) logger.info(f响应: {response.status_code} - {response.text[:200]}) return response def get(self, url, **kwargs): return self.request(GET, url, **kwargs) def post(self, url, **kwargs): return self.request(POST, url, **kwargs)封装的核心价值有两点一是统一入口任何请求都能打日志、做统计二是全项目共享配置比如verifyFalse在测试环境只需要配置一次切到生产环境时只需调整settings里的开关不用到处改代码。注意verifyFalse会产生一个InsecureRequestWarning的警告。测试环境用自签名证书时没法避免但可以通过urllib3.disable_warnings()屏蔽告警让日志更干净。4.3 断言封装状态码之外还要校验业务码一个常见的误区是只断言HTTP状态码是不是200。实际做过接口自动化的人都知道200只代表HTTP请求到达了服务端业务是否成功还要看响应体里的业务状态码。我遇到过不止一次接口返回200但code字段却是5000业务上就是失败的。所以断言的封装至少要覆盖三层HTTP状态码检查网络层和路由是否正确业务状态码检查后端业务逻辑是否成功关键业务字段检查核心数据是否符合预期class AssertUtils: staticmethod def assert_response(response, expected_status200, expected_biz_codeNone, expected_fieldsNone): assert response.status_code expected_status, \ fHTTP状态码不符, 期望{expected_status}, 实际{response.status_code} data response.json() if expected_biz_code: biz_code data.get(code) assert biz_code expected_biz_code, \ f业务状态码不符, 期望{expected_biz_code}, 实际{biz_code} if expected_fields: for key, value in expected_fields.items(): actual data.get(data, {}).get(key) assert actual value, \ f字段{key}不符, 期望{value}, 实际{actual}实际项目中断言的粒度需要根据接口的重要程度来设计。核心字段必须断言非核心字段不要过度断言否则接口一有合理变动用例就会误报测试维护成本飙升。4.4 数据驱动把用例数据从代码里抽离代码写死数据的做法只适合探索测试。真正跑回归的用例集数据必须外置方便产品和测试随时调整测试数据而不改代码。我常用YAML文件来管理用例数据因为YAML可读性好嵌套结构也清晰。以登录接口为例写一个login_cases.yaml- name: 登录成功 method: POST url: /api/v1/login data: username: admin password: 123456 expected_status: 200 expected_biz_code: 0 - name: 密码错误 method: POST url: /api/v1/login data: username: admin password: wrong_pass expected_status: 200 expected_biz_code: 1001然后配合pytest实现参数化import pytest import yaml from common.http_client import HttpClient from common.assert_utils import AssertUtils client HttpClient() def load_login_cases(): with open(data/login_cases.yaml, r, encodingutf-8) as f: return yaml.safe_load(f) pytest.mark.parametrize(case, load_login_cases()) def test_login(case): response client.post( case[url], jsoncase.get(data, {}) ) AssertUtils.assert_response( response, expected_statuscase[expected_status], expected_biz_codecase.get(expected_biz_code) )数据驱动的核心思路是让一个测试函数变成执行器数据的扩展不增加代码量。新增一个用例场景只需要在YAML里加一段配置测试代码一行都不用改。5. 业务实战登录鉴权、Token传递与跨接口链路测试5.1 登录接口与Token提取接口自动化的第一个坎往往是登录。登录方式多种多样有基于Cookie会话的有基于JWT Token的也有OAuth2.0的。以一个典型的JSON登录为例登录成功后的响应体往往长这样{ code: 0, message: success, data: { token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImV4cCI6MTcxNjAwMDAwMH0.signature } }我在框架里处理Token的逻辑是在所有依赖登录的用例执行之前先通过一个session级别的fixture完成登录并把Token注入HttpClient实例从而让所有用例自动携带Token。import pytest from common.http_client import HttpClient pytest.fixture(scopesession, autouseTrue) def login_fixture(): client HttpClient() login_resp client.post(/api/v1/login, json{ username: admin, password: 123456 }) assert login_resp.status_code 200 token login_resp.json()[data][token] client.set_token(token) return client这里注意scopesession的含义整个测试会话只执行一次登录所有用例共用这个登录态。这样既节省了重复登录的开销也保证了用例之间的连贯性。5.2 跨接口的业务链路下单一支付一查询接口自动化真正的价值在于串联链路。单测一个接口问题还算好定位一旦涉及多个接口之间的数据传递才能模拟出真实用户的操作路径。以订单支付链路为例创建订单拿到order_id对订单发起支付需要order_id和支付方式查询订单状态确认订单已变成已支付这三个接口之间是有数据依赖的第二个和第三个接口都依赖第一个接口返回的order_id。处理这种依赖我通常用一个context字典的顺序执行方式def test_order_payment_flow(): client HttpClient() # 登录 login_resp client.post(/api/v1/login, json{username: admin, password: 123456}) token login_resp.json()[data][token] client.set_token(token) # Step 1: 创建订单 order_resp client.post(/api/v1/order/create, json{ product_id: 1001, quantity: 1, address: 测试地址 }) assert order_resp.status_code 200 order_id order_resp.json()[data][order_id] # Step 2: 支付订单 pay_resp client.post(/api/v1/order/pay, json{ order_id: order_id, pay_type: alipay }) assert pay_resp.status_code 200 assert pay_resp.json()[data][pay_status] success # Step 3: 查询订单状态 query_resp client.get(f/api/v1/order/detail?order_id{order_id}) assert query_resp.status_code 200 assert query_resp.json()[data][order_status] paid这段代码要传达的核心思想是接口链路的测试要按照用户真实操作顺序来组织每一步的数据都要从前一步的响应中提取。如果第2步失败用例会在第2步中断并抛错你就能准确定位是哪一段出了问题。但是如果这里用pytest.mark.parametrize把三段拆成三个独立用例order_id的传递就成了难题这也是我选择顺序执行方式的原因。5.3 加密接口的应对思路不少金融和政企项目在接口层面做了数据加密常见的是AESRSA组合。测试这种接口关键点是和开发对齐加密逻辑确认用的是AES的哪种模式CBC、ECB或GCM密钥如何获取。以AES-CBC为例加密和解密的代码可以这样写from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 key b0123456789abcdef # 16字节密钥 iv babcdef9876543210 # 16字节偏移量 def aes_encrypt(data: str) - str: cipher AES.new(key, AES.MODE_CBC, iv) encrypted cipher.encrypt(pad(data.encode(utf-8), AES.block_size)) return base64.b64encode(encrypted).decode(utf-8) def aes_decrypt(data: str) - str: cipher AES.new(key, AES.MODE_CBC, iv) decrypted unpad(cipher.decrypt(base64.b64decode(data)), AES.block_size) return decrypted.decode(utf-8)加密接口的测试要格外注意断言时不要直接拿加密后的响应体做全文匹配而是先解密再断言业务字段。调试阶段可以写一个独立的解密脚本单独验证接口返回数据的正确性。6. 测试报告与持续集成单机跑通还要能定时执行6.1 pytest集成与报告生成接口自动化项目我基本全用pytest作为测试框架因为它和requests配合起来很自然fixture机制处理登录态和资源清理非常方便参数化支持又极其友好。运行用例加上-v参数可以看到每个用例的执行情况。生成HTML报告最简单的方式是pytest-htmlpip install pytest-html pytest testcases/ -v --htmlreports/report.html --self-contained-html--self-contained-html参数很关键它会把CSS和JS内嵌到HTML文件中生成单一文件报告分享给别人时不需要附带额外的资源目录。如果你想要更专业的报告可以用Allure。Allure报告从用例步骤、历史趋势到缺陷分类都做得很好适合团队展示和长期统计。基本用法pip install allure-pytest pytest testcases/ -v --alluredirreports/allure-results allure serve reports/allure-results6.2 日志收集测试失败的定位利器报告能告诉你失败在哪但没法告诉你为什么失败。所以我建议在框架里加上日志模块把每个请求的URL、请求参数、响应状态码、响应体关键内容记录下来。日志级别用INFO失败时在report里附上对应的日志文件路径。import logging logger logging.getLogger(api_test) logger.setLevel(logging.INFO) if not logger.handlers: handler logging.FileHandler(logs/api_test.log, encodingutf-8) formatter logging.Formatter(%(asctime)s - %(levelname)s - %(message)s) handler.setFormatter(formatter) logger.addHandler(handler)在HttpClient的请求方法里我前面已经加了日志输出。这样一次完整的请求链路在日志里能清晰看到发什么、收什么、断在哪。实际排查问题时日志比报告好用得多。6.3 Jenkins定时任务配置测试写好了要在CI环境里跑我用的最多的是Jenkins。在Jenkins里新建一个自由风格项目配置Git仓库地址构建步骤选执行shell或执行Windows批处理命令然后填入cd ${WORKSPACE} pip install -r requirements.txt pytest testcases/ -v --htmlreports/report.html --self-contained-html然后配置构建后操作里的Publish HTML reports把reports/report.html设置成展示页面。最后在构建触发器里勾选定时构建填入H 2 * * *表示每天凌晨2点跑一次。接口自动化跑定时任务有两个好处一是每天早晨到公司就能看到昨晚的回归结果有问题早上就处理二是对线上环境做持续健康巡检接口挂了你可能是第一个知道的人。7. 高频报错排查实录429、超时、SSL与编码问题7.1 429 Too Many Requests请求频率撞上服务端限流最近在跑一个接口巡检脚本时频繁遇到429 Too Many Requests。这个状态码的意思是请求太频繁触发了服务端的限流策略。我的第一反应不是改代码而是去确认限流规则是单IP限流还是账号维度限流单位时间窗口是多少分接口限流还是全局限流排查清楚后对症下药有几种方案加延时简单粗暴如果允许降速在每次请求前time.sleep(1)就把频率降下来了。重试退避如果确实需要用高频率跑就要加上带退避的重试机制第一次失败等1秒重试第二次等2秒第三次等4秒指数退避能有效降低与限流策略的硬碰硬。分布式来源IP当服务端按IP限流时用多台执行机或代理池分散请求来源。import time import random def request_with_backoff(session, method, url, max_retries3, **kwargs): for attempt in range(max_retries): response session.request(method, url, **kwargs) if response.status_code 429: wait_time 2 ** attempt random.uniform(0, 1) time.sleep(wait_time) continue return response return response # 最后还是一个429就返回给调用方判断7.2 连接超时与Proxy环境变量干扰ConnectionError几乎是每个接口测试人都会遇到的报错。常见原因有三类网络不通目标服务器IP不可达、端口没开放。代理干扰操作系统设置了HTTP_PROXY或HTTPS_PROXY环境变量requests默认会读取这些代理配置导致请求走了代理但代理不可用。目标服务没启动后端服务挂了TCP连接直接被拒绝。排查顺序我一般是先ping目标服务器再telnet端口通不通然后用curl -v裸测一下接口最后看Python代码里的代理配置。# 临时清除代理环境变量 unset HTTP_PROXY HTTPS_PROXY ALL_PROXY如果公司环境必须走代理就在requests里显式指定proxies { http: http://proxy.example.com:8080, https: http://proxy.example.com:8080 } response requests.get(url, proxiesproxies)7.3 SSLError证书校验引发的信任危机测试环境用自签名HTTPS证书时requests.get()默认会校验证书遇到不受信任的证书会抛出requests.exceptions.SSLError。解决方式有两种安全级别不同。方案一测试环境快速解决response requests.get(url, verifyFalse)方案二捕获异常并忽略特定场景:import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) response requests.get(url, verifyFalse)生产环境或涉及敏感数据的接口我不建议关闭证书校验而应该把证书文件下载下来通过verify/path/to/cert.pem传入。这是安全底线问题测试环境可以妥协生产环境不能。7.4 中文乱码与UnicodeEncodeError接口返回的中文响应体在控制台打印乱码是最常见的小问题。大部分原因出在控制台编码和响应体编码不一致。requests会从HTTP头里的Content-Type字段自动判断编码但如果接口没返回charset参数requests默认可能是ISO-8859-1中文自然就乱码了。解决方案是手动设置编码response requests.get(https://api.example.com/user/info) response.encoding utf-8 print(response.text)如果已经拿到response.text发现乱码可以用response.content拿到字节流然后手动解码content response.content.decode(utf-8, errorsignore)日志文件里写中文时如果遇到UnicodeEncodeError检查两点FileHandler是否设置了encodingutf-8以及写入的字符串是否混入了不可编码的emoji。8. 框架维护与扩展从能跑到跑得舒服写到这里你已经能搭建出一套可用的接口自动化框架了。最后分享几个我在维护阶段觉得特别重要的经验。第一不要过度设计。框架够用就行不要一开始就引入复杂的数据工厂、多环境引擎、动态参数注入这些模块。先把核心链路跑通让团队看到价值后续再根据需要渐进增强。第二用例的独立性比减少重复代码更重要。两个用例如果相互依赖其中一个失败会导致另一个也失败排查问题时会很困惑。设计用例时尽量让每个用例可以独立运行。第三环境切换要配置化。测试环境、预发布环境、生产环境的地址和账号信息放到配置里不要写死在代码中。我用的是config/settings.py加环境变量覆盖的方式切换环境时只需改一个环境变量。第四定期清理测试数据。接口自动化跑久了数据库里会积累大量测试产生的脏数据。我在项目中加了数据清理脚本每天定时执行避免测试数据干扰真实业务。最后再提一个小技巧框架跑完以后可以把测试结果推送到企业微信或者飞书群用webhook发一条消息包含通过率、失败用例、报告链接。这样整个研发团队都能看到自动化测试的产出而不是只有测试自己知道。这也是让接口自动化价值被看见的一个有效方式。