
系列从 0 到 1 搭建桌面应用 UI 自动化测试框架上一篇第 2 篇搭入口层提权、进程连接与日志下一篇第 4 篇建立证据链日志、截图与 Allure关键词fixture 链、POM 雏形、场景法、显式前置条件本篇导读入口能连上不等于能跑用例。第三步要证明框架能按真实用户路径把应用带到可操作状态。这一步的产出是一条可复用的 fixture 链三个最小 Page Object第一条 smoke 用例场景法的基本规则。一句话隐式状态是稳定性的敌人——登录态、相机在线、采图模式必须全变成 fixture 显式前置条件。流程图fixture 链如何托起业务主链路业务主链路fixture 链appscopesessionmain_windowscopefunctionlogged_in_main_windowscopesessionlogin_windowscopefunction启动等 WaitDialog 消失刷新相机登录关闭登录后弹窗连接首台相机确保采图模式1. 设计 fixture 链主测试套件的 fixture 是一条链pytest.fixture(scopesession)defapp():sessionAppSession()ifconfig.APP_PID:session.connect(config.APP_PID,externalTrue)else:session.start()yieldsessionfinally:session.close()pytest.fixture(scopefunction)defmain_window(app):# 等启动加载框 WaitDialog 消失后再返回...pytest.fixture(scopefunction)deflogin_window(app):# 当前必须在登录界面否则说明前序状态异常...pytest.fixture(scopesession)deflogged_in_main_window(app):# 登录一次后续用例复用...设计要点app是 session 级整个会话共享一个应用实例logged_in_main_window是 session 级只登录一次login_window是 function 级要求当前确实在登录界面登录后兜底关弹窗、连接首台相机、确保处于采图模式。2. POM 雏形先只做三个类BasePagefind / click / type_text / wait_enabled / captureLoginPage刷新、选相机、取详情、登录MainWindow连接状态、采图开关、AI 拍摄、对焦、光源。classBasePage:def__init__(self,window,appNone):self.windowwindow self._appapp self.current_step初始化deffind(self,**kwargs):returnself.window.child_window(**kwargs)defclick(self,**kwargs):self.find(**kwargs).click_input()returnself不要一上来追求“完美 POM”。先让用例能读再逐步抽公共动作。3. 第一条用例先测框架不测业务tests/test_smoke.py只验证三件事应用能启动且进程存活主窗口可见主窗口可交互未卡死。pytest.mark.order(1)classTestSmoke:deftest_app_launches(self,app):assertapp.process_idisnotNoneassertapp.appisnotNoneassertapp.app.is_process_running()这条用例的价值不是覆盖功能而是验证框架底座。4. 确定场景法项目采用场景法启动 → 刷新相机 → 登录 → 连接相机 → AI 拍摄 → 对焦 → 光源 → 扩展参数 → ...整个会话共享应用状态不每个用例重启。好处是贴近真实路径、执行快代价是用例间有耦合必须靠 fixture 和显式前置条件控制。场景法的三条规则每个共享状态都要有 fixture 契约每个用例的开始状态必须可判定不能靠“前面跑过所以现在应该有”的隐含假设。5. 真实踩坑现象根因修正单用例第二个找不到登录界面login_windowfunction 级加了已在主界面就跳过的自愈删除 function 级自愈——单用例必须显式在登录态session 级logged_in_main_window跳过登录是合理设计登录本身是场景链一环只执行一次拿到 WaitDialog 瞬时引用调用即 COMError加载框还没消失main_windowfixture 轮询等加载框消失点了侧边栏「运行」采图没启动「运行」是导航按钮采图开关是图像工具栏的ImageToolboxButton登录后单独检查采图按钮标题单文件运行时相机不在线没经过连接相机的用例logged_in_main_window兜底连接首台相机这些坑的共同点都在试图用“隐式状态”换取方便。6. 验收清单全量跑启动、登录、主界面、采图链路通过单跑test_smoke.py也能通过连续跑 3 次不需要人工干预失败时能明确知道卡在“启动 / 登录 / 连接 / 采图”哪一步。7. 可复用要点隐式状态是稳定性的敌人登录态、相机在线、采图模式都必须显式化。第一条用例先测框架不测业务框架不稳时业务用例通过只是运气。场景法不是偷懒不隔离而是用 fixture 把共享状态变成契约。关键代码示例示例 1fixture 链真实conftest.py节选3 层共享状态设计# 层 1session 级应用实例场景法整个会话共享一个pytest.fixture(scopesession)defapp():sessionAppSession()ifconfig.APP_PID:session.connect(config.APP_PID,externalTrue)else:session.start()yieldsession session.close()# 层 2session 级登录后主界面整个会话只登录一次pytest.fixture(scopesession)deflogged_in_main_window(app):topapp.current_top_class()# 情况 A已在主界面 → 跳过登录注意这是 session 级 fixture登录本身是场景链一环只执行一次所以可跳过function 级 login_window 不允许自愈iftopWindowClass.MAIN:mwMainWindow(app.main_window,appapp.app)yieldmw;return# 情况 B在登录界面 → 先观察 20s 是否自动登录应用记住会话时会无操作跳转iftopWindowClass.LOGIN:deadlinetime.time()20whiletime.time()deadline:time.sleep(1)topapp.current_top_class()iftopWindowClass.MAIN:break# 情况 Crestart 自愈top 为空/窗口异常时ifnottop:app.restart()...# 登录后兜底关弹窗 连首台相机 确保采图模式mwMainWindow(win,appapp.app)mw.dismiss_popup_if_any(timeout3.0)ifnotmw.is_camera_online():mw.refresh_cameras()ipsmw.get_camera_ips()ifips:mw.select_camera(ips[0])# 确保采图开关是 ImageToolboxButton不是侧边栏的「运行」导航按钮yieldmw示例 2第一条 smoke 用例真实tests/test_smoke.py28 行先测框架底座pytest.mark.order(1)pytest.mark.smokeclassTestSmoke:allure.severity(allure.severity_level.BLOCKER)deftest_app_launches(self,app):assertapp.process_idisnotNoneassertapp.appisnotNoneassertapp.app.is_process_running()deftest_main_window_visible(self,main_window):assertmain_window.is_visible(),主窗口不可见deftest_main_window_enabled(self,main_window):assertmain_window.is_enabled(),主窗口不可交互示例 3场景法完整用例模板真实tests/test_ai_shoot.py节选allure.epic(桌面应用)allure.feature(AI拍摄)pytest.mark.order(7)classTestAIShoot:deftest_ai_shoot_completes(self,logged_in_main_window):winlogged_in_main_windowwithallure.step(验证相机已连接):assertwin.is_camera_online(),f相机未在线:{win.get_connection_status()!r}win.capture_step(AI拍摄前,prefixai_shoot)withallure.step(点击 AI拍摄 按钮):win.click_ai_shoot()withallure.step(等待 AI拍摄 完成25s):time.sleep(25);win.capture_step(AI拍摄后,prefixai_shoot)withallure.step(验证生成效果样张 0-4):assertwin.get_ai_effect_count()5withallure.step(依次选择每个效果):foridxinrange(5):win.select_ai_effect(idx)win.capture_step(f效果{idx},prefixfai_effect_{idx})assertwin.is_camera_online()关键结论隐式状态是稳定性的敌人登录态、相机在线、采图模式都必须变成 fixture 显式前置条件不能靠「前面跑过所以现在应该有」。第一条用例先测框架底座先测进程存活、窗口可见/可交互再测业务功能。框架不稳时业务用例通过只是运气。场景法必须配单文件兜底logged_in_main_window里自动登录 20s 观察 相机自动连接 采图模式检查这三件事让run.py -f test_ai_shoot.py这种单文件调试也能成功。下一篇主链路跑通后下一步是让失败可解释第 4 篇建立证据链。