Python接口自动化测试框架:pytest核心功能与实战指南
1. 项目概述:为什么是pytest?
如果你已经跟着这个系列走到了第十一天,那说明你已经跨过了Selenium操作浏览器、理解了PO设计模式,甚至可能已经用unittest写过一些脚本了。现在,我们来到了接口自动化测试的门口,而手里拿着的钥匙,就是 pytest 。很多新手会问,unittest不是Python自带的吗,为什么还要学pytest?这就像问“为什么有了手动挡还要开自动挡”一样。unittest是基础,它教会你测试的结构和思想;但pytest是生产力工具,它能让你写得更快、更爽、更优雅。
pytest不是一个简单的测试框架,它更像是一个测试平台。它几乎兼容unittest,但提供了更简洁的语法、更强大的功能(如参数化、夹具、插件生态)和更友好的输出。在接口自动化中,我们面对的是大量的、结构化的请求与响应断言。pytest的灵活性,比如用 @pytest.mark.parametrize 轻松实现数据驱动,用 fixture 来管理测试前后的资源(如数据库连接、登录态),能让我们从繁琐的重复代码中解放出来,专注于测试逻辑本身。今天,我们就从零开始,彻底搞懂pytest的用法、规则、配置和标记,为你搭建一个坚实、高效的接口自动化测试框架打下基础。
2. pytest核心规则与项目结构
在开始写第一个测试用例之前,我们必须先理解pytest的“游戏规则”。它有一套默认的发现机制,遵循这些规则,你的测试才能被自动找到并执行。
2.1 默认的测试发现规则
pytest会递归搜索你指定的目录(默认是当前目录),寻找符合以下规则的文件和函数/方法:
- 文件命名 :测试文件应以
test_开头,或以_test.py结尾。例如,test_login.py或login_test.py都是有效的。 - 测试函数/方法命名 :测试函数(在类外部)或测试方法(在类内部)应以
test_开头。这是最重要的标识。 - 测试类命名 :测试类应以
Test开头,且该类不能有__init__方法。类中的测试方法同样需要以test_开头。
注意 :这些是默认规则。虽然可以通过配置修改,但在团队协作中,严格遵守这些约定能极大降低沟通成本,避免“为什么我的用例没跑?”这类问题。
2.2 推荐的项目目录结构
一个清晰的目录结构是维护大型自动化项目的基石。对于接口自动化,我推荐如下结构:
your_project/
├── common/ # 公共模块
│ ├── __init__.py
│ ├── logger.py # 日志模块
│ ├── request_util.py # 封装的请求工具类
│ └── db_util.py # 数据库操作工具(如需)
├── config/ # 配置文件
│ ├── __init__.py
│ ├── config.py # 基础配置(如环境URL)
│ └── pytest.ini # pytest配置文件(核心!)
├── test_data/ # 测试数据文件
│ ├── login_data.yaml
│ └── order_data.json
├── test_cases/ # 测试用例目录
│ ├── __init__.py
│ ├── test_login.py
│ └── test_order.py
├── conftest.py # 全局夹具定义文件(核心!)
├── requirements.txt # 项目依赖
└── run.py # 主运行入口(可选)
关键文件解析 :
-
conftest.py:这是pytest的“魔法”文件。你可以在这里定义 夹具(fixture) ,这些夹具可以被同一目录及子目录下的所有测试文件使用。它是实现测试前置后置操作(setup/teardown)复用的核心。 -
pytest.ini:pytest的配置文件。在这里可以修改默认行为,比如指定命令行参数、注册标记、配置日志等。 -
requirements.txt:列出所有依赖包,如pytest,requests,pytest-html等,方便团队环境统一。
2.3 第一个pytest测试用例
让我们从一个最简单的例子开始,感受pytest的简洁。假设我们要测试一个简单的计算函数。
首先,在 test_cases 目录下创建 test_demo.py :
# test_cases/test_demo.py
def add(a, b):
return a + b
def test_add_two_positive_numbers():
"""测试两个正数相加"""
result = add(3, 5)
assert result == 8
def test_add_positive_and_negative():
"""测试正数与负数相加"""
result = add(10, -4)
assert result == 6
class TestAddFunction:
"""将add函数的测试用例组织在一个类中"""
def test_add_with_zero(self):
"""测试与0相加"""
result = add(7, 0)
assert result == 7
def test_add_two_negative_numbers(self):
"""测试两个负数相加"""
result = add(-2, -3)
# 这里故意写一个错误断言,看看pytest如何报告失败
assert result == -4 # 实际结果是-5,此断言会失败
打开终端,切换到项目根目录,运行命令: pytest test_cases/test_demo.py -v -v 参数表示输出详细信息。你会看到清晰的通过和失败报告,对于失败的用例,pytest会清晰地指出期望值和实际值,这比unittest的 assertEqual 直观得多。
实操心得 :从一开始就养成用 assert 语句的习惯。pytest会重写 assert ,在断言失败时提供丰富的上下文信息,这是它的一大优势。不要再用 self.assertEqual(expected, actual) 这种unittest风格的写法了。
3. pytest核心功能深度解析
掌握了基本规则,我们来深入pytest最强大的几个功能,这些是构建高效接口自动化框架的利器。
3.1 夹具(Fixture):测试的脚手架
Fixture是pytest的灵魂。你可以把它理解为测试用例的“前置条件”和“后置清理”的提供者。它比unittest的 setUp / tearDown 更灵活,可以跨文件、跨类共享,并且支持作用域控制。
定义一个简单的Fixture (通常在 conftest.py 中定义):
# conftest.py
import pytest
import requests
@pytest.fixture(scope="function")
def get_login_token():
"""获取用户登录token的夹具,每个测试函数执行一次。"""
print("\n--- 开始执行登录,获取token ---")
login_url = "https://api.example.com/login"
payload = {"username": "test_user", "password": "test123"}
# 模拟登录请求
response = requests.post(login_url, json=payload)
token = response.json().get("access_token")
yield token # 将token提供给测试用例使用
print("\n--- 测试结束,可在此处执行清理(如登出)---")
# yield之后的代码,无论测试成功还是失败,都会执行,用于清理。
在测试用例中使用Fixture :
# test_cases/test_user_info.py
def test_get_user_profile(get_login_token):
"""测试获取用户信息,需要登录token"""
headers = {"Authorization": f"Bearer {get_login_token}"}
# 使用获取到的token发起请求...
# assert ...
print(f"使用的Token是:{get_login_token}")
Fixture的作用域(scope) :
function(默认):每个测试函数运行一次。class:每个测试类运行一次,该类中的所有方法共享同一个fixture实例。module:每个.py文件运行一次。package:每个包(目录)运行一次。session:整个测试会话(一次pytest运行)只运行一次。
注意事项 :对于像数据库连接、HTTP会话(如
requests.Session)这类创建成本较高的资源,使用scope="session"可以显著提升测试速度。但务必注意,如果测试会修改共享资源的状态(比如向共享数据库插入数据),就需要考虑测试隔离性,可能不适合用session作用域。
3.2 参数化(Parametrize):数据驱动的核心
接口测试中,我们经常需要用多组数据测试同一个接口。手动复制粘贴用例是低效且容易出错的。 @pytest.mark.parametrize 装饰器完美解决了这个问题。
基础参数化 :
import pytest
@pytest.mark.parametrize("test_input, expected", [
("3+5", 8),
("2+4", 6),
("6*9", 54),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
在接口自动化中的实战应用 : 假设我们测试一个登录接口,需要验证正常登录、密码错误、用户不存在等多种情况。
# test_cases/test_login.py
import pytest
import requests
class TestLoginAPI:
base_url = "https://api.example.com"
@pytest.mark.parametrize("username, password, expected_code, expected_msg", [
("correct_user", "correct_pwd", 200, "登录成功"),
("correct_user", "wrong_pwd", 401, "密码错误"),
("non_exist_user", "any_pwd", 404, "用户不存在"),
("", "some_pwd", 400, "用户名不能为空"),
])
def test_login(self, username, password, expected_code, expected_msg):
"""数据驱动测试登录接口"""
url = f"{self.base_url}/login"
payload = {"username": username, "password": password}
response = requests.post(url, json=payload)
# 断言状态码
assert response.status_code == expected_code
# 断言返回消息
response_json = response.json()
assert response_json.get("message") == expected_msg
实操心得 :将测试数据与测试逻辑分离是最佳实践。对于复杂的数据,可以将其存放在YAML或JSON文件中,然后在参数化时读取。这样,当测试数据需要更新时,你不需要去修改Python代码。
3.3 标记(Mark):给测试用例分类
当你有成百上千个测试用例时,如何有选择地运行它们?比如只运行冒烟测试、或者跳过某个已知问题的用例?pytest的标记系统提供了解决方案。
内置标记 :
@pytest.mark.skip(reason=“...” ):无条件跳过该测试。@pytest.mark.skipif(condition, reason=“...” ):如果条件为真,则跳过。@pytest.mark.xfail(reason=“...” ):预期该测试会失败,如果它失败了,pytest会报告为“xfailed”(预期失败);如果它通过了,则报告为“xpassed”(意外通过),这常用于测试尚未修复的Bug。
自定义标记 : 你可以在 pytest.ini 文件中注册自定义标记,并赋予它们意义。
# pytest.ini
[pytest]
markers =
smoke: 冒烟测试用例(核心功能)
regression: 回归测试用例
slow: 运行缓慢的测试用例
api: 接口测试用例
在测试用例上使用标记:
# test_cases/test_order.py
import pytest
@pytest.mark.smoke
@pytest.mark.api
def test_create_order():
"""创建订单 - 冒烟测试 & 接口测试"""
pass
@pytest.mark.regression
@pytest.mark.slow
def test_query_order_history():
"""查询历史订单 - 回归测试 & 慢速测试"""
pass
通过标记运行测试 :
- 只运行冒烟测试:
pytest -m smoke - 运行冒烟测试和接口测试:
pytest -m “smoke or api” - 运行非慢速的回归测试:
pytest -m “regression and not slow”
重要提示 :使用自定义标记前, 必须在
pytest.ini中注册 ,否则运行时会收到警告。这是一个很好的实践,它让标记的定义对团队可见,避免混淆。
4. pytest配置与插件生态
pytest的强大,一半来自于其高度可配置性,另一半来自于其丰富的插件生态。
4.1 核心配置文件:pytest.ini
pytest.ini 应该放在项目根目录。下面是一个功能丰富的配置示例:
# pytest.ini
[pytest]
# 1. 指定测试文件搜索路径(可配置多个)
testpaths = test_cases
# 2. 指定Python文件命名模式
python_files = test_*.py *_test.py
# 3. 指定测试类和函数的命名模式
python_classes = Test*
python_functions = test_*
# 4. 注册自定义标记(防止运行时警告)
markers =
smoke: 冒烟测试
regression: 回归测试
slow: 运行缓慢的测试
api: 接口测试
# 5. 添加默认命令行参数
# -v: 详细输出
# --tb=short: 当测试失败时,只显示简短的追溯信息,更清晰
# --strict-markers: 严格检查标记,如果使用了未注册的标记会报错
# --html=report.html: 使用pytest-html插件生成HTML报告(需先安装)
addopts = -v --tb=short --strict-markers --html=reports/report.html --self-contained-html
# 6. 配置日志(需配合logging模块)
log_cli = true
log_cli_level = INFO
log_file = logs/pytest_run.log
log_file_level = DEBUG
4.2 必备插件推荐
通过 pip install 安装这些插件,能让你如虎添翼。
-
pytest-html :生成美观的HTML测试报告。
- 安装:
pip install pytest-html - 使用:在
pytest.ini的addopts中添加--html=reports/report.html,或命令行直接运行pytest --html=report.html。
- 安装:
-
pytest-xdist :实现测试的分布式运行,多CPU并行,大幅缩短测试时间。
- 安装:
pip install pytest-xdist - 使用:
pytest -n auto(auto会自动检测CPU核心数)
- 安装:
-
pytest-rerunfailures :对失败的测试用例进行重跑。在UI自动化或网络不稳定的接口测试中非常有用。
- 安装:
pip install pytest-rerunfailures - 使用:
pytest --reruns 3(失败后重跑3次)或pytest --reruns 3 --reruns-delay 2(每次重跑间隔2秒)
- 安装:
-
pytest-ordering :控制测试用例的执行顺序(谨慎使用!测试用例在理想状态下应该是独立的)。
- 安装:
pip install pytest-ordering - 使用:用
@pytest.mark.run(order=1)装饰器标记用例。
- 安装:
-
pytest-base-url (或 pytest-variables ):方便地管理不同环境(测试/预发/生产)的基础URL。
- 安装:
pip install pytest-base-url - 在
pytest.ini中配置:base_url = https://test.env.com - 在fixture或测试用例中通过
request.config.getoption(“--base-url”)获取。
- 安装:
实操心得 :不要过度依赖 pytest-ordering 。如果测试用例之间有强依赖,说明你的测试设计可能有问题。优先考虑使用fixture来管理状态和依赖。对于环境配置,我更推荐使用独立的 config.py 文件,通过环境变量来切换,这样更灵活。
5. 接口自动化实战:构建测试框架骨架
现在,我们把所有知识串联起来,搭建一个简易但完整的接口自动化测试框架骨架。这个骨架包含了配置管理、请求封装、夹具管理和测试用例。
5.1 步骤一:环境与依赖准备
创建项目目录,并安装核心依赖。
# 创建项目目录
mkdir api_auto_framework && cd api_auto_framework
# 创建虚拟环境(推荐)
python -m venv venv
# 激活虚拟环境
# Windows: venv\Scripts\activate
# Mac/Linux: source venv/bin/activate
# 创建requirements.txt并写入内容
echo “pytest>=7.0.0
requests>=2.28.0
pyyaml>=6.0
pytest-html>=3.2.0
pytest-xdist>=3.2.0
pytest-rerunfailures>=10.3” > requirements.txt
# 安装依赖
pip install -r requirements.txt
5.2 步骤二:编写配置文件与工具类
1. 环境配置 ( config/config.py ) :
# config/config.py
import os
class Config:
"""配置类,根据环境变量加载不同配置"""
# 基础URL,通过环境变量切换
ENV = os.getenv(“TEST_ENV”, “test”).lower() # 默认测试环境
if ENV == “prod”:
BASE_URL = “https://api.production.com”
elif ENV == “staging”:
BASE_URL = “https://api.staging.com”
else:
BASE_URL = “https://api.test.com”
# 超时时间
TIMEOUT = 10
# 日志级别
LOG_LEVEL = “INFO”
# 数据库配置(示例)
DB_HOST = os.getenv(“DB_HOST”, “localhost”)
DB_PORT = int(os.getenv(“DB_PORT”, 3306))
2. 请求工具封装 ( common/request_util.py ) : 封装requests库,加入日志、异常处理、通用断言,是接口自动化的关键一步。
# common/request_util.py
import requests
import logging
from config.config import Config
class RequestUtil:
"""HTTP请求工具类"""
session = None
def __init__(self):
self.logger = logging.getLogger(__name__)
if RequestUtil.session is None:
RequestUtil.session = requests.Session() # 使用会话保持,提升性能
# 可以在这里为session添加默认请求头,如User-Agent
RequestUtil.session.headers.update({
“User-Agent”: “ApiAutoTestFramework/1.0”
})
def send_request(self, method, url, **kwargs):
"""发送请求的核心方法
Args:
method: 请求方法,'get', 'post', 'put', 'delete'
url: 请求地址,可以是相对路径(会自动拼接BASE_URL)
**kwargs: 传递给requests.request的其他参数,如json, params, headers
Returns:
requests.Response对象
"""
# 如果url不是以http开头,则拼接基础URL
if not url.startswith(“http”):
url = Config.BASE_URL + url
self.logger.info(f“请求方法: {method.upper()}”)
self.logger.info(f“请求URL: {url}”)
if ‘json’ in kwargs:
self.logger.info(f“请求体: {kwargs[‘json’]}”)
if ‘params’ in kwargs:
self.logger.info(f“请求参数: {kwargs[‘params’]}”)
try:
response = RequestUtil.session.request(method, url, timeout=Config.TIMEOUT, **kwargs)
self.logger.info(f“响应状态码: {response.status_code}”)
# 注意:对于大响应体,谨慎打印,可以只打印前N个字符或特定字段
self.logger.info(f“响应体: {response.text[:500]}...”) # 只打印前500字符
return response
except requests.exceptions.RequestException as e:
self.logger.error(f“请求发生异常: {e}”)
raise
# 提供便捷方法
def get(self, url, **kwargs):
return self.send_request(‘get’, url, **kwargs)
def post(self, url, **kwargs):
return self.send_request(‘post’, url, **kwargs)
def put(self, url, **kwargs):
return self.send_request(‘put’, url, **kwargs)
def delete(self, url, **kwargs):
return self.send_request(‘delete’, url, **kwargs)
5.3 步骤三:定义全局夹具(conftest.py)
在项目根目录创建 conftest.py ,定义测试用例共享的资源。
# conftest.py
import pytest
from common.request_util import RequestUtil
@pytest.fixture(scope=“session”)
def api_client():
"""提供一个全局的API请求客户端"""
client = RequestUtil()
yield client
# session结束后可以做一些清理,比如关闭session(但requests.Session通常不需要)
# 如果使用了其他需要关闭的资源,如数据库连接,在这里关闭
print(“\n所有测试执行完毕,可进行全局清理。”)
@pytest.fixture(scope=“function”)
def login_token(api_client):
"""获取登录token,每个测试函数执行一次。这是一个依赖其他fixture的fixture。"""
# 假设登录接口
login_url = “/auth/login”
login_data = {
“username”: “standard_user”, # 在实际项目中,应从配置或数据文件读取
“password”: “secret_sauce”
}
response = api_client.post(login_url, json=login_data)
# 这里应该做更健壮的断言,确保登录成功
assert response.status_code == 200
token = response.json().get(“access_token”)
if not token:
pytest.fail(“登录失败,未能获取到token”)
yield token
# 如果需要,可以在这里调用登出接口
# api_client.post(“/auth/logout”, headers={“Authorization”: f“Bearer {token}”})
5.4 步骤四:编写并运行测试用例
现在,我们可以用上面搭建的框架来写一个真实的测试用例了。
测试用例文件 ( test_cases/test_demo_api.py ) :
# test_cases/test_demo_api.py
import pytest
import allure # 可选:使用allure报告增强描述
class TestUserAPI:
"""用户相关接口测试"""
@pytest.mark.smoke
@pytest.mark.api
def test_get_current_user(self, api_client, login_token):
"""测试获取当前用户信息(冒烟测试)"""
headers = {“Authorization”: f“Bearer {login_token}”}
response = api_client.get(“/user/me”, headers=headers)
# 断言状态码
assert response.status_code == 200
# 断言响应体结构
user_info = response.json()
assert “id” in user_info
assert “username” in user_info
assert user_info[“username”] == “standard_user” # 根据登录用户断言
@pytest.mark.parametrize(“user_id, expected_code”, [
(1, 200),
(99999, 404), # 不存在的用户
(“invalid”, 400), # 无效的用户ID格式
])
def test_get_user_by_id(self, api_client, login_token, user_id, expected_code):
"""参数化测试:根据用户ID查询用户"""
headers = {“Authorization”: f“Bearer {login_token}”}
response = api_client.get(f“/user/{user_id}”, headers=headers)
assert response.status_code == expected_code
运行测试 : 在项目根目录下执行:
# 运行所有测试
pytest
# 运行带有smoke标记的测试
pytest -m smoke
# 运行测试并生成HTML报告(已在pytest.ini配置)
pytest
# 报告会生成在 reports/report.html
# 使用2个worker并行运行测试
pytest -n 2
# 运行失败重试3次
pytest --reruns 3
6. 常见问题与排查技巧实录
在实际使用pytest进行接口自动化的过程中,你一定会遇到各种各样的问题。这里记录了一些典型问题和我踩过的坑。
6.1 问题一:测试用例发现了,但为什么不执行?
现象 :运行 pytest 命令后,输出显示“collected 0 items”。 排查 :
- 检查命名 :首先确认你的测试文件和测试函数/方法是否遵循了命名规则(
test_开头或_test.py结尾)。 - 检查
__init__.py:确保测试文件所在的目录或其父目录中存在__init__.py文件(可以是空文件)。虽然新版本Python的命名空间包不一定需要,但pytest的某些发现机制下,有它会更保险。 - 检查导入路径 :如果测试文件在子目录中,确保项目根目录在Python的模块搜索路径中。通常,在项目根目录下运行
pytest即可。 - 使用
pytest --collect-only:这个命令会显示pytest发现了哪些测试项,但不执行。你可以用它来确认你的测试是否被正确识别。
6.2 问题二:Fixture找不到或报错“fixture ‘xxx‘ not found”
现象 :测试用例中引用的fixture名称报错。 排查 :
- 检查fixture定义位置 :确保fixture定义在测试文件内,或者定义在
conftest.py中,并且该conftest.py位于测试文件的当前目录或父目录中。pytest的发现机制是向上查找的。 - 检查fixture作用域 :如果你在一个
scope=”session”的fixture中,引用了另一个scope=”function”的fixture,这是可以的(session的生命周期覆盖function)。但反过来不行(function不能引用session?这里表述有误,function可以引用session,因为session生命周期更长)。实际上,fixture可以引用作用域相同或更广的fixture。 - 检查fixture名称拼写 :最简单也最容易犯的错误。
6.3 问题三:参数化测试时,大量测试数据导致报告混乱
现象 :用了 @pytest.mark.parametrize 后,测试报告里每个数据组合都显示为一条独立的测试,名字是 test_func[param1] , test_func[param2] ,当数据很多时,报告可读性差。 解决方案 : 使用 pytest.param 和 ids 参数来美化测试ID。
import pytest
@pytest.mark.parametrize(“username, password, expected”, [
pytest.param(“admin”, “admin123”, 200, id=“correct_admin”),
pytest.param(“admin”, “wrong”, 401, id=“wrong_pwd”),
pytest.param(“”, “admin123”, 400, id=“empty_user”),
], ids=[“正确管理员”, “错误密码”, “空用户名”]) # ids参数可以覆盖pytest.param中的id
def test_login(username, password, expected):
pass
这样在报告中,测试项会显示为 test_login[正确管理员] ,而不是 test_login[admin-admin123-200] ,一目了然。
6.4 问题四:如何优雅地处理测试数据?
痛点 :测试数据硬编码在Python文件中,难以维护,非技术人员无法参与数据准备。 最佳实践 :数据与代码分离。
- 使用YAML/JSON文件 :对于结构化的数据,YAML的可读性更好。
# test_data/login_cases.yaml - case_id: TC_LOGIN_001 title: “正常登录” username: “standard_user” password: “secret_sauce” expected_code: 200 expected_msg: “登录成功” - case_id: TC_LOGIN_002 title: “密码错误” username: “standard_user” password: “wrong” expected_code: 401 expected_msg: “密码错误” - 在Fixture中读取数据 :
# conftest.py import pytest import yaml import os @pytest.fixture(scope=“session”) def login_test_data(): data_file = os.path.join(os.path.dirname(__file__), “test_data”, “login_cases.yaml”) with open(data_file, ‘r’, encoding=‘utf-8’) as f: data = yaml.safe_load(f) return data - 在参数化中引用 :
注意:这里# test_login.py import pytest class TestLogin: @pytest.mark.parametrize(“case”, login_test_data(), ids=lambda case: case[‘title’]) def test_login(self, api_client, case): response = api_client.post(“/login”, json={ “username”: case[‘username’], “password”: case[‘password’] }) assert response.status_code == case[‘expected_code’] assert response.json()[‘message’] == case[‘expected_msg’]login_test_data需要是一个返回列表的fixture,并且作用域至少是module或session。
6.5 问题五:测试依赖外部服务不稳定,导致偶发性失败
场景 :被测接口依赖第三方服务或数据库,有时响应慢或超时,导致测试不是由于业务逻辑错误,而是由于环境问题失败。 解决方案 :
- 使用
pytest-rerunfailures插件 :这是最简单直接的方法,给不稳定的用例一次“复活”机会。pytest --reruns 3 --reruns-delay 2。 - Mock(模拟) :对于非核心的、不稳定的依赖,可以在单元测试或集成测试中将其Mock掉。使用
unittest.mock或pytest-mock插件。这要求你对代码结构有一定控制力。 - 增加超时和重试逻辑 :在你封装的请求工具类(如
RequestUtil)中,加入重试机制。可以使用tenacity库或requests的适配器来实现。 - 环境隔离 :尽可能搭建稳定、独立的测试环境。对于接口自动化,使用Mock Server(如WireMock, Mockoon)来模拟依赖的上下游服务,是当前非常流行的做法。
踩过这些坑之后,我的体会是,pytest框架本身并不复杂,难的是如何利用它的特性,结合良好的工程实践(如分层设计、数据分离、依赖管理),构建出一个健壮、可维护、高效的自动化测试框架。今天的这些内容,从规则到配置,从标记到实战,希望能为你铺平接口自动化测试的道路。记住,框架是工具,清晰的测试思路和良好的代码结构才是灵魂。
更多推荐
所有评论(0)