首页
/ PowerToys MouseUtils 模块 UI 自动化测试:发布测试清单迁移进度全解

PowerToys MouseUtils 模块 UI 自动化测试:发布测试清单迁移进度全解

2026-09-06 18:43:19作者:董灵辛Dennis

本指南以仓库文档 Release-Test-Checklist-Migration-Progress.md 为骨架,结合 MouseUtils 四个工具模块的 UI 自动化测试源码,系统解读 MouseUtils(鼠标实用工具)发布测试清单向自动化用例迁移的覆盖情况、每一条手工验证步骤的自动化实现方式,以及尚未自动化的场景及其技术原因。读完你可完整掌握 Find My Mouse、Mouse Highlighter、Mouse Pointer Crosshairs、Mouse Jump 四模块"手工清单 → 自动化用例"的对应关系,理解其基于像素采样、窗口句柄附加与屏幕坐标计算等黑盒断言手法的实现原理,并据此评估与补齐你自己的 UI 回归覆盖。

一、文档定位:一份"迁移进行时"的覆盖矩阵

MouseUtils(鼠标实用工具集)是 Microsoft PowerToys 中与鼠标输入体验直接相关的一组工具模块,在源码中集中存放于 src/modules/MouseUtils 目录,其 UI 自动化测试工程为 MouseUtils.UITests。发布前,项目通常会有一份面向测试人员的手工发布测试清单,覆盖每个模块的启用/停用、激活方式、外观与行为设置等场景;而 UI 自动化测试的目标,就是把这些"由人按步骤手工验证"的条目一条条改写成可以自动执行的 MSTest 用例。

Release-Test-Checklist-Migration-Progress.md 正是这份迁移工作的进度台账:它以清单为底稿,用复选框标注每条手工验证条目当前是否已迁移为自动化用例:

  • [x]:该清单项已完成自动化迁移(并有对应测试方法或测试辅助逻辑,见下文映射);
  • [ ]:该项仍待迁移/待补齐,属于后续工作的"欠账"。

全文档共 44 条验证点,其中 25 条已标记 [x](约 57%),19 条标记 [ ](约 43%)。文档开头的 Mouse Utils 是对共享清单模板中 "Mouse Utils" 一节的引用(该模板文件在当前仓库快照中并未随工程检出,因此本文以下内容以清单条目本身与可验证的测试源码为准)。

从测试工程属性看(MouseUtils.UITests.csproj),该工程基于 MSTest、引用通用 UI 自动化库 UITestAutomation,并显式关闭了 MSBuild 内嵌运行(<RunVSTest>false</RunVSTest>),说明它属于依赖真实 UI 会话、需要独立调度运行的 WinAppDriver/WinUI 会话级测试,而非普通的单元测试。

二、测试基础设施:清单条目是如何被自动执行的

在逐模块展开清单之前,先理解自动化用例复用的几块公共"脚手架",它们决定了清单里的条目能以多高的真实度被自动验证:

  1. 导航进入设置页:各测试类都实现了 LaunchFromSetting(),流程为——先通过 RestartScopeExe(...) 重启被测模块进程,把设置窗口调成 WindowSize.Large,若导航树中找不到 MouseUtilitiesNavItem 就先展开 InputOutputNavItem("输入和输出"分组),再点击 "Mouse utilities" 导航项进入鼠标工具设置页。
  2. 按可访问性标识定位控件:模块容器、开关、输入框全部通过稳定的 AutomationId 定位,统一定义在 util/MouseUtilsSettings.csAccessibilityIds 常量表中,例如:
    • 模块容器:MouseUtils_FindMyMouseTestIdMouseUtils_MouseHighlighterTestIdMouseUtils_MousePointerCrosshairsTestIdMouseUtils_MouseJumpTestId
    • 启停开关:MouseUtils_FindMyMouseToggleIdMouseUtils_MouseHighlighterToggleIdMouseUtils_MousePointerCrosshairsToggleIdMouseUtils_MouseJumpToggleId
    • 设置项分组:...ActivationMethodId...AppearanceBehaviorId...ExcludedAppsId 等。
  3. 真实的键鼠注入:用例通过 Session.SendKeys/SendKey/SendKeySequence 注入组合键(例如 Win + Shift + H、双击 LCtrl),通过 Session.PerformMouseAction(MouseActionType.LeftDown/RightDown/ScrollDown...)IOUtil.MoveMouseBy 注入鼠标按下/抬起/滚轮/拖动。
  4. 像素级"所见即所得"断言:由于这些工具的视觉反馈(聚光灯、高亮圈、十字准线)本身即功能主体,测试大量使用 GetPixelColorString(x, y) 在特定坐标采样屏幕像素颜色,并与设定的 RGB 值做 Assert.AreEqual / AreNotEqual。这是理解后文所有"校验覆盖层出现/消失"断言的关键——断言的不是某个 UI 控件属性,而是屏幕真实像素
  5. 窗口句柄附加与距离计算:Mouse Jump 用 Session.Attach("MouseJump") 附加预览窗口、以窗口中心点击后计算鼠标位移的欧氏距离(MouseJumpTests.CalculateDistance),用"光标是否落到屏幕中心附近(≤10px)"来等价验证"跳转到点击位置"。

