news 2026/6/8 7:16:29

大学生备赛用携程机票Web自动化测试脚本(Python+Selenium,含截图验证与即跑环境)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大学生备赛用携程机票Web自动化测试脚本(Python+Selenium,含截图验证与即跑环境)

本文还有配套的精品资源,点击获取

简介:专为2025软件测试大赛Web赛道预选赛设计的实战型自动化测试资源,基于Python 3.8和Selenium 4.x实现携程机票官网核心流程覆盖:首页搜索框输入、出发/到达城市选择、日期控件操作、航班列表筛选及结果页断言验证。脚本采用显式等待策略应对动态加载,每步操作后自动触发截图并保存至screenshots目录,文件名含时间戳与用例编号(如16522329344886_CtripFlight_R002_001.png),便于快速定位异常环节。主入口TestCtripFlight.py已适配pytest 8.3.5,无需修改即可执行;配套requirements.txt明确依赖版本,.gitignore和README.md提供基础维护指引;目录中已预置.pyc字节码、.pytest_cache缓存及__pycache__结构,减少初学者环境配置负担。适用于高校教学演示、学生自主演练或UI测试入门实践,重点训练元素定位策略、交互动作链编写、页面状态断言及失败现场留存能力。

1. 项目概述:这不是一个“跑通就行”的Demo,而是一套能上赛场的备赛训练脚本

你是不是也经历过这样的场景:在软件测试大赛校内选拔赛前一周,临时抱佛脚写了个Selenium脚本,结果一跑就报NoSuchElementException,调试半小时才发现是携程首页加了防爬层,XPath里少了个//div[@class="flight-search-box"]的父容器定位;或者日期控件用send_keys输不进去,反复查文档才想起得用JavaScript执行器点击日历图标;更别提比赛现场评委要求“请展示失败时的截图证据”,你手忙脚乱翻日志、开浏览器、手动截屏——时间早就超了。

这套“大学生备赛用携程机票Web自动化测试脚本”,就是为解决这些真实、高频、带压迫感的实战问题而生的。它不是教科书里那个“打开百度搜‘selenium’然后点搜索”的玩具示例,而是完整复刻2025年软件测试大赛Web赛道预选赛典型题型:在未提供测试账号、无后端接口权限、仅能操作前端页面的前提下,对携程机票官网(ctrip.com/flights)完成端到端业务流验证。关键词“携程机票测试”“Selenium脚本”“Web自动化”“pytest测试”“UI测试脚本”不是标签堆砌,而是每一行代码都在回应它们——比如TestCtripFlight.py里第87行对航班价格元素的断言,用的是WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//span[contains(@class,'price') and contains(text(),'¥')]"))),而不是简单粗暴的driver.find_element(By.XPATH, "...").text,因为后者在动态渲染下极可能触发StaleElementReferenceException;再比如所有截图文件名16522329344886_CtripFlight_R002_001.png中的R002代表“用例编号R002”,对应《赛题说明V2.1》中第二道题“验证往返航班价格排序逻辑”,这种命名不是为了好看,而是让评委在3秒内就能把截图和题干对上号。

它面向的不是已经会写Page Object Model的老手,而是刚学完《软件测试基础》、第一次听说WebDriverWait、对着Chrome DevTools里一堆嵌套<div>发懵的大三学生。所以目录里预置了.pytest_cache__pycache__,不是为了炫技,是因为我亲眼见过三个不同学院的学生,在同一台实验室电脑上,因Python字节码缓存冲突导致ImportError: cannot import name 'xxx'卡住整整两小时;所以requirements.txt里明确锁死selenium==4.15.0pytest==8.3.5,因为新版本Selenium 4.18对ActionChainsmove_to_element行为做了微调,而去年省赛某高校用的就是4.15——我们不赌版本兼容性,我们直接给你考场同款环境。这不是一套“能跑就行”的脚本,而是一份带着赛场硝烟味的生存指南。

2. 整体设计与思路拆解:为什么这样写?每一步都是踩坑后的选择

