ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python实现QQ音乐GUI自动化测试实践

Python实现QQ音乐GUI自动化测试实践

1. 项目概述

最近接手了一个QQ音乐客户端的GUI自动化测试项目,目标是实现对QQ音乐核心功能的回归测试验证。作为一个音乐类应用,QQ音乐的功能模块相对复杂,包括播放控制、搜索、歌单管理等多个核心功能点。传统的功能测试需要大量重复操作,而通过自动化测试可以显著提升测试效率和覆盖率。

这个项目采用了Python技术栈,主要使用pywinauto进行Windows GUI自动化操作,pytest作为测试框架,YAML文件管理测试元素定位信息。整个项目从需求分析到用例设计再到代码实现,完整覆盖了自动化测试的全流程。

2. 技术选型与架构设计

2.1 核心工具选型

在技术选型阶段,我们主要考虑了以下几个关键因素:

  1. 被测应用特性:QQ音乐是Windows桌面应用,采用WPF技术开发
  2. 测试需求:需要模拟真实用户操作,验证UI交互和功能逻辑
  3. 团队技术栈:团队熟悉Python生态

基于这些考虑,我们选择了以下技术组合:

  • pywinauto:专门针对Windows GUI应用的自动化测试库,支持Win32和WPF应用
  • pytest:Python生态中最流行的测试框架,提供丰富的插件和扩展能力
  • YAML:用于管理测试元素定位信息,比JSON更易读和编写

提示:选择pywinauto而不是Selenium的原因是,Selenium主要针对Web应用,而pywinauto专门为Windows桌面应用设计,提供了更丰富的控件操作API。

2.2 项目架构设计

整个项目的架构分为以下几个层次:

  1. 测试用例层:包含各个功能模块的测试用例
  2. 工具封装层:封装了日志、YAML读取等公共工具
  3. 应用控制层:通过pywinauto控制QQ音乐应用
  4. 元素管理层:使用YAML文件管理所有UI元素定位信息

这种分层设计使得各模块职责清晰,便于维护和扩展。当UI发生变化时,只需修改YAML文件中的元素定位信息,而不需要改动测试用例代码。

3. 核心实现细节

3.1 应用启动与控制

项目使用pywinauto的Application类来控制QQ音乐应用。在conftest.py中,我们定义了一个QQmusicApp类来封装应用的生命周期管理:

class QQmusicApp: def __init__(self): self.app_path = r"E:\qqmusic\qqmusic\QQMusic.exe" self.logger = Logger.getlog() self.app = None self.win = None def launch(self): try: self.app = Application(backend="uia").start(self.app_path) self.win = self.app.window(title="QQMusic") self.win.wait("visible") self.logger.info("应用程序启动成功!") except Exception as e: self.logger.error(f"应用程序启动失败:{e}") def close(self): if self.win: self.win.close()

这里有几个关键点需要注意:

  1. backend="uia"指定使用UI Automation API,这是WPF应用推荐的backend
  2. window(title="QQMusic")通过窗口标题定位主窗口
  3. wait("visible")确保窗口完全加载后再进行后续操作

3.2 元素定位与管理

项目中所有UI元素的定位信息都存储在YAML文件中,通过yamlUtils.py提供的工具类进行读取。例如elements.yml中定义搜索框的方式:

search: auto_id: "SearchEdit" control_type: "Edit"

在测试代码中通过以下方式使用:

edit_ele = read_yaml("search") edit = QQMusic_app.win.child_window( auto_id=edit_ele['auto_id'], control_type=edit_ele["control_type"] )

这种设计的好处是:

  1. 元素定位信息集中管理,便于维护
  2. 测试代码更简洁,只关注业务逻辑
  3. 当UI变化时只需修改YAML文件

3.3 日志系统设计

项目中设计了一个灵活的日志系统,能够将不同级别的日志输出到不同文件:

class Logger: logger = None @classmethod def getlog(cls): if cls.logger is None: cls.logger = logging.getLogger(__name__) cls.logger.setLevel(logging.DEBUG) # 创建总日志、info日志和error日志处理器 handler = logging.FileHandler("logs/app.log", encoding="utf-8") info_handler = logging.FileHandler("logs/info.log", encoding="utf-8") err_handler = logging.FileHandler("logs/error.log", encoding="utf-8") # 设置日志格式 formatter = logging.Formatter( "%(asctime)s %(levelname)s [%(name)s] [%(filename)s (%(funcName)s:%(lineno)d)] - %(message)s" ) # 添加处理器 cls.logger.addHandler(handler) cls.logger.addHandler(info_handler) cls.logger.addHandler(err_handler) return cls.logger

日志系统特点:

  1. 按日志级别分离存储,便于问题排查
  2. 详细的日志格式,包含时间、级别、文件名、行号等信息
  3. 线程安全的单例设计,确保整个项目使用同一个日志实例

4. 测试用例设计与实现

4.1 播放控制测试

播放控制是音乐应用的核心功能,我们设计了多种测试场景:

  1. 随机播放测试:验证歌曲播放结束后是否随机切换到下一首
  2. 单曲循环测试:验证歌曲播放结束后是否重复播放当前歌曲
  3. 列表循环测试:验证播放完最后一首后是否回到列表第一首

