Voice-QA:一套面向 Android 语音指令场景的自动化测试工具是怎么设计出来的

最近有一个语音测试工具被整理成了独立仓库:voice-qa

如果只看名字,很容易把它理解成“一个能把文本转成语音的小工具”。
但从真实测试落地角度看,做它的目的并不是解决“合成音频”这么单一的问题,而是想把语音指令测试里最零散、最重复、最容易靠人工反复点点点的那一段流程,真正沉淀成一套可复用工具链。

因为语音测试在 里都长期处在一个很尴尬的位置:

  • 它不是纯接口测试
  • 也不是传统 UI 自动化就能完整覆盖
  • 它涉及音频生成、设备播放、日志取证、结果断言、截图录像、设备管理
  • 但这些能力又往往分散在多个脚本、多个工具、多个同学手里

最后就会变成一个很典型的问题:

每个人都能“手动测一遍”,但很难把语音测试变成可批量执行、可留证、可回归、可持续维护的工程能力。

Voice-QA 就是在这个背景下做出来的。

一、为什么会专门做一套语音自动化测试工具

一提到语音测试,最常见的第一版理解是:

  • 放一段音频给设备听
  • 看设备有没有识别成功
  • 再人工盯着页面和日志判断结果

这种方式在问题规模很小时没什么问题,但一旦进入真实项目,很快就会失控。

因为你会马上遇到这些现实问题:

  • 需要批量测几十条甚至上百条语音指令
  • 不同指令前后要插入唤醒词、静音、返回首页等固定模板
  • 仅靠“听起来像对了”根本不够,需要日志证据
  • 只看日志还不够,还要同时截图、录屏保留现场
  • 不同 Android 设备要反复连、断、装包、跑测试
  • 同一条语音除了识别正确,还要看冷启动时间和执行稳定性

如果这些动作都靠人工做,效率会非常低;
如果这些动作都靠零散脚本拼起来,后面维护和排障会非常痛苦。

所以这里需要的不是一个单功能工具,而是一套完整的测试执行链路:

  • 能生成语音
  • 能批量播放
  • 能从设备侧拿到证据
  • 能做断言
  • 能输出报告
  • 能让非命令行用户也能操作

这也是 Voice-QA 的设计起点。

二、这个工具真正要解决的,不是 TTS,而是“语音测试链路碎片化”

从仓库结构看,Voice-QA 表面上有很多模块:

  • cmd/ 命令行入口
  • internal/tts/ 语音合成
  • internal/player/ 音频播放
  • internal/adb/ 设备操作与 logcat / 截图
  • internal/audio/ 音频处理
  • gui/ 图形界面
  • scripts/ 构建和依赖下载

但如果从测试问题出发看,这些模块其实都在围绕一件事:

把语音测试从“人工动作拼盘”收束成一条可执行、可断言、可留证、可交付的流水线。

也就是说,它的核心目标不是“合成出一段音频”,而是把下面这条链打通:

  1. 文本输入
  2. 按模板生成目标语音
  3. 在 Android 设备上播放
  4. 同步抓取 logcat
  5. 在合适时机截图、可选录屏
  6. 基于日志做自动断言
  7. 生成增量测试报告

这一点很重要。
因为如果工具只做到了第 2 步,那它只是 TTS 工具;
只有做到第 7 步,它才配叫“语音自动化测试工具”。

三、为什么在架构上把它拆成 CLI、内部能力层和 GUI 三层

这个项目的一个关键设计,是没有直接把所有逻辑都塞进 GUI,也没有只做命令行。

更倾向的结构是:

1. 命令行层负责标准化执行入口

CLI 是最稳的一层,因为:

  • 适合批量执行
  • 适合脚本和 CI 接入
  • 参数和行为边界清晰
  • 更容易做回归和排障

从 README 里也能看到,命令行已经覆盖了几种核心模式:

  • 初始化配置
  • 单条文本生成
  • 批量文本生成
  • 简单模式生成
  • 播放模式测试
  • 指定配置文件执行

这意味着底层能力不是绑死在界面上的。

2. internal/ 能力层负责把“一个个动作”沉淀成模块

仓库里最有价值的部分,其实是 internal/ 这层。

它把问题拆成了几类稳定能力:

  • 音频处理
  • 配置管理
  • 音频播放
  • TTS 引擎抽象
  • ADB 与设备取证

这种拆法的好处是非常直接的:

  • TTS 引擎可以替换
  • 设备取证逻辑不会污染生成逻辑
  • 播放模式和 GUI 都能复用同一套底层能力