2.1 架构选型:拒绝过度设计,聚焦“可解释性”与“可追溯性”

很多教学资料一上来就推Page Object Model(POM),理由很充分:“高内聚低耦合”“便于维护”。但现实是:大学生备赛周期通常只有2~3周,真正能投入编码的时间可能不到40小时。让他们花8小时搭POM框架,再花12小时写三个页面类,最后发现FlightSearchPage里的select_departure_city()方法在携程新版中失效了,而修复要改5个地方——这不叫工程化,这叫制造焦虑。

所以本脚本采用扁平化单文件结构,所有逻辑集中在TestCtripFlight.py中,按业务流分块注释:

# === R001 用例:单程搜索流程验证 === # 步骤1:访问首页并等待搜索框加载 # 步骤2:输入出发城市(显式等待+防输入框失焦) # 步骤3:输入到达城市(处理下拉列表异步加载) # ...

好处是什么?第一,可解释性强——当学生被问到“这个XPath为什么这么写”,他能立刻指着第42行说:“因为携程把城市输入框包在id=’searchBox’的div里,而下拉列表是动态生成的,所以必须等class=’city-suggest-list’出现”;第二,可追溯性高——截图文件名16522329344886_CtripFlight_R002_001.png里的R002直接对应代码中# === R002 用例:往返航班日期联动验证 ===区块,评委看截图就知道你在验证哪条赛题要求;第三,学习成本低——删掉所有import语句,从def test_flight_search_single()开始读,5分钟就能理解整个流程。

当然,这不是反对POM。我在结尾会分享一个“赛后升级建议”:当你的脚本稳定运行超过5轮模拟赛,就可以把select_date()抽成独立函数,再封装进FlightDateSelector类——但那是进阶动作,不是入门门槛。

2.2 等待策略:显式等待不是“加个wait”,而是理解携程的加载节奏

携程机票首页的加载不是“全页刷新→元素出现”这么简单。它有三层异步节奏:

  • 第一层:骨架加载(约0.8秒)
    <div id="searchBox">容器出现,但内部输入框还是disabled状态;
  • 第二层:组件激活(约1.2秒)
    出发/到达城市输入框变为可编辑,但下拉列表数据尚未请求;
  • 第三层:数据渲染(约0.5秒)
    用户输入“北京”后,<ul class="city-suggest-list">动态插入12个匹配项。

如果统一用time.sleep(3),看似稳妥,实则埋雷:比赛环境网络波动,3秒可能不够;而本地固态硬盘+千兆宽带,3秒又纯属浪费,拖慢整体执行速度(一套完整用例含6个Rxx用例,每个sleep 3秒就是18秒无效等待)。

所以脚本采用分层显式等待

# 等待骨架容器(最外层) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "searchBox")) ) # 等待输入框可交互(中间层) WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//input[@placeholder='出发城市']")) ) # 等待下拉列表数据渲染(最内层) WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CLASS_NAME, "city-suggest-list")) )

关键细节在于EC.element_to_be_clickableEC.visibility_of_element_located的区别:前者确保元素不仅存在、可见,而且enabled=True且不在display:none中;后者只保证元素在DOM中且visibility:visible。对携程的城市输入框,必须用前者,否则click()会抛ElementClickInterceptedException——因为初始状态下,输入框虽可见,但被一个半透明遮罩层覆盖。

提示:不要迷信“最长等待10秒”。我在实验室实测过,携程首页在校园网环境下,95%的请求在2.3秒内完成骨架加载,所以WebDriverWait(driver, 10)是留足安全余量,不是默认等待10秒。

2.3 截图机制:不是“出错才截”,而是“每步必截”的证据链思维

很多初学者以为截图只为debug,这是巨大误区。在测试大赛中,截图是核心交付物——评委不看你console输出的AssertionError,他们要看R003_002.png里航班列表是否真的按价格升序排列,要看R001_003.png里日期控件是否正确高亮了所选日期。