针对每个工具的"设置模型"被独立建模,供用例在改动设置与断言结果时复用,例如 util/FindMyMouseSettings.csutil/MouseHighlighterSettings.csutil/MousePointerCrosshairsSettings.cs。这些模型还顺带暴露了设置项的取值范围字符串(见下),与清单中的待测设置一一对应。

三、Find My Mouse(找不到鼠标时快速定位):8/15 已迁移

Find My Mouse 在鼠标静止时被触发后,会在指针周围画出一个高亮"聚光灯"式的覆盖层,帮助用户在一堆窗口中找回鼠标。对应源码目录为 src/modules/MouseUtils/FindMyMouse(FindMyMouse.cpp/FindMyMouse.h),测试实现于 FindMyMouseTests.cs

3.1 启用/禁用与激活场景

清单中该模块"启用与触发"相关的 5 个条目均已 [x]

清单条目(手工验证步骤) 状态 对应自动化实现要点
启用 FindMyMouse 后,鼠标保持不动,按两次左 Ctrl,覆盖层出现 [x] TestEnableFindMyMouse:先 Toggle(true) 开启 → ActivateSpotlight 连续注入两次 LCtrlVerifySpotlightAppears 采样光标处像素断言等于聚光灯色、光标外区域断言等于背景色
按任意其他键,覆盖层消失 [x] 注入 Key.AVerifySpotlightDisappears 反向断言聚光灯/背景色不再出现
再次按两次左 Ctrl,覆盖层出现 `[x] 重复上述"出现"断言
按下鼠标按键,覆盖层消失 `[x] Session.PerformMouseAction(LeftClick) 后执行"消失"断言
停用 FindMyMouse 后按两次左 Ctrl,覆盖层不再出现 [x] TestDisableFindMyMouse 系列:启用→断言出现→Toggle(false)→断言消失→再次 Toggle(true) 恢复

需要注意测试的时序细节:两次 Ctrl 注入之间、以及断言之前都有 Task.Delay 等待,说明这类覆盖层存在渲染/动画时序,自动化必须容忍固定延迟(代码中多处 100ms~2000ms 不等)。

3.2 游戏模式(独占全屏)场景:尚未自动化

清单第 12~15 行专门针对 "Do not activate on game mode"(游戏模式下不激活)选项设计了正反两个场景,且均为 [ ]

  • 开启该选项并启动一个 CG native 独占全屏的游戏后,按两次左 Ctrl,验证覆盖层不出现;
  • 关闭该选项并启动同一款游戏后,按两次左 Ctrl,验证覆盖层出现(清单还如实备注"though it'll likely minimize the game"——覆盖层弹出很可能会把游戏最小化)。

从代码结构看,FindMyMouse 模块内确有与游戏模式相关的分支逻辑(见 FindMyMouse.cpp 中对全屏/游戏进程的检测处理)。这两条迟迟未 [x] 也符合工程直觉:UI 自动化会话无法可靠地拉起并维持一个 CG 独占全屏游戏,且注入按键会触发最小化等副作用,因此只能保留为手工回归点。这是"清单上合理保留手工项"的典型代表。

3.3 外观与行为设置项:部分已迁移

清单 3.3 的 8 个设置项验证中 3 项已 [x]