以随机播放测试为例,关键实现如下:

def test_play_random(self, QQMusic_app): # 点击播放全部按钮 play_btn.click_input() # 将歌曲播放进度拉到尾部 rec = process_line.rectangle() x = rec.right - 3 y = math.floor((rec.top + rec.bottom) / 2) mouse.click(coords=(x, y)) # 检查下一首歌曲 music_name = QQMusic_app.win.child_window( auto_id=music_name_ele['auto_id'], control_type=music_name_ele['control_type'] ).window_text() if music_name != "Andy阿杜": self.logger.info("随机播放下一曲正确") else: self.logger.error("随机播放下一曲错误")

测试要点:

  1. 使用鼠标点击进度条最右侧,模拟播放完成
  2. 验证下一首歌曲是否随机切换
  3. 多次执行测试以提高可靠性

4.2 搜索功能测试

搜索功能测试主要验证:

  1. 搜索框能否正常输入
  2. 搜索结果是否正确显示
  3. 智能推荐是否正常工作

关键实现代码:

def test_search(self, QQMusic_app): edit = QQMusic_app.win.child_window( auto_id=edit_ele['auto_id'], control_type=edit_ele["control_type"] ) # 唤起输入框 edit.click_input() # 全选后输入关键词 edit.type_keys("^a邓紫棋") # 验证搜索结果 result = get_search_result() assert "邓紫棋" in result

测试技巧:

  1. 使用^a快捷键全选现有文本,避免文本追加
  2. 验证搜索结果包含关键词
  3. 可以测试特殊字符和长文本输入

4.3 歌单管理测试

歌单管理测试包括:

  1. 创建新歌单
  2. 添加歌曲到歌单
  3. 删除歌单
  4. 歌单排序

这部分测试相对复杂,需要模拟完整的用户操作流程:

def test_playlist_management(self, QQMusic_app): # 创建新歌单 create_playlist_btn.click_input() new_playlist_name = "测试歌单_" + str(int(time.time())) name_input.type_keys(new_playlist_name) confirm_btn.click_input() # 验证歌单创建成功 playlist_list = get_playlist_list() assert new_playlist_name in playlist_list # 添加歌曲到歌单 add_songs_to_playlist(new_playlist_name) # 删除歌单 delete_playlist(new_playlist_name) playlist_list = get_playlist_list() assert new_playlist_name not in playlist_list

注意事项:

  1. 使用时间戳确保歌单名称唯一
  2. 每个操作后都需要验证结果
  3. 清理测试数据,避免影响后续测试

5. 常见问题与解决方案

5.1 元素定位问题

在UI自动化测试中,元素定位是最常见的问题之一。我们总结了以下解决方案:

  1. 使用UI Spy工具:pywinauto自带的inspect.exe可以查看控件属性
  2. print_control_identifiers():打印窗口中所有控件信息
  3. 多属性组合定位:同时使用auto_id、control_type、title等属性提高定位准确性

例如,当简单定位方式失效时:

# 常规定位方式失效时 window.child_window( auto_id="elementId", control_type="Button", found_index=2 )

5.2 异步加载等待

音乐应用中很多操作是异步的,需要合理等待:

  1. 显式等待:使用wait()方法等待元素出现
  2. 隐式等待:设置全局超时时间
  3. 自定义等待:对于特殊场景可以轮询检查

最佳实践:

# 等待窗口可见 window.wait("visible", timeout=10) # 等待元素可点击 element.wait("enabled") # 自定义等待条件 def wait_for_condition(condition, timeout=10): end_time = time.time() + timeout while time.time() < end_time: if condition(): return True time.sleep(0.5) return False

5.3 测试数据管理

自动化测试需要妥善管理测试数据:

  1. 测试前准备:确保测试环境干净
  2. 测试中隔离:不同测试用例使用独立数据
  3. 测试后清理:删除测试产生的数据

实现示例:

@pytest.fixture def test_playlist(QQMusic_app): # 测试前创建歌单 playlist_name = create_test_playlist() yield playlist_name # 测试后删除歌单 delete_playlist(playlist_name)

6. 项目优化方向

6.1 测试报告生成

目前项目使用pytest自带的输出,可以考虑集成Allure等报告工具,生成更美观的测试报告:

  1. 添加测试步骤描述
  2. 截图关键操作节点
  3. 添加测试用例标签和优先级

6.2 持续集成

将自动化测试集成到CI/CD流程中:

  1. 代码提交触发测试
  2. 定时执行回归测试
  3. 测试失败自动通知

6.3 性能监控

除了功能测试,可以增加性能监控:

  1. 启动时间测试
  2. 内存占用监控
  3. CPU使用率分析

7. 项目心得

在实际开发这个自动化测试项目过程中,我积累了一些宝贵的经验:

  1. 元素定位要稳健:不要依赖容易变化的属性,如坐标位置
  2. 测试用例要独立:每个测试用例应该能够独立运行,不依赖其他用例的状态
  3. 日志要详细:好的日志系统能极大提升调试效率
  4. 异常处理要全面:充分考虑各种异常情况,增强测试健壮性

一个特别实用的技巧是:对于复杂的UI操作,可以先用脚本录制工具记录操作流程,然后再转化为自动化测试代码,这样能显著提高开发效率。

返回列表