因此脚本实现强制性步骤截图,而非异常截图:

def take_step_screenshot(driver, step_name): timestamp = int(time.time() * 10000) # 精确到万分之一秒 filename = f"{timestamp}_CtripFlight_{step_name}.png" filepath = os.path.join("screenshots", filename) driver.save_screenshot(filepath) print(f"[截图] 已保存 {filepath}") # 在每个关键操作后调用 take_step_screenshot(driver, "R001_001") # 首页加载完成 driver.find_element(By.XPATH, "//input[@placeholder='出发城市']").send_keys("上海") take_step_screenshot(driver, "R001_002") # 出发城市输入完成

文件名规则{timestamp}_{project}_{step}有三重意义:
-timestamp:避免并发执行时文件名冲突(虽然单机跑,但为扩展性预留);
-CtripFlight:项目标识,方便赛后整理归档;
-R001_002:直指赛题编号,R001是用例集,“002”是该用例内第二步,比“search_page_input_departure”这类描述性命名更契合竞赛场景。

注意:截图前务必调用driver.execute_script("window.scrollTo(0, 0);")。携程首页有吸顶导航栏,若页面滚动到底部再截图,顶部关键元素会被截掉——这是我带队参加华东区决赛时,因一张截图缺了搜索按钮,被扣0.5分的真实教训。

3. 核心细节解析与实操要点:那些文档里不会写的“手感”

3.1 元素定位:为什么XPath比CSS选择器更适合携程?

携程的HTML结构充满“防御性设计”:
- 大量使用动态class名,如class="flight-search-input__input--1a2b3c",每次发布都变;
- 关键按钮没有id,只有<button type="button"># 稳定方案:用文本内容+层级关系 search_btn = driver.find_element(By.XPATH, "//button[.//span[text()='搜索']] | //button[contains(@class,'search-btn')]" ) # 对比脆弱方案:依赖动态class # driver.find_element(By.CSS_SELECTOR, "button.search-btn-1a2b3c") # 下次更新就挂

更关键的是,XPath支持following-sibling轴,这对处理携程的“城市输入框+下拉列表”联动至关重要。例如,当输入“北京”后,要点击下拉列表中第一个匹配项,但列表项没有唯一标识,只能靠位置:

# 定位出发城市输入框 dep_input = driver.find_element(By.XPATH, "//input[@placeholder='出发城市']") dep_input.send_keys("北京") # 等待下拉列表出现,并点击第一个选项(利用兄弟节点关系) first_suggestion = driver.find_element(By.XPATH, "//input[@placeholder='出发城市']/following-sibling::div//li[1]" ) first_suggestion.click()

这里/following-sibling::div精准指向输入框同级的下拉容器,比//div[@class='city-suggest-list']/li[1]更可靠,因为后者可能匹配到到达城市的下拉列表。

3.2 日期控件:放弃send_keys,拥抱JavaScript执行器

携程的日期选择器是典型的“伪输入框”:
-<input>标签本身是只读的(readonly="readonly");
- 真正的交互发生在背后的日历弹窗,由<div class="calendar-popup">承载;
- 直接send_keys("2025-05-20")会触发InvalidElementStateException

正确解法是三步走

  1. 点击日历图标(触发弹窗)
    python calendar_icon = driver.find_element(By.XPATH, "//input[@placeholder='出发日期']/following-sibling::span[@class='calendar-icon']" ) calendar_icon.click()

  2. 等待日历网格加载(注意:不是等弹窗div,而是等里面的日期格子)
    python WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "//div[@class='calendar-grid']//button[@data-date]")) )

  3. 用JavaScript点击目标日期(绕过UI限制)
    python target_date = "2025-05-20" driver.execute_script(""" var dateBtn = document.querySelector("button[data-date='{date}']"); if (dateBtn) dateBtn.click(); """.format(date=target_date))

为什么不用ActionChains?因为move_to_element().click()在日历弹窗这种绝对定位、z-index高的浮层上,极易被遮挡或坐标偏移。JavaScript执行器直接操作DOM,稳定度提升300%——这是我用Selenium Grid跑100次对比测试得出的数据。

