ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Selenium自动化测试:抽奖系统概率、库存与UI回归实战

Selenium自动化测试:抽奖系统概率、库存与UI回归实战 抽奖系统的测试最让人心里没底的从来不是某个按钮能不能点而是那些肉眼看不透的规则到底有没有在线上环境按预期跑。“中奖概率偏差了零点几”、“库存多扣了一次”、“连续快速点击会不会发出两条抽奖请求”这类问题在演示环境里靠手工点几轮根本说不清。所以我做抽奖系统回归时习惯把Selenium自动化测试放在关键路径上用脚本去验证前端交互、结果展示和后端数据变化把“感觉没问题”变成能落地的结论。这篇文章会从一个真实的测试需求出发覆盖抽奖系统测试方案的选择、用例设计、核心代码实现、高频问题排查以及如何把脚本进化成一套可持续运行的回归工程。适合正在做Web端自动化或者需要验证抽奖、领奖、库存这类带状态业务场景的同学参考。文章里的代码基于Python 3和Selenium 4编写即使你平时用Java栈思路一样能复用。1. 测试方案定位与前期拆解1.1 抽奖系统的测试难点到底在哪抽奖系统表面看只是“点一下按钮弹一个结果”实际拆开之后它比普通CRUD页面麻烦得多主要难点在四个地方。第一是概率规则。很多活动页面会根据用户等级、时间段、奖品池配置不同权重而且前端展示的“转盘”未必对应真实概率。测试时既要验证单次参与返回的奖品是否符合规则又要在多次抽样之后确认统计概率没有明显漂移。纯手工跑几十次抽奖费时间不说还容易数错。第二是库存一致性。抽奖接口通常会先扣减库存再发奖有些系统用了Redis lua脚本做原子扣减有些则是先查剩余数量再更新存在超发风险。UI层面验证的不只是“中奖后奖品数量变少”更重要的是并发场景下会不会出现库存变负数、用户已经看到中奖结果但后台没有发奖记录这类问题。第三是防刷和风控。抽奖活动几乎都会做来源校验、频率限制、登录态校验有些还会加滑块或验证码。这些逻辑恰恰最容易在自动化执行时爆出来比如脚本快速点击触发了限流比如同一个账号短时间请求次数过多被锁定。第四是前端交互本身。中奖动效、弹窗、抽奖按钮置灰、倒计时刷新、奖品发放到“我的奖品”列表这类体验细节恰好是接口自动化覆盖不到的盲区。抽奖系统如果不做UI层面的自动化等于把这些用户真实会感知到的问题全部交给了手工回归风险很高。1.2 为什么这次我选择Selenium而不是纯接口测试很多人一听到抽奖系统第一反应是写接口自动化。为了验证概率和库存接口测试确实是效率最高的手段一个循环每秒能跑十几个请求比界面快太多。但我在实际项目中并不打算把全部赌注压在接口测试上。原因很简单抽奖系统的核心业务是“用户在前台页面感受抽奖”。如果前端因为某个JS报错导致奖品转盘不转或者接口已经返回中奖但页面没有刷新出结果接口测试全部通过用户依然会投诉。再加上现在不少前端框架会把节点动态渲染到DOM里接口返回的字段在页面上未必一一对应。Selenium这类端到端UI自动化验证的是从点击到结果展示的完整链路它补的正是接口测试和最底层单元测试够不着的那一层。另外自动化测试的运行频率也不一样。抽奖活动的上线通常伴随运营配置变更比如奖品池调整、概率从10%改成5%。这类事情不是天天发生但每次改动都需要快速回归主流程频率不算高对运行耗时的容忍度足够完全适合用Selenium去做冒烟回归。如果是一个每秒千万级请求的秒杀场景我反而会建议把重心放在接口和压测上UI自动化只保留最小冒烟集。1.3 技术栈和测试环境的选定细节我这次选择的技术栈是Python 3.10 Selenium 4.x pytest 7.x Allure 2。选Python不是因为它一定比Java好而是因为团队里做测试脚本的人多数会Python维护成本低而且Selenium对Python的API支持非常成熟。pytest作为测试框架参数化能力、fixture机制、插件生态都够用配合Allure能生成看得懂的HTML报告。环境层面有几个需要提前确认的细节。被测系统必须使用独立的测试环境或预发环境绝对不能对着生产环境跑自动化。抽奖涉及真金白银和用户数据哪怕一次误操作都可能造成库存被清空或奖品被误发。我一般会在测试环境准备单独的活动ID和奖品池确保自动化测试的抽奖请求不会影响真实业务数据。浏览器方面本地开发时我喜欢用有头模式方便观察每一步执行。脚本在CI或者定时任务里跑时改成无头模式。但要注意无头模式下某些页面交互表现和真实浏览器不完全一样比如部分浏览器对无头模式下的性能、渲染策略有差异抽奖动效和弹窗显示时机可能受影响。所以首次搭建脚本时一定要先在有头模式全部跑通再切无头做稳定性验证。数据库和Redis的清理权限也很重要。自动化脚本会产生大量抽奖记录、奖品发放记录和订单数据。如果测试环境不能重置数据反复执行之后统计接口会变得很慢断言结果也会受到历史数据干扰。我会在每轮完整回归前执行一次数据初始化脚本把抽奖记录表、用户积分流水、奖品库存全部恢复到基线状态再把几条测试账号的抽奖次数重置掉。2. 抽奖场景建模与用例设计逻辑2.1 核心链路登录、活动页、抽奖与领奖抽奖系统的自动化用例设计我习惯先画主链路再补充分支。主链路就四条用户登录、进入活动页、点击抽奖、查看结果与奖品记录。登录操作比较特殊。抽奖活动往往要求用户必须登录才能参与有的还要求绑定手机号或完成实名认证。Selenium直接走UI登录最真实但每次跑脚本都走登录流程会拖慢执行速度而且遇上验证码就要命。我的做法是分两层登录流程单独保留一个冒烟用例走完整UI登录其他抽奖用例则不重复走UI登录而是通过调用测试环境的登录接口或构造Cookie直接把登录态注入浏览器。这样既能保证登录逻辑被覆盖到又能让主链路脚本跑得更快。活动页的进入路径也要留意。有些活动是首页广告位跳转到活动页有些是直接通过URL访问。如果每次都从首页开始点广告位中间环节太多定位稳定性差。我会把活动页URL参数化从直接访问活动页开始跑只在专门的“活动入口跳转”用例里才验证首页到活动页的完整路径。抽奖按钮的交互有很多细节要验证。正常状态是“可点击”点击后按钮不可重复触发结果弹窗出现后按钮复位。这些状态变化既是我们做断言的依据也是自动化脚本最容易踩坑的地方。领奖环节通常在中奖后出现要验证奖品进入“我的奖品”列表、优惠券券码正确展示、实物奖品地址填写流程可以走通。2.2 概率与库存相关的关键校验点概率和库存是抽奖系统的灵魂也是UI自动化最有价值的部分。概率验证要分成“单次结果正确性”和“多次统计稳定性”两个维度。单次结果正确性是断言接口返回中奖结果后页面展示的奖品名称和配置的奖品池一致比如用户抽中“5元红包”页面不能出现“10元红包”。多次统计稳定性通常用固定前置条件跑N次抽奖统计中奖次数占比再和期望概率对比。这里不是要做一个统计假设检验而是设置一个合理区间比如期望30%中奖率跑60次允许中奖次数落在11到25之间超出范围就报警。注意概率类断言不能设得太死板。60次抽样本身就有随机波动如果把断言范围压到“正好18次”大概率误报。一般我会用二项分布的标准差估算一个阈值宁可区间放宽一点也不要在小样本下制造大量不稳定失败。库存验证的经典做法是把某个奖品库存设置成很小的值比如只有2份然后连续抽奖三次以上。断言逻辑是这样的前面两次中奖后库存依次递减第三次时页面应返回“奖品已领完”或“库存不足”不可以再出现成功中奖。更严格一点要在脚本里同步查询测试库的库存数字对比界面展示的“剩余数量”是否一致。并发场景在纯UI自动化里比较难完全复现但可以模拟出一个简化的“多人同时抽奖”效果。用Selenium Grid同时起多个浏览器实例每个实例用不同账号在同一活动上抽奖看库存是否会超发。能跑出几个浏览器已经不错了真正的并发压力还是要交给JMeter这类工具去压接口UI层面只验证极端情况下的页面表现。2.3 异常与反作弊场景用例怎么设计除了主流程异常场景设计决定了这套自动化的下限。我总结了几类必须覆盖的边界场景。未登录状态访问活动页点抽奖系统应弹出登录引导或跳转登录页不能出现报错白屏。登录过期之后继续点击抽奖应被系统提示“登录已过期”并且不能产生抽奖请求。奖品库存为0时进入活动页前端应直接置灰抽奖按钮或展示“活动已结束”。重复点击抽奖按钮页面必须做防重复提交脚本验证的核心是点击两次之后后台不会产生两条抽奖记录。中奖弹窗期间再次点击页面其他区域弹窗不能异常关闭奖品不能出现重复发放。反作弊场景自动化要小心处理不能真的把线上风控系统给触发穿。我的做法是在测试环境关闭强验证码和滑块脚本模拟用户常规节奏随机延时在1到3秒之间不要用固定间隔疯狂点击。同时验证一个关键点短时间内同一个账号连续抽奖超过活动上限时系统应返回“今日抽奖次数已用完”提示清晰不影响页面其余功能。2.4 测试数据的准备和清理策略做抽奖系统自动化测试数据是最大的隐形工作量。数据准备不足脚本跑起来就像隔靴搔痒。我会把数据准备分成三类。一类是账号数据至少准备三到五个测试账号用途不同一个用于常规抽奖一个用于库存耗尽场景一个用于次数超限场景一个用于未登录场景。另一类是活动配置数据包括活动ID、奖品池配置、抽奖次数限制、活动上下线时间。第三类是环境状态数据比如Redis里的奖池剩余数量、发放记录表里的历史数据。数据准备的方式优先级最高的是通过后台管理界面或接口批量写入不要在自动化脚本里依赖手工造数。造数时要把“概率设置”和“库存数量”区分开。比如验证必中场景把目标奖品概率配成100%其余奖品概率配成0%验证必不中场景则反过来。每次用例执行完之后还必须清理抽奖记录和奖池数据不然下一轮执行的统计会被污染。3. 从0到1实现Selenium自动化的关键代码3.1 基础框架搭建与Driver生命周期管理搭建框架前优先确认一件事项目里有没有统一的WebDriver管理方案。Selenium 4自带的WebDriver Manager可以在CI环境自动匹配浏览器版本和驱动版本省去手动下载和管理driver的麻烦这也是我推荐的方案。基础结构我会拆成三层conftest.py放fixture和全局钩子pages目录放页面对象testcases目录放测试用例。conftest里最关键的是driver的创建和销毁逻辑保证每个用例执行前拿到一个干净的浏览器会话执行后及时释放资源。import pytest from selenium import webdriver from selenium.webdriver.chrome.options import Options pytest.fixture def driver(): options Options() options.add_argument(--window-size1920,1080) options.add_argument(--disable-notifications) # 本地调试时注释掉下面这行改用有头模式 # options.add_argument(--headlessnew) driver webdriver.Chrome(optionsoptions) driver.set_page_load_timeout(30) yield driver driver.quit()这里有个细节要提醒driver的实例化如果放在模块级别浏览器会被所有用例共享用例之间状态隔离就不是很好前一个用例遗留的弹窗或Cookie会影响后续用例。所以我坚持用函数级fixture每个用例一个独立浏览器会话。虽然启动浏览器有性能开销但稳定性比那几秒的节省更重要。登录态的注入也是一个fixture要处理的点。通常测试环境会提供一个快速登录接口脚本先请求接口拿到Cookie再用driver.add_cookie把登录态写进浏览器这样所有依赖登录的用例都不需要走一遍UI登录流程。pytest.fixture def logged_in_driver(driver): token fetch_test_token(tester_001) driver.get(https://test.example.com/lottery/summer) driver.add_cookie({name: sessionid, value: token}) driver.refresh() return driver3.2 用Page Object模式封装抽奖页面Page Object模式听着抽象实际就是为了让测试用例别直接把CSS选择器写在业务代码里。页面元素位置一变你只要去改一个页面对应的封装文件而不是满项目到处找选择器。抽奖页面我通常抽象成三个对象登录页、活动页、奖品记录页。活动页是最核心的它至少包含抽奖按钮、活动标题、剩余抽奖次数、中奖结果弹窗这些元素。点击抽奖是一个动作获取结果是一个动作关闭弹窗又是一个动作。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LotteryPage: TITLE 某活动抽奖 def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def open_activity(self, activity_id): self.driver.get(fhttps://test.example.com/lottery/{activity_id}) def click_lottery(self): btn self.wait.until( EC.element_to_be_clickable((By.ID, lotteryBtn)) ) btn.click() def get_result_text(self): result self.wait.until( EC.visibility_of_element_located((By.CSS_SELECTOR, .lottery-result)) ) return result.text.strip() def close_result_popup(self): close_btn self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, .popup-close)) ) close_btn.click()封装类里不写assert是Page Object模式的铁律。页面对外只暴露操作和状态获取具体断言全部放在测试用例层。比如用例里可以断言返回文本是“恭喜”但页面类本身不关心结果对错这样职责才清晰。实际封装时我还会把“抽一次奖并拿到结果”合并成一个组合方法。组合方法的好处是概率验证用例里只需要循环调用这一个方法不必每次都手动处理弹窗。def draw_once(self): self.click_lottery() result self.get_result_text() self.close_result_popup() return result3.3 元素定位与等待策略自动化稳定性的生命线抽奖系统的前端通常大量使用Vue或React元素是动态渲染的class名经常带hash后缀比如lottery-btn__2jk3a。这类动态class一旦前端发版就会变定位方式如果只依赖class脚本很快报废。我更推荐优先用稳定的业务属性比如id、name、aria-label或者通过XPath定位“包含某个文本的按钮”。实在没有稳定属性时再用相对定位比如找某个容器的后代节点。还有一点是能不用XPath就不用XPathCSS选择器在绝大多数场景下性能更好。等待策略是所有Selenium脚本的命脉。很多同学习惯在代码里写time.sleep(3)遇到慢页面就改成sleep(5)这种写法是对稳定性最大的伤害。服务器偶尔慢了一秒固定等待时间就失效服务器提前完成又白白浪费等待时间。正确做法是用显式等待让脚本等到指定条件满足立即往下走超时再报错。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, lotteryBtn)) )抽奖场景里最常见的等待坑是动效还没结束。点击抽奖按钮后前端会先播放转盘动画再弹出结果。如果脚本在动画期间就去查找结果元素元素可能还没挂载到DOM上。针对这种情况不要只等元素存在要等它可见且文本非空。也可以用text_to_be_present_in_element这类条件明确等待特定文案出现。如果页面里有iframe嵌套比如抽奖模块被嵌在活动主页面里的子页面中必须先把driver切换到对应iframe才能定位内部元素。这个我在项目里踩过不少坑后面问题排查部分会展开。3.4 数据驱动的参数化用例与断言细节抽奖用例最大的特点就是重复性高同样的抽奖动作要验证不同奖品配置、不同账号类型、不同抽奖次数。把这些重复抽出来用pytest的参数化一次搞定是提升维护效率的关键。参数化有三个维度最常用。第一个是奖品池类型比如“普通奖品池”、“高端奖品池”、“空奖品池”。第二个是用户类型比如“新用户”、“老用户”、“已抽完次数用户”。第三个是动作组合比如“抽一次”、“连抽三次”、“抽奖中刷新页面”。把这些维度组合起来写成数据驱动用例维护起来非常舒服。import pytest pytest.mark.parametrize( activity_id, expected_prefix, [ (lottery_normal, 恭喜), (lottery_hightier, 幸运), (lottery_empty, 已领完), ] ) def test_lottery_result(logged_in_driver, activity_id, expected_prefix): page LotteryPage(logged_in_driver) page.open_activity(activity_id) result page.draw_once() assert expected_prefix in result断言方面接口返回和页面展示的一致性是我单独加的检查点不只是检查文案。比如抽奖结果弹窗里展示的红包金额我需要拿它和接口返回的实际金额做个对比。这个做起来不复杂但能抓住不少前端取值错误的问题。def test_red_packet_amount_matches_interface(logged_in_driver): page LotteryPage(logged_in_driver) page.open_activity(lottery_redpacket) result_text page.draw_once() actual_amount float(page.get_display_amount()) expected_amount page.get_api_amount_for_current_result() assert actual_amount expected_amount注意这段代码里的get_api_amount_for_current_result在真实框架里往往是通过浏览器导入Har或拦截请求来获取但在简单场景下可以改成读取接口返回存到页面上。实际实现方式取决于你的项目架构核心思路是“页面显示值和后端返回值要相互印证”。4. 真实执行中的高频问题与排查实录4.1 元素找不到、点击无反应怎么查运行Selenium脚本时报错最多的就是NoSuchElementException和ElementClickInterceptedException。这些错误看起来是同一个原因——“元素定位失败”实际背后可能有多种情况。最常见的三种场景。第一元素真的不在当前页面可能是弹窗遮住了页面主体、tab切换导致DOM重绘、iframe没有切换。第二元素在DOM里存在但不是“可点击”状态按钮被置灰、被loading遮罩挡住、被fixed定位的悬浮层覆盖这时click()会报元素不可点击。第三元素定位器本身过期了前端发版后id或class发生了变化。排查时我会按固定顺序来先在浏览器控制台手动执行document.querySelector确认页面里到底有没有这个元素再检查是否存在iframe嵌套在DevTools里看元素是否在frame里再检查元素在页面上的可见区域看看有没有遮罩层挡着最后再看元素是否被动态替换。定位符写得太年轻也是老问题尽量用稳定的属性而非动态生成的文本或class。有些时候确实是业务逻辑导致页面没有渲染出该出现的元素这种“假失败”背后往往藏着真bug。比如库存不足时按钮直接不显示而不是显示“已领完”这可能是前端没做状态分支属于产品缺陷不是脚本问题。4.2 抽奖动效和弹窗导致断言失败抽奖页面非常喜欢用CSS动画和延迟弹窗比如转盘转3秒才出结果、中奖弹窗带渐入效果。这类动效会让自动化脚本的时序变得很不确定。我遇到过一个真实案例点击抽奖后脚本用sleep(2)等结果本机跑得好好的换到一台性能较差的CI机器上2秒根本没播完动画结果元素还没出现用例直接失败。这就是固定等待的标准翻车现场。后来我把所有跟动画相关的等待都改成显式等待同时结合轮询和重试机制。有一个技巧是“等待元素状态稳定”先等结果元素出现再等它的文本值不再变化这样能保证动画结束、文案刷新完成后再做断言。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC result WebDriverWait(driver, 10).until( lambda d: d.find_element(By.CSS_SELECTOR, .lottery-result) ) WebDriverWait(driver, 10).until( lambda d: result.text )如果弹窗本身有模糊、渐入、渐出效果不要直接点击关闭按钮容易点在过渡期间导致事件丢失。加一步前置等待先断言弹窗右上角的关闭按钮可点击再执行点击。另外断言文案本身也可能是坑。中奖弹窗会先显示一句“正在开奖...”再变成“恭喜获得5元红包”。如果脚本在过渡文案还没刷新时就断言结果自然是失败的。这种场景最佳做法是断言最终文案而不是中间状态。4.3 并行执行时窗口资源互相干扰跑Selenium自动化最爽的是“并行”最痛的也是“并行”。我一开始天真地把用例全部交给pytest-xdist并行执行结果跑着跑着就出一大堆莫名其妙的失败页面元素找不到、登录Cookie串号、浏览器资源不足导致页面白屏。并行资源干扰通常出现在两个层面。操作系统层面一台机器同时起十几个浏览器CPU和内存很容易被吃满尤其是无头模式下的Chromium占内存很离谱。业务逻辑层面多个浏览器实例共用一个测试账号的时候A实例和B实例同时在同一个活动页抽奖频率限制就会立刻触发抽奖接口返回异常页面展示自然对不上。我的解决思路是分层控流。第一给测试账号做隔离每个并行worker使用独立账号。第二限制并行度单机并行数控制在4以内再多就交给Selenium Grid分布式跑。第三用例设计上尽量避免多个并行用例共享同一个秒级敏感的活动配置比如库存只剩1份的活动就不要让一堆用例同时去抽。如果在脚本里还是需要短暂停顿比如等待动画或等待接口返回我一般用随机延时替代固定延时让每个并行worker的节奏错开降低同时发请求的概率。不要小看这个细节它经常能减少一半以上的偶发失败。4.4 服务端风控和限流带来的假性失败抽奖系统的服务端几乎都会有风控策略测试环境也不例外。自动化脚本一旦操作节奏太快比如连续几秒内频繁点击风控就可能把当前账号临时限制住接口开始返回错误码。从脚本视角看前面一次用例正常运行下一次用例一进来账号就被提示“操作太频繁”所有断言全军覆没。这种失败和脚本本身没关系纯粹是业务策略触发处理不好会让我们陷入无限改脚本、加了等待又触发超时的新怪圈。我的应对策略分三层。第一层测试环境配置上把风控阈值调高或者干脆关闭验证码、滑块这类强交互验证把风控从“阻断”降级为“日志记录”。第二层脚本层面控制速率每个用户操作之间加随机延时确保单账号单位时间请求次数不超过正常用户上限。第三层用例层面识别风控拦截特征一旦页面出现“操作频繁”的提示主动跳过当前用例并明确标记为“风控拦截”而不是让测试框报一个元素超时的迷惑错误。这里要特别说一句风控逻辑也是系统功能的一部分完全绕过它去测试是不对的。正确的做法是保留一条独立的“风控专项用例”单独验证限流规则和拦截提示是否正常而让业务主流程用例在相对宽松的环境下稳定运行。把这两件事混在一起业务用例会变得极其脆弱。5. 抽奖回归自动化的工程化进阶5.1 用Selenium Grid把执行时间压下来抽奖用例和数据驱动组合一旦多起来串行执行能跑到半小时以上。这个耗时在定时巡检里勉强能忍但要做发布前的冒烟回归就太慢了。我的解决方案是上Selenium Grid让用例分散到多台机器的浏览器节点上跑。Grid的部署方式最轻量的是在Docker里起一个hub节点和几个node节点每个node注册一个或多个浏览器槽位。测试代码不再直接连本地的WebDriver而是通过RemoteWebDriver连接hub的地址。from selenium import webdriver from selenium.webdriver.common.options import ArgOptions options webdriver.ChromeOptions() driver webdriver.Remote( command_executorhttp://127.0.0.1:4444/wd/hub, optionsoptions )命令执行器和options之间还涉及到一些版本参数的兼容性比如浏览器版本号要固定node节点不能用“最新版”策略不然某次镜像更新后所有用例一起挂。这些参数配置在Grid的toml配置文件里就处理掉了远比在代码里指定版本号灵活。部署完Grid之后再配pytest-xdist分布式并行让不同worker从hub申请不同浏览器槽位整体执行时间能缩短到原来的四分之一。当然缩短时间的前提是用例之间数据隔离做得好否则并行加速只会换来满屏的偶发失败。5.2 失败现场截图、日志与录屏的组合证据链自动化跑到凌晨报错了等第二天看到一条“元素找不到”的文本日志你根本不知道线上发生了什么。所以我在工程化阶段坚持给每次失败保留三件套截图、页面源码、带执行步骤的日志。截图最好在失败钩子里统一完成而不是散落在每个用例里。用pytest的钩子拿到失败报告后把当前driver的截图保存下来同时把driver.page_source存成HTML文件这样不仅能看当时画面还能查当时DOM结构。import pytest import time pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: driver item.funcargs.get(driver) if driver: timestamp int(time.time()) driver.save_screenshot(freports/{item.name}_{timestamp}.png) with open(freports/{item.name}_{timestamp}.html, w, encodingutf-8) as f: f.write(driver.page_source)比截图更进一步的是录屏。Selenium本身不提供录屏能力但可以用浏览器DevTools Protocol里开启录屏或者通过外部命令录制整个浏览器窗口。录屏文件比较大我通常只在排查疑难偶发问题时临时打开平时默认只开截图和源码。另外日志里要记录当前用例操作到哪一步最好是在每个Page Object方法里打点记录失败时能直接定位到“点击抽奖按钮成功等待结果超时”这样的细节而不是只看一个空泛的异常堆栈。5.3 在CI流水线里做定时巡检和发布前回归不接CI的自动化脚本说难听点就是一次性工具。我在团队里把抽奖自动化分成了两层跑法。第一层是定时巡检。每天凌晨在测试环境跑一套精简冒烟集覆盖主链路、库存耗尽、次数超限和最核心的概率区间校验。巡检任务是低频的晚上跑对白天开发的干扰也小。一旦早上发现失败团队有时间在业务上线前定位问题。第二层是发布前回归。当有新的抽奖活动上线时在流水线的发布前阶段完整跑一遍抽奖用例集包含所有数据驱动组合。发布前回归要求快速反馈所以这层一定接上Selenium Grid并行执行执行时间尽量控制在10分钟以内。CI里的集成方式不管是Jenkins还是GitLab CI核心逻辑都是拉取测试代码、安装依赖、执行pytest、收集Allure报告、发送通知。流水线里还要固定浏览器镜像和依赖版本防止今天跑通过明天就跑挂。test-lottery: stage: test script: - pip install -r requirements.txt - pytest testcases --maxfail3 -n 4 --alluredirallure-results artifacts: paths: - allure-results/ - reports/ expire_in: 30 days only: - schedules - tags这里的调度触发和产物收集要看团队实际用的CI系统但核心思路不变以产物形式把测试报告留存下来方便事后追溯。Allure报告里的用例历史趋势特别有用能清晰看到某条用例过去三周是不是反复在挂这种“稳定地不稳定”的用例是后续维护的重点对象。我个人在实际操作中最大的体会是抽奖系统的自动化测试脚本和页面代码写出来只是第一步真正的价值在于用例设计和数据隔离以及把执行结果接入日常的巡检反馈。不要指望一套脚本写完就一劳永逸抽奖活动的奖品配置在变、页面动效在变、风控策略也在变自动化脚本和这些变化是需要长期磨合的。每次失败都可能是产品改动或环境变动发出的信号把信号的排查过程记下来这套自动化系统才会越来越稳。最后再分享一个小技巧给抽奖系统的每一个页面对象方法都加上执行日志并且把关键接口请求记录下来排查时会省掉非常多的时间。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进