也就是说,这个项目不是“先做个 GUI,再往里塞逻辑”,而是先把能力沉淀出来,再给它加不同入口。

3. GUI 层负责降低使用门槛,而不是重新实现一套逻辑

这个项目还做了 Wails GUI,而且 README 里已经明确拆成了几个功能页:

  • 设备管理
  • 启动时间测试
  • 生成语音
  • 播放模式
  • 配置设置

这一步的价值,不是“界面更好看”,而是让语音测试从“只有懂命令的人能用”升级到“测试同学可以直接操作”。

很多内部工具最后没落地,不是因为功能不够,而是因为使用门槛太高。
只有 CLI,没有 GUI,往往只能服务少数维护者;
只有 GUI,没有底层能力沉淀,又很容易做成一次性界面工具。

所以这套三层拆分,本质上是在平衡:

  • 可复用性
  • 可维护性
  • 可交付性

四、这个工具最核心的设计点,不是“生成语音”,而是“模板化语音编排”

Voice-QA 最值得保留的一个设计,不是单纯支持多 TTS 引擎,而是它没有把语音输入简化成“一行文本 -> 一段音频”。

它引入了模板序列概念,把一条完整语音拆成有序片段:

  • 静音
  • 唤醒词
  • 主文本
  • 返回指令
  • 再次静音

这个设计对语音测试非常关键,因为真实场景里很多语音指令根本不是“直接一句话就结束”的。

例如 README 里的默认模板,其实已经很接近实际设备测试节奏:

  • 先静音
  • 再播唤醒词
  • 再给设备留反应时间
  • 再播主文本
  • 再插入足够长静音等待系统处理
  • 最后再播返回首页指令

这意味着工具处理的不是“语音素材生成”,而是“可执行语音场景生成”。

这两者的工程价值完全不同。

如果没有这层模板化能力,测试同学就只能手工拼接音频,或者自己在外面再套一层脚本。
而一旦模板能力进入配置层,后续:

  • 场景切换
  • 唤醒词替换
  • 等待时间调优
  • 回收动作调整

都可以通过配置完成,而不必反复改代码。

五、为什么这个工具要同时支持 Edge TTS 和 Piper

README 里已经明确支持两类 TTS:

  • Edge TTS:在线,微软神经网络语音
  • Piper:离线

这个设计背后其实不是“多支持一个引擎看起来更强”,而是解决两类不同场景。

1. 在线 TTS 更适合快速得到自然语音效果

在线引擎的优点通常是:

  • 音质更自然
  • 声音选择更多
  • 适合快速迭代测试素材

对语音识别测试来说,这能更接近日常播报效果。

2. 离线 TTS 更适合受控环境和可重复执行

离线引擎的价值则在于:

  • 不依赖外网
  • 执行更可控
  • 更适合实验室和长期回归

如果工具只支持在线引擎,后面很容易被网络稳定性牵制;
如果只支持离线引擎,又可能在自然度和配置便利性上受限。

所以这不是“多做一个选项”,而是在测试工程里有意区分:

  • 快速生成效率
  • 可重复回归能力

六、播放模式为什么才是这个工具最有测试价值的部分

从 README 来看,播放模式的执行链路已经非常完整:

  1. 启动 logcat
  2. 播放 WAV
  3. 在结束前自动截图
  4. 可选录屏
  5. 停止 logcat
  6. 按日志字段断言
  7. 生成测试报告

更关键的是这部分才是 Voice-QA 和普通 TTS 工具真正拉开差距的地方。

因为它解决了语音测试里最难工程化的那一段:

如何让“设备听了一段音频”变成“有证据、有断言、有报告的一次自动化测试”。

单看这几个动作,好像都不复杂,但真正难的是把它们组织到一条稳定链路里。

例如:

  • 什么时候开始抓日志
  • 什么时候截图最有价值
  • 录屏要不要默认开启
  • 断言应该断 UI 结果还是断日志字段
  • 中途停止时如何保留已完成结果

这些细节处理不好,工具很容易变成“功能很多,但实际排障价值不高”。

从 README 给出的逻辑看,它至少已经在几个关键点上做对了:

  • 有明确断言口径,而不是只生成附件
  • 有截图时机控制,而不是随便截一张
  • 有增量报告保存,避免中断就全部丢失

这三点对真实测试落地都非常关键。

七、为什么会把 ADB、截图、录屏、性能采集都收进同一个工具里

看到这里时,一个很自然的问题是:这是不是把工具做得太大了。
TTS 生成、播放、ADB、性能、GUI,看起来像是几类不同事情。