3.3 断言设计:从“文本包含”到“业务逻辑验证”

初学者常写:

assert "¥580" in price_element.text # ❌ 脆弱!价格可能显示为"¥580.00"或"¥ 580"

专业做法是剥离格式,验证数值逻辑

# 提取纯数字价格 price_text = price_element.text.strip() price_num = float(re.sub(r'[^\d.]', '', price_text)) # 移除¥、空格、逗号 # 验证业务规则:单程价格应在400~1200区间(基于历史数据) assert 400 <= price_num <= 1200, f"价格异常:{price_num}" # 进阶:验证排序逻辑(R003用例) prices = [] for elem in driver.find_elements(By.XPATH, "//span[contains(@class,'price')]"): p = float(re.sub(r'[^\d.]', '', elem.text)) prices.append(p) assert prices == sorted(prices), "航班价格未按升序排列"

这里的关键洞察是:测试大赛的评分标准从来不是“能不能拿到值”,而是“能不能验证业务规则”。re.sub(r'[^\d.]', '', ...)这行正则,比任何XPath教程都更能教会学生什么叫“数据清洗”。

4. 实操过程与核心环节实现:从零开始跑通的完整路径

4.1 环境准备:三步到位,拒绝“配置地狱”

很多学生卡在第一步:装完Python,pip install selenium,运行就报WebDriverException: Message: 'chromedriver' executable needs to be in PATH。本脚本彻底规避此问题,采用chromedriver-autoinstaller方案:

# 1. 创建虚拟环境(推荐,避免污染系统Python) python -m venv ctrip_env ctrip_env\Scripts\activate # Windows # 或 source ctrip_env/bin/activate # macOS/Linux # 2. 安装依赖(requirements.txt已锁定版本) pip install -r requirements.txt # 3. 运行主测试(自动下载匹配Chrome版本的chromedriver) python TestCtripFlight.py

requirements.txt内容精简但致命:

selenium==4.15.0 pytest==8.3.5 chromedriver-autoinstaller==0.6.4 Pillow==10.2.0 # 用于后续图像比对扩展

chromedriver-autoinstaller的妙处在于:它会在首次运行时检测你本地Chrome浏览器版本(如124.0.6367.78),自动下载对应chromedriver(124.0.6367.78),并注入Selenium的PATH。这意味着——
- 不用去chromedriver官网找版本;
- 不用解压到特定目录;
- 不用配置环境变量;
- 甚至换台电脑,只要Chrome版本一致,脚本照跑不误。

实操心得:如果你的Chrome是企业版或通过MS Store安装,autoinstaller可能检测失败。此时打开Chrome,地址栏输入chrome://version/,复制“版本”号(如124.0.6367.78),手动下载对应chromedriver,解压后把chromedriver.exe放到脚本同级目录即可。这是我在三所高校巡讲时,遇到最多的“非标环境”问题。

4.2 主测试文件详解:TestCtripFlight.py的逐行解读

TestCtripFlight.py是整套脚本的心脏,全文327行,我们聚焦最核心的test_flight_search_roundtrip()函数(R002用例):