设置项 状态 备注
Overlay opacity(覆盖层不透明度) [ ] 当前仓库 FindMyMouseTests.csFindMyMouseSettings.OverlayOpacity 虽有赋值但未见注入到对应 UI 控件的逻辑,仍未完成像素级验证
Background color(背景色) [x] 通过颜色选择器弹窗设置 RGB 十六进制(校验形如 #RRGGBB 7 字符),再用像素断言核对
Spotlight color(聚光灯色) [x] 同上
Spotlight radius(聚光灯半径) [x] 通过 InputBox 直接输入半径像素值,VerifySpotlightAppearscursor + radius-1 处仍采样到聚光灯色,在 radius+50 之外采样到背景色
Spotlight initial zoom(初始缩放,1x 与 9x 差异明显) [ ] 代码已出现对该滑块的设置(spotlightInitialZoomSlider.QuickSetValue(...)),清单状态与之存在时间差
Animation duration(动画时长) [ ] 代码中同样已有对 FindMyMouseAnimationDuration 输入框的设置逻辑
切换激活方式为"晃动鼠标(shake)"并实测 [ ] FindMyMouseSettings.ActivationMethod 枚举已定义 ShakeMouse,但 ActivateSpotlight 中该分支仅留注释"Simulate shake mouse",尚未实现晃动模拟
Excluded apps(排除的应用) [ ] 测试中只对排除应用入口做了点击展开,尚未端到端断言"在排除应用内不触发"

这里有一个值得注意的观察:清单状态与当前代码之间存在明显的"时间差"。例如清单将 Spotlight initial zoom、Animation duration 记为 [ ],但当前 FindMyMouseTests.cs 中已经包含对 Zoom 滑块与动画时长输入框的注入与断言辅助逻辑。这说明仓库内该文件的实现进度已领先于迁移台账的记录(台账更新滞后于代码提交),阅读时建议以代码为"实现真值"、以清单为"官方待办视图"。

此外,FindMyMouse 的动画类断言还依赖系统"动画效果"开关:测试在检测到 "Animations are disabled in your system settings." 提示时会自动打开 Windows 设置开启动画,再回到 PowerToys 设置页重载用例(CheckAnimationEnable 辅助方法),从而保证覆盖层动画(淡入、缩放)能被稳定采样到。

四、Mouse Highlighter(鼠标点击高亮):8/12 已迁移

Mouse Highlighter 在按下/拖动鼠标按键时,于指针周围绘制随动的彩色光环,常用于演示/录屏场景。对应源码目录 src/modules/MouseUtils/MouseHighlighter,测试见 MouseHighlighterTests.cs

4.1 启用、拖动跟随与停用(5/5 已迁移)

清单中的 5 个功能场景全部 [x]

  • 按下激活快捷键后点击左/右键,验证出现高亮——测试注入 Win+Shift+H 开启高亮后,用 LeftDown/RightDown 模拟点击,在光标处与 cursor + radius-1 处断言命中主键/次键高亮色;
  • 按住左键拖动,高亮随指针移动——VerifyMouseHighlighterDragIOUtil.SimulateMouseDown(true) + 每 10ms 移动 1px 共 500 步的循环模拟拖动,在终点位置再次断言高亮色仍在光标下;
  • 按住右键拖动同理(SimulateMouseDown(false));
  • 再次按激活快捷键,点击不再出现高亮;
  • 停用模块后按激活快捷键,模块不再被触发(VerifyMouseHighlighterNotAppears 反向断言)。

4.2 设置项:快捷键与左右键颜色已迁移

设置项 状态 说明
修改激活快捷键并实测 [x] 先故意输入单键 H 触发 "Invalid shortcut" 无效提示断言,再输入 Win+Shift+H(另一用例为 Win+Shift+O)并保存,随后用新快捷键完成全套出现/消失断言
Left button highlight color [x] 通过颜色弹窗设置,十六进制为 8 位 ARGB(如 FFFF0000),断言时用 Substring(2) 剥离 Alpha 后与 #RRGGBB 像素值比对
Right button highlight color [x] 同上(如 FF00FF00
Opacity [ ] 待迁移
Radius [ ] 从设置模型看存在 "Radius (px) Minimum5" 输入框,代码里 SetMouseHighlighterAppearanceBehavior 已有对该输入框的写入断言
Fade delay [ ] 模型中为 "Fade delay (ms) Minimum0"
Fade duration [ ] 模型中为 "Fade duration (ms) Minimum0",且"高亮消失"断言正是依赖 Task.Delay(duration+100) 等待淡出完成后再反向采样

与 FindMyMouse 类似,Mouse Highlighter 的半径/淡出时长等条目在清单中虽标记为 [ ],但 MouseHighlighterTests.cs 中已存在 SetMouseHighlighterAppearanceBehavior 对 Radius、Fade delay、Fade duration 输入框及 Always highlight color 的完整注入实现——迁移台账同样落后于代码。淡出验证依赖设置模型的取值范围元数据,这是把"带单位的 UI 文本"与可执行断言解耦的典型做法("Radius (px) Minimum5"、"Fade duration (ms) Minimum0" 这样的字符串即控件自动化名称)。

五、Mouse Pointer Crosshairs(指针十字准线):5/10 已迁移

Mouse Pointer Crosshairs 以光标为中心绘制横竖十字准线,便于精确对齐与定位。源码目录 src/modules/MouseUtils/MousePointerCrosshairs,测试见 MousePointerCrosshairsTests.cs

5.1 基本功能场景(3/3 已迁移)

  • 按激活快捷键,十字线出现并跟随鼠标移动——VerifyMousePointerCrosshairsAppears 不仅在光标中心采样,还在 x±50 / y±50 四个方向采样,逐点断言命中十字线颜色;随后用 IOUtil.MoveMouseBy(-1,0)×100 拖动鼠标后再次断言,验证"跟随移动";
  • 再次按激活快捷键,十字线消失;
  • 停用模块后按快捷键不再激活。

5.2 设置项:仅"颜色"与"快捷键"已迁移

设置项 状态 取值范围(来自设置模型) 说明
修改激活快捷键 [x] 单键 H 触发 "Invalid shortcut" 断言后改用 Win+Alt+A / Win+Alt+P
Crosshairs color [x] 6 位 RGB,像素断言同样在中心与四方向采样
Crosshairs opacity [ ] "Crosshairs opacity (%)" 滑块 代码已有 opacitySlider.QuickSetValue(100) 与文本断言,清单未同步
Crosshairs center radius [ ] Minimum 0 / Maximum 500 (px) 代码已有 SetText 断言
Crosshairs thickness [ ] Minimum 1 / Maximum 50 (px) 同上
Crosshairs border color [ ] 代码已有颜色弹窗注入(SetColor),断言子流程完备
Crosshairs border size [ ] Minimum 0 / Maximum 50 (px) 同上

清单记录与代码的"时间差"在这一模块体现得最明显:SetMousePointerCrosshairsAppearanceBehaviorMousePointerCrosshairsTests.cs)已经把 opacity、center radius、thickness、border size、border color、以及 "Fix crosshairs length"(固定长度,含 Minimum 1 的 FixedLength)全部写入并断言,而清单里 7 个设置项中有 5 个仍是 [ ]。此外测试还覆盖了"展开外观与行为分组后滚动定位输入框"这类真实交互细节(ScrollDown×3),说明该用例已相当接近端到端操作形态。

六、Mouse Jump(鼠标跳屏):4/7 已迁移

Mouse Jump 通过激活快捷键弹出多屏预览画布,点击预览任意位置即可把光标瞬移过去。它是五件鼠标工具中实现分层最多的一件(WinUI3 前端 + Common 模型/助手库 + HotKeys),源码散见于 src/modules/MouseUtils/MouseJumpMouseJump.CommonMouseJump.WinUI3 等工程,测试见 MouseJumpTests.cs

6.1 已迁移场景(4/4)

清单条目 自动化实现
按激活快捷键,出现屏幕预览 TestEnableMouseJump2 注入新快捷键 Win+Shift+ZVerifyWindowAppearsSession.Attach("MouseJump") 确认预览窗口打开
修改激活快捷键后新快捷键生效 TestEnableMouseJump3 用另一组 Win+Shift+J 验证同样能拉起预览窗口
点击预览任意位置,光标跳到该处 VerifyWindowAppears 内的等价校验:预览窗口打开后,在其中心执行 LeftClick,随后比较鼠标新位置与屏幕中心,断言欧氏距离 <= 10,即"点击预览中心 → 光标被传送到屏幕中心"
停用模块后快捷键不再触发 Toggle(false) 后再次注入快捷键,VerifyWindowNotAppears 通过 IsWindowOpen("MouseJump") 反向断言窗口未打开

6.2 仍为手工回归的多显示器场景(3/7)

  • Display 设置中调整屏幕排列顺序后确认预览随之更新且功能正常——[ ]
  • 修改屏幕缩放比例后确认仍然正常——[ ]
  • 拔掉附加显示器后确认仍然正常——[ ]

这三条未自动化是合理的:它们要求用例运行期间动态变更系统显示拓扑(增删/重排/缩放显示器),这在共享 CI 主机与 WinAppDriver 会话中几乎不可控,属于环境破坏型手工用例。从代码证据看,Mouse Jump 对多显示器拓扑确实高度敏感——MouseJump.Common 下有完整的显示/设备/DPI 建模(DpiModeHelperScreenHelperDeviceInfo/DisplayInfo/ScreenInfo),多屏布局换算正是其核心逻辑,因此最稳妥的回归仍由真实多显示器环境的人工清单承担。

七、迁移进度汇总与"欠账"归因

将四模块的 [x]/[ ] 汇总如下:

模块 已迁移 [x] 待迁移 [ ] 合计
Find My Mouse 8 7 15
Mouse Highlighter 8 4 12
Mouse Pointer Crosshairs 5 5 10
Mouse Jump 4 3 7
合计 25(约 57%) 19(约 43%) 44

剩余 19 条大致可按原因归为三类:

  1. 系统/环境依赖型(难以自动化):如 Find My Mouse 的"游戏模式 + CG 独占全屏"正反用例、Mouse Jump 的"显示器重排/缩放/热插拔"——需要真实游戏或可变的多屏拓扑;
  2. 手势/物理操作型(模拟成本高):如 Find My Mouse 的"晃动鼠标"激活方式——ActivateSpotlight 中该分支只有注释占位,尚无可靠的晃动轨迹模拟;
  3. 台账滞后型(代码已就绪):Find My Mouse 的 initial zoom/animation duration、Mouse Highlighter 的 Radius/Fade delay/Fade duration、Mouse Pointer Crosshairs 的 opacity/center radius/thickness/border color/border size 等,在当前仓库的测试辅助方法中已存在完整注入与断言逻辑,仅清单勾选状态未同步。

在推进这类迁移时,可以按以下顺序自查:先在 util/Settings 模型类中补齐该设置项的自动化名称(含单位与上下限,如 "Crosshairs thickness (px) Minimum1 Maximum50");再在对应 *Tests.cs 中新增设置注入辅助函数(参考 SetFindMyMouseAppearanceBehavior/SetMousePointerCrosshairsAppearanceBehavior 的颜色弹窗 #RRGGBB/#AARRGGBB 校验、滑块 QuickSetValue、输入框 SetText 重试 5 次的既有套路);最后用屏幕像素断言验证"改了什么就看见什么",并把清单条目勾为 [x]

八、如何运行与查看这套 UI 测试

这些用例属于需要真实 Windows UI 会话的 UI 自动化测试(项目在 Linux 上无法直接执行,需在 Windows + PowerToys 开发环境构建后运行):

  • 测试工程:src/modules/MouseUtils/MouseUtils.UITests(MSTest,IsTestProject=true,产物输出到 tests\MouseUtils.UITests\);
  • 依赖:通用 UI 自动化框架工程 src/common/UITestAutomation(csproj 中 ProjectReference),它封装了 Session/By.AccessibilityId/GetPixelColorString/IOUtil 等能力;
  • 调度方式:由于 RunVSTest=false,需使用 vstest.console 或测试资源管理器显式运行;用例通过 [TestClass]/[TestMethod](显示名形如 MouseUtils.FindMyMouse.EnableFindMyMouse)组织,并带有 [TestCategory("Mouse Utils #N")] 分类标签,便于按模块/场景筛选;
  • 前提条件:被测的 FindMyMouse、MouseHighlighter、MouseJump、MousePointerCrosshairs 等模块可执行体需就绪,且系统需开启"动画效果",测试会自动检测 "Animations are disabled" 提示并代为开启。

更细的运行与调试说明可参考仓库开发文档 ui-tests.md 与 UI 自动化框架源码 UITestAutomation;MouseUtils 模块自身的实现与设置入口则分别在 FindMyMouseMouseHighlighterMousePointerCrosshairsMouseJump 各模块目录中。

结语

Release-Test-Checklist-Migration-Progress.md 不只是一份简单的勾选清单,它实际上是 MouseUtils 四模块"手工发布回归 → UI 自动化回归"迁移过程的唯一对照表[x] 代表某项能力已被自动化用例以真实的键鼠注入 + 像素/窗口断言覆盖,[ ]] 则暴露了自动化难以触碰的边界(独占全屏游戏、多显示器拓扑、晃动手势)。结合仓库内测试源码可以发现,清单的勾选状态滞后于代码实现进度——真正判断某条能力是否已自动化,应以 *Tests.cs 中的注入与断言逻辑为准,而以本清单作为统一视图进行跟踪与核对。这套"手工清单 + 自动化迁移台账 + 像素级断言"的组合,对任何以视觉反馈为核心的用户工具(尤其是鼠标类、画笔类、屏幕标注类功能)的回归测试设计都具备直接借鉴价值。

登录后查看全文
热门项目推荐
相关项目推荐