Python+Pytest+Requests+Allure+CI/CD:从零搭建企业级接口自动化测试框架

📅 2026/7/2 23:16:01
Python+Pytest+Requests+Allure+CI/CD:从零搭建企业级接口自动化测试框架
1. 项目概述与核心价值最近在团队里做了一次技术分享主题就是“从零搭建一个接口自动化测试框架”。之所以选这个主题是因为我发现很多同学无论是刚入行的测试新人还是有一定经验的开发在面对一个全新的项目或者接手一个老旧的测试脚本集合时常常会感到无从下手。要么是写一堆零散的、难以维护的脚本要么就是被网上各种复杂的框架概念吓退。其实搭建一个清晰、高效、可维护的自动化测试框架并没有想象中那么难。今天我就把自己用 Python Pytest Requests Allure CI/CD 这套组合拳搭建框架的完整思路和实操过程掰开揉碎了分享给大家。这个框架方案已经在我们多个中大型项目中稳定运行不仅提升了回归测试的效率更重要的是让测试用例成为了活的、可追踪的文档并且能无缝集成到开发流程中。简单来说这个框架能帮你解决几个核心痛点第一告别脚本“一次性”的尴尬通过良好的结构设计让测试代码像业务代码一样易于维护和扩展。第二生成直观、专业的测试报告让测试结果不再是冷冰冰的“Pass/Fail”而是有步骤、有截图如果需要、有日志的完整故事。第三实现自动化测试的“无人值守”通过CI/CD流水线每次代码提交都能自动触发测试及时发现问题。无论你是想系统学习接口自动化还是正在为团队寻找一个靠谱的测试方案我相信接下来的内容都会对你有所帮助。我们不只是讲工具怎么用更会深入探讨为什么这么设计以及在实际落地时那些容易踩坑的细节。2. 技术栈选型与整体架构设计2.1 核心组件深度解析在动手写代码之前选对工具是成功的一半。我们选择的这套技术栈Python, Pytest, Requests, Allure, CI/CD是经过大量项目实践验证的“黄金组合”每一环都承担着不可替代的角色。Python作为脚本语言语法简洁、生态丰富是自动化测试领域的绝对主流。它降低了测试工程师的编码门槛同时其强大的库支持如Requests用于HTTP请求Pytest作为测试框架让我们能专注于测试逻辑本身而不是语言特性。Pytest是我们测试框架的“骨架”和“发动机”。它远不止一个断言工具。其核心优势在于三点一是灵活的Fixture机制可以优雅地处理测试前置如登录获取token、后置清理测试数据以及测试数据的依赖注入二是强大的参数化功能能用极简的代码实现大量相似用例的覆盖三是丰富的插件生态比如我们后面要用到的pytest-html,pytest-xdist分布式执行以及与Allure对接的allure-pytest。相比于Python自带的unittestPytest的约定优于配置、更Pythonic的写法能让测试代码更简洁、更易读。Requests库是Python中处理HTTP请求的“瑞士军刀”其API设计极其人性化。在接口测试中我们核心操作就是构造请求方法、URL、头信息、参数、体和解析响应。Requests用几行代码就能完成这些操作并且对JSON数据的处理非常友好。它是我们与被测系统进行通信的桥梁。Allure是一个开源的测试报告框架它生成的报告堪称“测试界的艺术品”。为什么不用Pytest自带的HTML报告因为Allure报告提供了多维度的分析视角它按特性Feature、故事Story、用例TestCase分层展示能清晰看到每个测试步骤Step的详细请求和响应信息支持附件如图片、日志文件的嵌入还能展示测试环境信息、历史趋势图。一份好的Allure报告不仅是给测试人员看的更是给开发、产品、项目经理看的沟通利器能直观地反映测试覆盖度和质量情况。CI/CD持续集成/持续部署是这个框架从“自动化”走向“智能化”的关键。我们将测试框架集成到CI/CD流水线如GitHub Actions, GitLab CI, Jenkins中实现代码提交后自动触发测试套件执行并将生成的Allure报告自动发布。这建立了快速的反馈闭环确保问题能在合并到主分支前就被发现。它让自动化测试从“可选项”变成了开发流程中的“必选项”。2.2 框架目录结构设计一个清晰的目录结构是项目可维护性的基石。盲目地把所有文件扔在一个文件夹里项目稍微一大就会变成灾难。下面是我推荐并经过实践检验的标准目录结构api_auto_framework/ ├── common/ # 公共模块 │ ├── __init__.py │ ├── logger.py # 日志配置模块 │ ├── config.py # 配置文件读取环境、数据库等 │ └── utils.py # 通用工具函数如加密、日期处理 ├── core/ # 框架核心 │ ├── __init__.py │ ├── request_client.py # 封装的Requests客户端 │ └── assert_utils.py # 封装的断言工具 ├── data/ # 测试数据管理 │ ├── __init__.py │ ├── test_data.yaml # 或 .json, .py 文件 │ └── sql/ # 初始化或清理数据库的SQL脚本 ├── test_cases/ # 测试用例层 │ ├── __init__.py │ ├── conftest.py # Pytest的Fixture集中管理 │ ├── test_login.py # 按业务模块组织用例文件 │ └── test_order.py ├── reports/ # 测试报告目录.gitignore忽略 │ ├── allure-results/ # Allure原始结果文件 │ └── allure-report/ # 生成的HTML报告可动态生成 ├── requirements.txt # 项目依赖清单 ├── pytest.ini # Pytest配置文件 └── README.md # 项目说明文档设计思路解析common/: 存放与具体业务无关的底层工具。比如日志我们希望在每个请求、每个断言失败时都能有记录因此单独封装。core/: 这是框架的“心脏”。request_client.py会对Requests进行二次封装加入日志、重试机制、统一的异常处理等。assert_utils.py则封装业务常用的断言比如判断JSON响应中的某个字段是否符合预期避免在用例中写冗长的assert response[‘code’] 0。data/: 测试数据与脚本分离是基本原则。将测试数据如用户名、密码、商品ID放在YAML或JSON文件中用例文件只关心逻辑。这样数据变更时无需改动代码也便于做数据驱动测试。test_cases/: 用例层。conftest.py是Pytest的本地插件文件里面定义的Fixture可以被该目录及子目录下的所有测试文件使用。我们将常用的Fixture如获取token、初始化数据库连接等都放在这里。reports/: 报告目录。通常会把allure-results和allure-report加入.gitignore因为它们是每次运行生成的不需要纳入版本控制。注意这个结构不是一成不变的。对于更复杂的项目你可能需要在core/下引入Page Object模式的思想为每个主要的API接口创建一个类将请求构造和基础验证封装进去让测试用例更像是在调用一个个业务函数进一步提升可读性和维护性。3. 核心模块实现与封装艺术3.1 打造健壮的HTTP请求客户端直接使用requests.get()或requests.post()在简单场景下没问题但在企业级框架中远远不够。我们需要一个能处理各种边缘情况、便于统一管理的客户端。# core/request_client.py import requests import json import time from common.logger import get_logger logger get_logger(__name__) class RequestClient: def __init__(self, base_urlNone): self.session requests.Session() self.base_url base_url # 可以在这里为session设置默认请求头如User-Agent self.session.headers.update({ User-Agent: ApiAutoTestFramework/1.0, Content-Type: application/json }) def request(self, method, endpoint, **kwargs): 发送HTTP请求的核心方法 :param method: 请求方法GET, POST等 :param endpoint: 接口路径如 /api/login :param kwargs: 传递给requests.request的其他参数如 params, json, data, headers :return: 响应对象 url f{self.base_url}{endpoint} if self.base_url else endpoint # 记录请求日志敏感信息如密码需脱敏这里简化处理 logger.info(f请求开始: {method} {url}) if json in kwargs: logger.debug(f请求体: {json.dumps(kwargs[json], ensure_asciiFalse)}) if params in kwargs: logger.debug(f查询参数: {kwargs[params]}) start_time time.time() try: response self.session.request(methodmethod, urlurl, **kwargs) elapsed time.time() - start_time logger.info(f请求结束: {method} {url} - 状态码: {response.status_code} - 耗时: {elapsed:.2f}s) # 尝试记录响应体注意大响应体的处理 try: resp_content response.json() logger.debug(f响应体(JSON): {json.dumps(resp_content, ensure_asciiFalse)}) except json.JSONDecodeError: logger.debug(f响应体(文本): {response.text[:500]}...) # 只记录前500字符 return response except requests.exceptions.ConnectionError as e: logger.error(f网络连接错误: {e}) raise except requests.exceptions.Timeout as e: logger.error(f请求超时: {e}) raise except Exception as e: logger.error(f未知请求错误: {e}) raise # 提供便捷方法 def get(self, endpoint, **kwargs): return self.request(GET, endpoint, **kwargs) def post(self, endpoint, **kwargs): return self.request(POST, endpoint, **kwargs) def put(self, endpoint, **kwargs): return self.request(PUT, endpoint, **kwargs) def delete(self, endpoint, **kwargs): return self.request(DELETE, endpoint, **kwargs)封装要点与避坑指南使用Session对象requests.Session()可以自动保持cookies对于需要登录态的接口测试至关重要无需手动管理cookie。集中式日志在每个请求前后记录关键信息URL、方法、状态码、耗时这是后期排查问题的第一手资料。务必区分info和debug级别生产运行时可以关闭debug日志提升性能。统一的异常处理将网络异常、超时等通用错误在客户端层面捕获并记录避免每个测试用例都写一遍try-catch。响应处理对响应内容做JSON解析尝试并记录。注意如果响应体非常大如文件下载直接response.json()或response.text可能会占用大量内存需要特殊处理。下一步增强你可以在此基础上轻松添加重试机制针对网络抖动或服务端偶发性5xx错误、请求钩子用于自动添加签名参数、响应结果解析与校验直接返回解析后的业务数据或抛出断言异常。3.2 设计灵活可复用的Pytest FixtureFixture是Pytest的灵魂它用于准备测试环境、提供测试数据。好的Fixture设计能让测试用例变得非常干净。# test_cases/conftest.py import pytest from core.request_client import RequestClient from common.config import get_config pytest.fixture(scopesession) def api_client(): 会话级别的Fixture返回配置好的请求客户端 config get_config() base_url config[test_env][base_url] client RequestClient(base_urlbase_url) yield client # 如果需要可以在这里添加会话结束后的清理工作 client.session.close() logger.info(测试会话结束HTTP客户端已关闭。) pytest.fixture(scopefunction) def auth_token(api_client): 函数级别的Fixture获取登录态token每个测试函数执行一次 config get_config() login_data { username: config[test_account][username], password: config[test_account][password] } response api_client.post(/api/auth/login, jsonlogin_data) assert response.status_code 200 token_data response.json() # 假设返回格式为 {code: 0, data: {token: xxx}} assert token_data[code] 0 token token_data[data][token] # 将token设置到客户端的默认请求头中 api_client.session.headers.update({Authorization: fBearer {token}}) yield token # 测试函数结束后移除授权头避免影响其他不需要登录的测试 api_client.session.headers.pop(Authorization, None) pytest.fixture def unique_order_data(): 生成唯一的测试订单数据避免重复数据冲突 import uuid order_sn fTEST_ORDER_{uuid.uuid4().hex[:8]} return { order_sn: order_sn, product_id: 1001, quantity: 2 }Fixture使用心得作用域scope是关键scopesession的Fixture如api_client在整个测试运行期间只执行一次适合初始化成本高的操作。scopefunction默认每个测试函数都执行适合需要独立环境的测试。scopeclass和scopemodule则对应类和模块级别。yield的妙用yield之前是setup代码yield返回值给测试函数使用yield之后是teardown代码。这是管理资源如数据库连接生命周期的标准模式。Fixture依赖一个Fixture可以依赖另一个Fixture如auth_token依赖api_clientPytest会自动解析和执行依赖关系。conftest.py的位置Fixture可以定义在测试文件内部但更推荐将通用的Fixture放在conftest.py中这样可以被同一目录及子目录的所有测试文件共享。3.3 实现数据驱动与参数化测试测试同一个接口的不同参数组合是接口测试的常态。Pytest的pytest.mark.parametrize装饰器是解决这个问题的最佳工具。# test_cases/test_login.py import pytest import allure allure.feature(用户认证模块) class TestLogin: allure.story(登录功能-正向用例) pytest.mark.parametrize(username, password, expected_code, [ (admin, admin123, 0), (test_user, test123, 0), ]) def test_login_success(self, api_client, username, password, expected_code): 测试使用不同有效账号密码登录成功 with allure.step(f步骤1: 使用账号{username}和密码进行登录): login_data {username: username, password: password} response api_client.post(/api/auth/login, jsonlogin_data) with allure.step(步骤2: 验证响应状态码和业务码): assert response.status_code 200 result response.json() assert result[code] expected_code assert token in result.get(data, {}) allure.attach(bodyresponse.text, name登录成功响应, attachment_typeallure.attachment_type.JSON) allure.story(登录功能-异常用例) pytest.mark.parametrize(username, password, expected_msg, [ (, admin123, 用户名不能为空), (admin, , 密码不能为空), (wrong_user, wrong_pass, 用户名或密码错误), (admin, wrong_pass, 用户名或密码错误), ]) def test_login_failure(self, api_client, username, password, expected_msg): 测试各种错误的登录场景 login_data {username: username, password: password} response api_client.post(/api/auth/login, jsonlogin_data) assert response.status_code 200 # 业务异常通常也返回200但code非0 result response.json() assert result[code] ! 0 # 假设错误信息在 msg 字段 assert expected_msg in result[msg]参数化实战技巧数据与逻辑分离当参数组合非常多时可以把测试数据提取到外部的YAML或JSON文件中然后在Fixture中读取并返回给parametrize。这样用例文件会非常清爽。结合Allure使用allure.step装饰器或上下文管理器可以将测试步骤清晰地展示在Allure报告中。allure.attach可以附加请求/响应的详细信息、截图等让报告内容无比丰富。清晰的用例描述参数化后每个用例组合在报告中会显示为一条独立的用例。为了区分可以在参数化时使用ids参数为每组数据起一个可读的名字例如ids[“空用户名”, “空密码”, “错误凭证”]。4. Allure报告集成与深度定制4.1 环境搭建与基础集成首先你需要安装Allure命令行工具和Pytest插件。# 安装Allure命令行工具以MacOS为例其他系统请参考官网 # 需要先安装Homebrew: /bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) brew install allure # 在Python项目中安装allure-pytest插件 pip install allure-pytest然后在pytest.ini配置文件中指定Allure结果文件的存储路径。# pytest.ini [pytest] addopts -v -s --alluredir./reports/allure-results testpaths test_cases python_files test_*.py python_classes Test* python_functions test_*这样每次执行pytest命令测试结果数据一堆.json文件就会生成到./reports/allure-results目录。最后生成并打开HTML报告# 根据结果文件生成HTML报告 allure generate ./reports/allure-results -o ./reports/allure-report --clean # 打开报告本地查看 allure open ./reports/allure-report4.2 提升报告可读性的高级技巧基础的集成只能生成一个“骨架”报告。要让报告真正有价值必须注入丰富的元数据。添加测试层级的描述使用allure.feature功能模块、allure.story用户故事/测试场景对测试类和方法进行归类。这会在Allure报告的“Behaviors”标签页生成清晰的树状结构便于按功能维度查看测试结果。细化测试步骤在测试方法内部使用with allure.step(“步骤描述”):将复杂的测试逻辑分解。每个步骤在报告中都会独立展示其执行时间和状态成功/失败方便定位问题发生在哪个环节。附加测试证据这是Allure报告最强大的功能之一。你可以附加任何对调试有帮助的信息。# 附加文本/JSON allure.attach(bodyjson.dumps(response.json(), indent2, ensure_asciiFalse), nameAPI响应, attachment_typeallure.attachment_type.JSON) # 附加图片例如UI自动化截图 # allure.attach.file(‘./screenshot/error.png’, name‘失败截图’, attachment_typeallure.attachment_type.PNG)动态生成环境信息在测试开始前创建一个environment.properties文件到Allure结果目录。# 可以在conftest.py的session级fixture中实现 import os pytest.fixture(scopesession, autouseTrue) def record_environment_info(): env_info { 操作系统: os.name, Python版本: os.sys.version, 测试环境: Staging, 项目版本: 1.0.0 } results_dir ./reports/allure-results os.makedirs(results_dir, exist_okTrue) with open(os.path.join(results_dir, environment.properties), w) as f: for key, value in env_info.items(): f.write(f{key}{value}\n) yield这样在Allure报告的“Environment”标签页就能看到本次测试运行的环境信息。踩坑实录Allure报告标题被参数挤换行在使用pytest.mark.parametrize时如果参数值很长生成的Allure报告中的用例标题可能会因为过长而换行影响阅读。解决方法是在parametrize中使用ids参数为每一组参数指定一个简短的、可读的别名。pytest.mark.parametrize(“a, b, expected”, [(1, 2, 3), (10, 20, 30)], ids[“小数字相加”, “大数字相加”]) # 使用ids def test_add(a, b, expected): ...这样报告中显示的用例名就是test_add[小数字相加]和test_add[大数字相加]清晰又美观。5. 集成CI/CD实现自动化流水线框架搭建好了用例也写完了最后一步就是让它“跑起来”。我们选择GitHub Actions作为CI/CD平台因为它与GitHub集成无缝且对开源项目免费。5.1 编写GitHub Actions工作流在项目根目录创建.github/workflows/api-test.yml文件。name: API Automation Test on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: # 每天凌晨2点运行一次可选用于定时巡检 - cron: 0 2 * * * jobs: test: runs-on: ubuntu-latest # 使用最新的Ubuntu系统作为运行环境 steps: # 1. 拉取代码 - name: Checkout code uses: actions/checkoutv3 # 2. 设置Python环境 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: 3.9 # 指定项目所需的Python版本 # 3. 安装依赖 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt # 安装Allure命令行工具 sudo apt-get update sudo apt-get install default-jre -y # Allure需要Java运行环境 wget https://github.com/allure-framework/allure2/releases/download/2.24.0/allure-2.24.0.tgz tar -zxvf allure-2.24.0.tgz sudo mv allure-2.24.0 /opt/allure sudo ln -s /opt/allure/bin/allure /usr/bin/allure # 4. 运行测试这里假设你的测试命令是 pytest - name: Run tests with Pytest run: | pytest --alluredir./reports/allure-results env: # 注入测试环境所需的变量通常从GitHub Secrets读取 TEST_BASE_URL: ${{ secrets.TEST_BASE_URL }} TEST_USERNAME: ${{ secrets.TEST_USERNAME }} TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }} # 5. 生成Allure报告并上传为Artifact - name: Generate Allure Report if: always() # 即使测试失败也生成报告 run: | allure generate ./reports/allure-results -o ./reports/allure-report --clean - name: Upload Allure Report as Artifact uses: actions/upload-artifactv3 with: name: allure-report path: ./reports/allure-report/ retention-days: 7 # 报告保留7天 # 6. 可选部署报告到GitHub Pages或云存储生成一个永久可访问的URL # - name: Deploy to GitHub Pages # uses: peaceiris/actions-gh-pagesv3 # with: # github_token: ${{ secrets.GITHUB_TOKEN }} # publish_dir: ./reports/allure-report5.2 关键配置与安全实践环境变量与Secrets绝对不要将测试环境的URL、账号密码等敏感信息硬编码在代码或配置文件中。GitHub提供了“Secrets”功能可以在仓库的Settings - Secrets and variables - Actions中设置。在Workflow文件中通过${{ secrets.NAME }}引用。在你的common/config.py中应该优先从环境变量读取这些配置。# common/config.py import os def get_config(): return { ‘test_env’: { ‘base_url’: os.getenv(‘TEST_BASE_URL’, ‘http://default-fallback-url’) }, ‘test_account’: { ‘username’: os.getenv(‘TEST_USERNAME’, ‘default_user’), ‘password’: os.getenv(‘TEST_PASSWORD’, ‘default_pass’) } }测试结果与报告留存actions/upload-artifact步骤将生成的Allure报告打包成一个工件你可以在每次Action运行的详情页下载查看。这对于历史记录和问题回溯非常有用。失败通知你可以集成邮件、Slack、钉钉等Webhook在测试失败时发送通知。GitHub Actions市场有丰富的相关Action可供选择。6. 实战中的疑难杂症与排查心法框架跑起来后真正的挑战才开始。下面是我在多年实践中总结的几个高频问题和解决思路。6.1 依赖安装与环境问题问题ModuleNotFoundError: No module named ‘requests’原因项目依赖没有正确安装或者是在一个虚拟环境外运行了测试。解决始终使用虚拟环境venv, conda。在项目根目录创建python -m venv venv激活后安装依赖。使用requirements.txt精确管理依赖。生成它pip freeze requirements.txt。安装它pip install -r requirements.txt。在CI/CD脚本中务必在执行测试前运行pip install -r requirements.txt。问题Allure命令行工具安装后allure --version识别不了原因通常是系统PATH环境变量没有包含Allure的安装路径。解决Linux/Mac如果通过下载压缩包安装需要将解压后的bin目录添加到PATH。例如在~/.bashrc或~/.zshrc中添加export PATH$PATH:/path/to/allure/bin然后执行source ~/.zshrc。Windows将Allure的安装目录如C:\allure\bin添加到系统的环境变量PATH中。CI/CD中如上面GitHub Actions示例所示通过sudo ln -s创建软链接到/usr/bin是更可靠的方式。6.2 测试执行与网络问题问题接口返回429 Too Many Requests或{error:too many requests, please try again later}原因测试脚本发送请求的频率过高触发了服务端的限流策略。解决添加延迟在连续的请求之间使用time.sleep()加入短暂的间隔如0.5-1秒。但这会拖慢测试速度。使用重试机制更优雅在封装的RequestClient中集成重试逻辑。可以使用tenacity库。from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception def is_rate_limit_error(exception): return isinstance(exception, requests.exceptions.HTTPError) and exception.response.status_code 429 class RequestClient: retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10), retryretry_if_exception(is_rate_limit_error)) def request(self, method, endpoint, **kwargs): # ... 原有请求逻辑 ... response.raise_for_status() # 触发HTTPError异常供tenacity判断 return response这样当遇到429错误时框架会自动等待一段时间后重试最多重试3次。与开发沟通在测试环境是否可以适当放宽限流策略或者提供专用的测试账号/令牌。问题测试用例之间存在脏数据干扰原因测试A创建的数据影响了测试B的预期结果。这在集成测试中非常常见。解决保证用例独立性这是黄金法则。每个用例在执行前应该将环境恢复到已知的干净状态。可以通过Fixture的setup和teardown来实现。使用测试数据工厂创建数据时使用随机或唯一标识符如UUID、时间戳。例如上面的unique_order_dataFixture。清理测试数据在用例或Fixture的teardown阶段清理本用例创建的数据。这可能需要调用专门的清理接口或直接操作测试数据库。使用事务回滚如果测试直接连数据库可以考虑在测试开始时开启一个数据库事务测试结束后回滚这样所有数据库更改都不会持久化。但这对接口测试来说通常不直接。6.3 报告与集成问题问题Allure报告中的用例标题被参数化数据挤得换行不美观原因与解决如前所述使用pytest.mark.parametrize的ids参数为每组测试数据提供一个简短的别名。问题CI/CD流水线中测试通过但本地运行失败或反之原因环境不一致是最可能的原因。包括Python版本、第三方库版本、系统环境变量、依赖服务如Redis, MySQL的配置等。排查锁定依赖版本使用pip freeze requirements.txt确保本地和CI环境安装完全相同的包版本。使用容器化使用Docker将测试环境包括Python、依赖、甚至必要的服务打包成镜像。在CI中直接使用该镜像运行测试可以最大程度保证环境一致性。这是目前最专业的做法。对比环境变量检查CI脚本中注入的环境变量是否与本地.env文件或系统环境变量一致。查看详细日志确保你的测试框架日志在CI中也能输出并仔细对比本地和CI的运行日志差异。搭建一个完整的接口自动化测试框架就像搭积木选对组件技术栈设计好蓝图目录结构然后一块块稳健地垒起来模块实现最后通上电让它自动运转CI/CD。这个过程肯定会遇到各种小麻烦比如环境配置、网络波动、数据污染但每解决一个你对整个体系的理解就更深一层。我个人最大的体会是框架的“好用”比“高大上”更重要。初期不必追求大而全先从核心业务流程的1-2个接口测起把请求封装、用例编写、报告生成这个闭环跑通再逐步扩展模块、增加特性、优化CI流程。最重要的是让这个框架能真正为团队提效让自动化测试成为开发流程中自然、可靠的一环而不是一个负担。