def test_flight_search_roundtrip(): """R002:验证往返航班搜索流程及日期联动""" driver = webdriver.Chrome(options=chrome_options) driver.maximize_window() try: # 步骤1:访问首页(带超时重试) for attempt in range(3): try: driver.get("https://www.ctrip.com/flights") WebDriverWait(driver, 15).until( EC.title_contains("机票") ) break except TimeoutException: if attempt == 2: raise Exception("首页加载超时,检查网络或URL") time.sleep(2) take_step_screenshot(driver, "R002_001") # 首页成功加载 # 步骤2:切换至往返模式(携程默认单程,需主动点击) roundtrip_tab = driver.find_element(By.XPATH, "//div[@class='flight-type-tabs']//span[text()='往返']" ) roundtrip_tab.click() take_step_screenshot(driver, "R002_002") # 往返模式激活 # 步骤3:设置出发日期(使用JS执行器) dep_date_input = driver.find_element(By.XPATH, "//input[@placeholder='出发日期']" ) dep_date_input.click() # 触发日历 driver.execute_script(""" document.querySelector("button[data-date='2025-05-20']").click(); """) take_step_screenshot(driver, "R002_003") # 出发日期选定 # 步骤4:设置返回日期(关键!验证联动逻辑) # 携程规则:返回日期必须晚于出发日期,且不能超过出发后30天 ret_date_input = driver.find_element(By.XPATH, "//input[@placeholder='返回日期']" ) ret_date_input.click() driver.execute_script(""" document.querySelector("button[data-date='2025-05-25']").click(); """) take_step_screenshot(driver, "R002_004") # 返回日期选定 # 步骤5:执行搜索(注意:往返模式下按钮文字是"搜索往返航班") search_btn = driver.find_element(By.XPATH, "//button[.//span[text()='搜索往返航班']]" ) search_btn.click() # 步骤6:等待结果页加载,并验证航班列表存在 WebDriverWait(driver, 20).until( EC.presence_of_element_located((By.XPATH, "//div[@class='flight-list']")) ) take_step_screenshot(driver, "R002_005") # 结果页加载完成 # 步骤7:业务断言——验证返回日期筛选生效 # 检查结果页URL是否包含retDate参数 assert "retDate=2025-05-25" in driver.current_url, \ "返回日期未正确传递至结果页" # 步骤8:截图存证(最终状态) take_step_screenshot(driver, "R002_006") finally: driver.quit()

这段代码体现了四个实战原则:
-容错设计:首页加载用3次重试,避免偶发网络抖动导致整套用例失败;
-业务感知:不仅点按钮,还验证URL参数,因为这才是携程真正的业务逻辑落点;
-状态隔离:每个take_step_screenshot()前都有明确的状态描述(“往返模式激活”“出发日期选定”),截图即文档;
-资源清理finally: driver.quit()确保即使断言失败,浏览器也会关闭,避免残留进程占用内存。

4.3 截图管理:如何用screenshots目录构建“可视化测试报告”

screenshots目录不是垃圾场,而是你的证据仓库。脚本运行后,你会看到类似这样的文件列表:

screenshots/ ├── 17152329344886_CtripFlight_R002_001.png # 首页 ├── 17152329344887_CtripFlight_R002_002.png # 往返模式 ├── 17152329344888_CtripFlight_R002_003.png # 出发日期 ├── 17152329344889_CtripFlight_R002_004.png # 返回日期 ├── 17152329344890_CtripFlight_R002_005.png # 结果页 └── 17152329344891_CtripFlight_R002_006.png # 最终状态

这些文件可直接用于:
-赛前自检:用图片查看器批量打开,快速确认每步UI状态是否符合预期;
-答辩演示:将R002_001.pngR002_006.png做成6页PPT,配文“R002用例执行证据链”,评委一眼看懂;
-问题复现:若某次运行失败,直接打开对应时间戳的截图,无需重跑——比如R002_004.png里返回日期没高亮,说明JS执行器点击失败,优先检查data-date属性是否存在。

实操技巧:Windows用户可右键screenshots文件夹 → “属性” → “常规” → 勾选“只读”,防止误删。Mac/Linux用户执行chmod 444 screenshots/。这是我在指导学生时,为避免“手滑rm -rf”导致证据丢失的保命操作。

5. 常见问题与排查技巧实录:那些让你拍大腿的“原来如此”

5.1 典型问题速查表

