ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Trae辅助搭建pytest与Appium自动化回归测试全流程

Trae辅助搭建pytest与Appium自动化回归测试全流程 上周接到一个需求新版 App 要上三个新增功能领导给了两周时间要求自动化回归必须跟上不能只靠手工点点点。我当时的方案是接口层用 pytest 和 requests 做覆盖UI 层用 Appium 跑回归再挂一个定时任务每天自动执行。实际动手的时候大部分代码是让 Trae 帮我写和改的。Trae 是一个 AI 编程 IDE更准确地说它可以当作一个能理解整个项目上下文的辅助引擎来用。在自动化测试这种样板代码多、结构重复的场景里它的效率优势比写业务代码更明显。这篇文章就把整个过程拆开讲从 pytest 接口框架搭建到 Appium 回归再到定时任务和报告通知全部是可落地、可以直接复现的内容希望能给你省掉不少试错成本。1. Trae 在自动化测试里的定位与关键功能1.1 自动化测试的痛点不是代码难写而是变更太频繁很多人以为自动化测试的难点是 Python 语法、pytest 用法或者 Appium 的 API我实际干下来的感受完全不是这样。真正的瓶颈是“维护”接口字段一变断言代码跟着改页面改版所有页面对象里的定位符要过一遍环境地址一换公共配置到处找跑完还要整理报告、盯失败原因。技术上每件事都不难但串在一起非常消耗时间。这正好是 AI 辅助发挥优势的地方。Trae 和传统编辑器最大的区别是它能感知上下文我打开整个测试项目在对话框里直接说“把 requests 请求统一封装成 HttpClientheader 从全局配置文件读取”它能基于当前目录结构去生成而不是给你一段孤立的示例代码。更关键的是跨文件感知——新增功能通常不是只改一个文件入口、用例、数据、断言四处都要动Trae 可以把这四处关联起来一起改。换句话说它适合的不是“写一段教程代码”而是“对现有项目做工程化改造”。1.2 这次实测下来最值得用的三个 Trae 能力我这次用得最多、效果也最明显的功能有三个。第一个是会话式代码生成与重构。以前写一个接口的完整用例从请求封装到数据驱动到断言至少要写四五个文件。现在把接口文档粘贴给 Trae然后要求“按项目现有风格生成 pytest 用例使用 yaml 数据驱动”它生成的结构基本能直接跑。我在实际过程中发现只要先给它看一两个现有文件的写法它生成的风格和团队规范高度一致。第二个是跨文件搜索与批量修改。Trae 可以基于自然语言定位代码位置例如我输入“找出所有直接调用 requests.post 的测试文件改成走 HttpClient”它会把涉及的文件列出来逐文件改完。这个能力在做新增功能回归时特别有用因为新功能往往会引入新的公共逻辑需要批量迁移旧代码。第三个是 Trae CLI。命令行模式下可以执行 AI 任务我主要用来做两件事一是提交代码前自动跑一遍关键字简单检查二是把 pytest 失败的堆栈喂给 CLI让 AI 先给出一个“失败原因初步判断”。引入到日常工作流后每天早上看报告的成本低了一个量级。话说回来Trae 也不是万能的。它生成代码的能力取决于你给的信息量。接口文档、现有代码风格、依赖库版本这三样越明确产出越靠谱。后面每章我都会讲到怎么通过 Prompt 把信息喂得更全。2. 用 Trae 从 0 到 1 搭建 pytest 接口自动化框架2.1 环境准备版本锁定比“最新版”更重要如果你维护的是自己一个人用的脚本直接pip install pytest requests没问题。但如果是团队项目我强烈建议把版本锁死在 requirements.txt 里。原因很简单Trae 在生成代码时会假设某些方法存在不同版本的 requests 和 pytest 差异虽然不大但allure-pytest恰好遇到过兼容性问题版本不锁今天能跑明天同事一把依赖升级全部用例报错查起来非常浪费时间。我这次用的依赖版本组合如下写在 requirements.txt 里pytest7.4.0 requests2.31.0 PyYAML6.0 allure-pytest2.13.2 pytest-xdist3.3.1 appium-python-client2.11.0初始化环境python -m venv .venv source .venv/bin/activate pip install -r requirements.txt这里有一个实操细节新增功能往往还要操作数据库做数据准备建议顺手装一个pymysql但不要一开始就装一堆用不到的库。我踩过坑Trae 看你环境里什么库都有容易生成一些很炫但不必要的代码比如用 SQLAlchemy 连数据库其实你只需要一个fetchone()。2.2 公共请求模块让 Trae 生成可复用的 HTTP 客户端接口测试框架里最核心的模块就是 HTTP 客户端。它的作用是统一处理 base_url、token、超时、日志和请求头避免每个用例里裸调requests.get/post。我给 Trae 的 Prompt 大概是这样的“在 common/http_client.py 里生成一个 HttpClient 类使用 requests.Session支持传入 base_url 和 token提供 request 方法日志要打出请求方法、URL、状态码和耗时。”生成后我稍微调整最终代码如下# common/http_client.py import logging import time import uuid import requests class HttpClient: def __init__(self, base_url: str, token: str ): self.session requests.Session() self.base_url base_url if token: self.session.headers.update({Authorization: fBearer {token}}) self.session.headers.update({Content-Type: application/json}) def request(self, method: str, path: str, **kwargs): url f{self.base_url}{path} kwargs.setdefault(timeout, 15) request_id uuid.uuid4().hex[:8] logging.info([%s] %s %s begin, request_id, method.upper(), url) start time.time() response self.session.request(method, url, **kwargs) cost round((time.time() - start) * 1000, 2) logging.info([%s] status%s cost%sms, request_id, response.status_code, cost) return response为什么用requests.Session而不是直接requests.get因为 Session 会自动管理连接池和 cookie接口自动化里登录态依赖 cookie 的场景特别多比如你先调登录接口后续接口都带着 session这比每个请求手动传 cookie 干净得多。2.3 数据驱动用例新增功能场景参数化新增功能的需求往往是同一接口要覆盖多组参数组合比如“订单改签”这个场景可能需要测试不同的改签时间、改签原因、订单状态。如果每个组合写一个函数代码会膨胀到没法维护。正确做法是用 yaml 保存测试数据用parametrize实现数据驱动。先建配置文件# config/config.yaml base_url: https://api.example.com token: your_token_here新建一个通用读取工具# common/yaml_util.py import yaml from pathlib import Path def read_yaml(path: str): abs_path Path(__file__).parent.parent / path with open(abs_path, r, encodingutf-8) as f: return yaml.safe_load(f)然后是测试数据文件# data/order_change.yaml - name: 改签到当天成功 order_id: A1001 change_time: 2025-06-01 14:00 reason: 个人行程变更 - name: 改签到过去时间应该失败 order_id: A1002 change_time: 2025-01-01 08:00 reason: 测试用例# test_cases/test_order_change.py import pytest from common.http_client import HttpClient from common.yaml_util import read_yaml client HttpClient(https://api.example.com, your_token_here) pytest.mark.parametrize(case, read_yaml(data/order_change.yaml)) def test_order_change(case): payload { order_id: case[order_id], change_time: case[change_time], reason: case[reason], } resp client.request(POST, /api/order/change, jsonpayload) assert resp.status_code 200 data resp.json() assert data[code] 0 if case[reason] : assert data[data][status] REJECTED else: assert data[data][status] CHANGED这里我特别想让 Trae 帮忙的是“生成用例的边界条件”。你直接问它“这个接口还有什么边界情况”它会根据接口文档给出空值、超长字符串、非法枚举等补充建议比自己坐那儿想全快很多。不过要留个心眼AI 给的边界条件不一定都对应当前版本最后还是得对一下产品需求和接口文档。2.4 断言与报告用例写得再好报告难看也白搭断言是自动化测试的灵魂。很多人只写一句assert resp.status_code 200这种用例基本是自欺欺人。接口返回 200 只代表请求通了不代表业务成功。我让 Trae 生成用例的时候会明确要求“断言必须包含业务字段校验”比如 code、status、关键业务字段的值。报告方面我习惯用 Allure因为它的展示效果对非技术人员友好领导能一眼看懂有多少用例通过、失败在哪个模块。项目根目录建一个 pytest.ini[pytest] testpaths test_cases addopts -s -q --alluredirallure-results --clean-alluredir运行一次pytest allure generate allure-results -o allure-report --clean allure open allure-report生成报告这个过程也可以让 Trae 写成一个脚本后面定时任务直接调用不用每次手敲命令。这一步看起来不起眼但它决定了自动化测试能不能长期坚持下来——报告越容易看团队越愿意用用例越会被维护。3. Trae 辅助 Appium 完成移动端新增功能回归3.1 Appium 环境准备与 capability 配置移动端回归比接口层麻烦因为涉及真机或模拟器、Appium Server、UiAutomator2 三套环境。我这次用 Appium 2.0 配合 UiAutomator2capability 配置如下# app/config.py desired_caps { platformName: Android, appium:automationName: UiAutomator2, appium:deviceName: emulator-5554, appium:appPackage: com.example.app, appium:appActivity: .MainActivity, appium:noReset: True, appium:autoGrantPermissions: True, }有几个容易忽略的点。第一noReset一定设成 True否则每次跑用例都清 App 数据登录态全丢。第二autoGrantPermissions建议打开否则弹窗权限会把用例打断。第三模拟器要提前起来并且确保adb devices能看到设备再启动 Appium。这些配置我自己写容易忘所以直接让 Trae 生成一个driver_fixture.py要求它把启动、等待、退出都封装进 fixture。生成的代码大概如此# app/driver_fixture.py import pytest from appium import webdriver from app.config import desired_caps pytest.fixture(scopefunction) def driver(): driver webdriver.Remote(http://127.0.0.1:4723/wd/hub, desired_caps) driver.implicitly_wait(5) yield driver driver.quit()这里我特意把scope设置为function因为 UI 用例之间的状态经常互相干扰一个用例结束就重启会话虽然慢一点但可靠性高。后续如果追求执行速度再按模块去调整 fixture 作用域。3.2 BasePage 封装与 Page Object 改造Appium 用例最怕的就是定位符写成一坨直接铺在用例里。页面一改版几十个用例全部要动。所以 Page Object 模式是移动端自动化标配。我先让 Trae 生成一个基础 BasePage把通用操作抽出来# app/page/base_page.py from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 15) def find_element(self, locator): return self.wait.until(EC.presence_of_element_located(locator)) def click(self, locator): self.find_element(locator).click() def send_keys(self, locator, text): element self.find_element(locator) element.clear() element.send_keys(text) def swipe_up(self, times1): size self.driver.get_window_size() x size[width] // 2 start_y int(size[height] * 0.8) end_y int(size[height] * 0.3) for _ in range(times): self.driver.swipe(x, start_y, x, end_y, 800)封装完 BasePage 之后我让 Trae 按同样的风格为新增功能页面生成 Page Object。比如“订单详情页”新增了“预计送达时间”展示我就把大概的页面布局描述给它它生成了类似这样的类# app/page/order_detail_page.py from appium.webdriver.common.appiumby import AppiumBy from app.page.base_page import BasePage class OrderDetailPage(BasePage): arrive_time_locator (AppiumBy.ID, com.example.app:id/tv_arrive_time) def get_arrive_time(self): return self.find_element(self.arrive_time_locator).text生成之后我做的第一件事是检查定位符是不是真的对应开发给到的 resource-id。AI 不会骗你但它只能根据你的描述生成一个“大概率的正确值”。3.3 一条完整的下单回归用例是怎么组合出来的新增功能回归不是只测新增的那几个点而是要确认新增功能没有破坏老流程。所以我说一下完整用例的组织方式。一个端到端用例通常包含登录、跳转、操作、断言四步。借用 Page Object 后用例层可以写得非常干净# test_cases/test_buy_flow.py import pytest from app.page.home_page import HomePage from app.page.order_detail_page import OrderDetailPage from app.page.order_page import OrderPage pytest.mark.usefixtures(driver) class TestBuyFlow: def test_create_order(self, driver): home HomePage(driver) home.click_category(数码) home.click_first_product() detail OrderDetailPage(driver) detail.click_buy_now() order OrderPage(driver) order.confirm_order() assert order.is_order_success() # step5: 校验新增的预计送达时间 assert detail.get_arrive_time() ! , 预计送达时间不应为空这种结构的好处是用例本身只描述业务行为真正的定位符和操作细节全部在 Page Object 层。新增功能上线后主要改动落到 page 层和少量新用例上而不是翻遍所有历史用例。有一点要提醒UI 自动化用例的断言要比接口层更克制。不要断言太多细节否则每条用例都异常脆弱。UI 层只要能证明“核心流程没坏、关键信息有展示”就够了把精确的字段校验留给接口层。这是我自己吃过大亏之后才想明白的分工。4. 让用例每天自动跑serverless 定时任务与报告通知4.1 为什么需要定时任务接口和 UI 用例都写完了如果每次都要人手动执行自动化测试的价值就砍掉一半。我这边的要求是每天凌晨自动跑全量回归早上来公司直接看报告。实现方式可以选 Linux 服务器上的 crontab也可以选云厂商的 serverless 定时触发器。两者原理差不多都是按 cron 表达式在指定时间点执行一个命令区别只是有没有台服务器。如果你手头正好有测试服务器用 crontab 最直接。如果是团队没有常驻服务器Serverless 定时任务更轻量按次计费还不用维护机器。不管哪种都需要一个“一键执行”的入口脚本。4.2 一键运行脚本与定时触发器配置我写了一个run_daily.py它的职责是跑 pytest、生成报告、根据结果发通知# run_daily.py import subprocess import sys from common.notify import send_wecom_webhook if __name__ __main__: code pytest_main() # 生成 allure 报告 subprocess.run([allure, generate, allure-results, -o, allure-report, --clean]) if code 0: send_wecom_webhook(https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxxx, 自动化测试全部通过) else: send_wecom_webhook(https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxxx, 自动化测试有失败请查看报告) sys.exit(code) def pytest_main(): import pytest return pytest.main([-s, -q, test_cases, --alluredirallure-results, --clean-alluredir])如果使用 crontab配置如下0 1 * * * cd /opt/autotest python run_daily.py logs/daily.log 21这个 cron 表达式的意思是每天凌晨 1 点执行一次。关键点是一定要把日志重定向到文件里。实际出现过定时任务跑挂了但因为在终端里直接跑正常所以一直没发现直到加了日志才知道是环境变量缺失导致命令找不到。日志是排查定时任务问题的最好朋友。如果走 Serverless原理是一样的在控制台创建函数运行环境选 Python入口设置为run_daily.index然后配置定时触发器cron 表达式写0 1 * * * *。函数执行完会返回日志比服务器上查日志更方便。4.3 测试报告输出与机器人通知报告生成之后不能让工程师自己登录服务器看。最简单的做法是接一个企业微信群机器人通过 webhook 把结果文本推送到群里。核心代码# common/notify.py import requests def send_wecom_webhook(webhook_url: str, content: str): body { msgtype: text, text: {content: content} } requests.post(webhook_url, jsonbody, timeout10)通知文本不要只写“成功/失败”我会让 Trae 生成一段小工具从 allure-results 目录里统计失败用例名然后拼进去。这样群里看到消息就知道是哪个模块挂了不用再打开报告翻半天。通知这个环节很容易被忽略但它决定了自动化测试能不能“被”用起来。5. 常见问题与排查技巧实录5.1 我踩过的几个坑第一Trae 生成了代码里不存在的 API。比如它可能在 pytest 里用了一个自定义 fixture但我没定义同名 fixture跑用例直接报错。现在我每次让 Trae 生成代码都会加一句“只使用项目已有依赖库和已有文件不得假设额外模块存在”这句话能显著减少返工。第二生成用例时fixture 作用域没搞清导致数据污染。Trae 默认生成的 fixture 往往是scopefunction但接口自动化里登录态可能希望是session级别。如果混用很容易出现“用例单独跑通过全量跑失败”的诡异问题。建议生成后立刻检查 fixture 的作用域。第三Appium 定位等待方式不对。presence_of_element_located和visibility_of_element_located是两回事某些情况下元素在 DOM 里存在但不可见用 visibility 才会等到正确状态。AI 默认生成的代码不一定符合实际场景这条尤其要人工校验。第四定时任务运行环境与本地不一致。本地跑 pytest 能找到命令云函数环境里却找不到 spawn 的 allure因为环境变量不同。我的解决方法是脚本里用绝对路径或者把 allure 调用封装成在函数内通过shutil.which探测。5.2 问题排查速查表我把这段时间遇到的典型问题整理成一个表方便你直接对照现象可能原因排查看法pytest 一个用例都没执行testpaths 配错或文件名不是 test_ 开头先pytest --collect-only看收集结果用例单独跑通过全量跑失败fixture 作用域冲突或用例间共享数据被改写检查是否有 module 级 fixture 修改了全局状态Appium 定位不到元素resource-id 对不上或元素在另一个 WebView用 Appium Inspector 看当前页面 UI 层级定时任务没跑cron 时间格式错误、脚本无执行权限、环境变量缺失先看脚本日志再手动执行入口脚本对比Trae 生成的断言太弱没有明确要求业务断言重新生成时要求“必须校验 code 和状态字段”Allure 报告为空allure-results 路径不一致确认 pytest.ini 的 addopts 与实际报告路径一致5.3 给新人的一个团队协作建议如果你不是一个人维护这套东西建议在项目里沉淀一个docs/prompt-template.md把常用的 Trae 提示词规范写下来。比如“生成用例时必须使用 data 目录下的 yaml 数据驱动”“发送请求必须走 HttpClient”“不得假设额外依赖库”。这样不管是老同事还是新同学生成的代码风格都是一致的Long-term维护成本会低很多。另一个小技巧是让 Trae 顺手生成“用例和需求单号”的映射注释。新增功能往往对应产品需求单把单号写进用例注释里将来需求变更、字段调整时你能快速找到是哪批用例要改。这个细节帮我省过好几次返工。最后说点个人体会。Trae 这类工具真正改变的是我做自动化测试的手感以前碰到大改版至少两三天耗在改定位符和公共方法上现在可以把相当一部分机械工作丢给 AI我集中精力看用例逻辑和边界条件。但记得一条AI 写出来的测试代码一定要亲自走一遍数据流尤其要检查断言是不是真的能抓到问题。如果断言写得浅AI 生成一百个用例也救不了后台那几行 bug。希望这篇攻略能让你下次接新增功能时少熬几个夜。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表