但从测试链路角度看,它们其实是同一个闭环里的不同节点。

因为一条语音测试要真正完成,通常不只关心“识别对没对”,还会关心:

  • 设备有没有正确响应
  • 语音播放期间页面长什么样
  • 识别失败时日志是什么
  • 这次执行有没有导致应用崩溃
  • 冷启动或响应时间是不是异常

如果这些能力散落在多个工具里,现场往往会变成这样:

  • 一个脚本负责生成音频
  • 一个播放器负责播放
  • 一个人工窗口盯 logcat
  • 另一个工具负责截图
  • 最后手工写结论

这样一来,证据链就非常容易断。

所以把这些能力收进同一个工具,不是为了“做大”,而是为了保证:

  • 一次执行有统一上下文
  • 证据产物天然对齐
  • 配置能集中管理
  • 排障时不用跨多个工具来回找

八、这个项目里最值得关注的一点:它已经开始从“脚本”向“产品化工具”转

更关键的是 Voice-QA 最大的意义,不在于仓库里有多少文件,而在于它已经表现出明显的产品化倾向。

几个信号很明显:

1. 有跨平台分发思路

README 里明确支持:

  • Linux
  • Windows
  • Windows GUI

并且有构建脚本、安装包和内置依赖目录。

这说明它不是只准备在作者本机跑通。

2. 有面向非开发者的 GUI

这意味着它不是“只给自己用的工程脚本”,而是准备服务更广的测试角色。

3. 有安装包和版本化产物

版本号格式和安装器设计,都说明项目已经在往“可交付工具”走,而不是停留在源码层。

4. 有中断可恢复的报告思路

这件事非常工程化。
因为真实测试里最怕的不是失败,而是跑到一半停了以后什么都没留下。

九、这类工具设计里最需要警惕的几个坑

如果后面继续迭代这类语音测试工具,会一直盯这几个方向。

坑 1:把“能播出来”误认为“能测起来”

很多语音工具都能生成音频,但并没有把播放、日志、断言、证据留存组织起来。
这类工具适合做素材,不适合做测试。

坑 2:断言只看表象,不看系统证据

如果只看 UI 最终结果,不看日志,很多误识别、误唤醒、半成功状态都很难被稳定判断。
所以日志断言是很重要的一层。

坑 3:播放成功但现场没留住

没有截图、没有录屏、没有增量报告时,一旦 case 失败,排障成本会陡增。
语音测试尤其需要证据,因为问题往往不是稳定复现的。

坑 4:所有能力都堆在一个入口里,后面没法维护

Voice-QA 现在的模块拆分方向是对的,但后面如果继续膨胀,依然要警惕:

  • GUI 逻辑反向侵入底层
  • 播放模式和性能模式耦合过重
  • 配置项越来越多但缺少层次

工具一旦走向平台化,结构治理会比单点功能更重要。

十、如果继续往下做,这个项目最值得继续强化什么

基于现在仓库里已经体现出来的方向,更关键的是后面最值得继续做的不是“再多几个按钮”,而是下面几件事。

1. 强化场景编排能力

现在已经有模板语音和播放模式,后面其实可以更进一步,把“语音场景”抽象成:

  • 唤醒
  • 指令
  • 等待
  • 回收
  • 断言

这样就不只是批量播音频,而是可以定义测试场景。

2. 把断言从单点日志匹配升级成多证据联合判断

例如同时结合:

  • logcat
  • 截图
  • 视频
  • 应用启动状态
  • 响应时间

这样断言会更稳,也更不容易误判。

3. 增加更适合长期回归的报告结构

比如:

  • 场景维度统计
  • 指令维度通过率
  • 失败类型归类
  • 历史趋势对比

一旦有这层数据,工具价值就会从“执行器”往“质量分析器”走。

4. 进一步收敛配置复杂度

这类工具很容易越做配置越多。
如果配置没有分层,后面新用户会越来越难上手。

结语

Voice-QA 的核心目标,从来不是再做一个“文本转语音工具”,而是把语音指令测试里最难工程化的那一段,真正收束成一套能执行、能留证、能断言、能交付的工具链。

从目前仓库已经体现出来的结构看,它至少已经把几个关键问题串起来了:

  • TTS 生成
  • 模板化语音编排
  • Android 设备播放
  • ADB 日志与截图取证
  • 播放断言
  • GUI 与安装分发

这也是这个项目有意义的地方:

它不是单纯把几个脚本凑到一起,而是在尝试把“语音测试”这件长期依赖人工经验的事情,慢慢沉淀成一套可复用的工程资产。