问题现象可能原因快速排查命令解决方案
NoSuchElementException报在城市输入框定位携程首页新增了A/B测试流量层,搜索框被包裹在<div id="ab-test-container">driver.page_source.count('searchBox')返回0在XPath前加//div[@id='ab-test-container']或改用//input[@placeholder='出发城市']
截图全黑或只有浏览器边框Chrome启动时未禁用GPU加速(Linux服务器常见)chrome_options.add_argument('--disable-gpu')chrome_options中添加该参数
ElementClickInterceptedException频繁发生页面有浮动广告遮挡目标元素driver.find_element(By.XPATH, "//div[@class='ad-banner']").is_displayed()添加driver.execute_script("arguments[0].style.display='none';", ad_element)隐藏广告
pytest运行报ModuleNotFoundError: No module named 'selenium'虚拟环境未激活,pip install到了系统Pythonwhich pythonpip show selenium确认which python输出路径包含ctrip_env,否则重新激活
日期控件点击后无反应Chrome版本>120,chromedriver-autoinstaller未及时更新chromedriver-autoinstaller --version升级到0.6.4+,或手动指定chromedriver路径

5.2 独家避坑技巧:来自真实赛场的血泪经验

技巧1:用“页面快照”替代“实时断言”应对动态内容
携程航班列表会实时刷新价格,price_element.text可能在你find_elementtext之间被修改。解决方案是一次性抓取整个列表HTML

# 错误:两次DOM查询,中间可能变化 price_elem = driver.find_element(By.XPATH, "//span[@class='price']") price_text = price_elem.text # 此时价格可能是¥580 # 正确:一次查询,提取全部 flight_list_html = driver.find_element(By.XPATH, "//div[@class='flight-list']").get_attribute('outerHTML') # 用正则从HTML中提取所有价格:<span class="price">¥580</span> prices = re.findall(r'<span[^>]*class="price"[^>]*>¥(\d+)</span>', flight_list_html)

技巧2:给截图加水印,杜绝“张冠李戴”
比赛提交材料时,曾有队伍因截图命名混乱,把R001的图贴到了R003的报告里。我们在take_step_screenshot()中加入水印:

from PIL import Image, ImageDraw, ImageFont def add_watermark(filepath, text): img = Image.open(filepath) draw = ImageDraw.Draw(img) font = ImageFont.truetype("arial.ttf", 24) draw.text((20, 20), text, fill=(255, 0, 0), font=font) img.save(filepath) # 调用 add_watermark(filepath, f"R002_003 | {datetime.now().strftime('%H:%M:%S')}")

红色时间戳水印,既证明截图真实性,又标注精确时刻,评委想挑刺都难。

技巧3:用pytest的--tb=short参数净化日志
默认pytest报错堆栈长达200行,新手根本找不到关键错误。运行时加参数:

pytest TestCtripFlight.py -v --tb=short

错误信息从“File /path/to/selenium/webdriver/remote/webelement.py, line 123…”压缩为“E selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {“method”:“xpath”,“selector”:“//input[@placeholder=‘出发城市’]”}”,效率提升5倍。

6. 教学延伸与能力跃迁:从跑通脚本到理解测试本质

这套脚本的终点,不是pytest输出6 passed,而是你开始思考:
- 为什么携程要用data-date而不是value属性存储日期?(答案:为无障碍访问支持屏幕阅读器)
- 如果把R002用例的返回日期改成2025-06-25(超30天),脚本应该报什么错?是前端拦截还是后端返回空列表?(答案:前端JS会禁用超出范围的日期按钮,document.querySelector("button[data-date='2025-06-25']")返回null)
- 当前脚本只验证“有航班”,如果赛题要求“验证头等舱占比≥15%”,你该如何扩展?(答案:用XPath定位//div[contains(@class,'cabin-class') and contains(text(),'头等舱')],统计数量并计算比例)

这就是从“工具使用者”到“质量守护者”的跃迁。我建议你在跑通全部Rxx用例后,做三件事:
1.反向工程:用Chrome DevTools的Network面板,记录一次真实搜索的XHR请求,对比脚本中driver.current_url的参数,理解前端如何与后端协同;
2.破坏性测试:在test_flight_search_single()中,把出发城市设为“火星”,观察携程的错误提示文案和样式,思考“无效输入”的测试边界;
3.性能埋点:在try块开头加start_time = time.time()finally前加print(f"R001耗时: {time.time()-start_time:.2f}s"),收集各步骤耗时,你会发现“等待下拉列表”占总时长65%——这直接指向优化方向:能否用driver.get("https://www.ctrip.com/flights?dep=SHA&arr=PEK&depDate=2025-05-20")直连结果页?

最后分享一个小技巧:把TestCtripFlight.py打印出来,用红笔在每行take_step_screenshot()旁边手写一句“这一步在验证什么业务规则”。比如在R002_004.png旁写:“验证返回日期选择器是否启用,且日期范围符合携程30天规则”。当你写满三页纸,你就真正读懂了这套脚本——它不只是代码,而是用Python写的测试思维导图。

我在带队参加全国总决赛前夜,和队员一起把所有截图按用例编号贴在白板上,用箭头连出业务流,那面墙成了我们最硬的底气。现在,这份底气,我也交到你手上。

本文还有配套的精品资源,点击获取

简介:专为2025软件测试大赛Web赛道预选赛设计的实战型自动化测试资源,基于Python 3.8和Selenium 4.x实现携程机票官网核心流程覆盖:首页搜索框输入、出发/到达城市选择、日期控件操作、航班列表筛选及结果页断言验证。脚本采用显式等待策略应对动态加载,每步操作后自动触发截图并保存至screenshots目录,文件名含时间戳与用例编号(如16522329344886_CtripFlight_R002_001.png),便于快速定位异常环节。主入口TestCtripFlight.py已适配pytest 8.3.5,无需修改即可执行;配套requirements.txt明确依赖版本,.gitignore和README.md提供基础维护指引;目录中已预置.pyc字节码、.pytest_cache缓存及__pycache__结构,减少初学者环境配置负担。适用于高校教学演示、学生自主演练或UI测试入门实践,重点训练元素定位策略、交互动作链编写、页面状态断言及失败现场留存能力。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 7:14:06

从模块手册到代码:深度解读MAX31865寄存器配置与STM32 SPI通信时序

从模块手册到代码&#xff1a;深度解读MAX31865寄存器配置与STM32 SPI通信时序 当你在调试MAX31865时遇到数据偶尔跳变、通信失败或配置失效的问题&#xff0c;是否曾怀疑过自己的SPI时序配置&#xff1f;本文将从芯片数据手册的寄存器位定义出发&#xff0c;结合STM32的SPI外设…

作者头像 李华
网站建设 2026/6/8 7:05:13

STM32寄存器开发:深入理解GPIO复用功能与引脚重映射

一、引言GPIO作为MCU与外界交互最基本的接口&#xff0c;其复用功能&#xff08;Alternate Function&#xff09;允许我们将同一个引脚分配给不同的片上外设&#xff08;如USART、SPI、I2C、定时器等&#xff09;&#xff0c;而引脚重映射&#xff08;Remap&#xff09;则是在芯…

作者头像 李华
网站建设 2026/6/8 7:00:30

2026山东大学项目实训个人记录(五)

一、本阶段任务背景 本阶段继续进行前后端的联调工作&#xff0c;并修复上次联调之后&#xff0c;前后端都发生了修改导致的新问题。 二、具体工作 1.问题现象 点击登录/注册按钮&#xff0c;浏览器 Network 无任何请求&#xff0c;后端终端也没有任何日志。前端页面看起来一切…

作者头像 李华
网站建设 2026/6/8 6:58:56

GTX 1660 SUPER炼丹炉搭建记:保姆级CUDA 11.5.1 + cuDNN 8.3.0配置避坑指南

GTX 1660 SUPER深度学习环境配置实战&#xff1a;从驱动匹配到模型验证第一次接触深度学习训练的朋友&#xff0c;往往会被GPU环境配置的复杂性劝退。本文将手把手带你用GTX 1660 SUPER这张性价比显卡&#xff0c;搭建一个稳定高效的"炼丹炉"。不同于简单的安装步骤罗…

作者头像 李华