基于Python+pytest+pywinauto+YAML的仿QQ音乐PC端GUI自动化测试报告
一、摘要
在软件产品快速迭代的背景下,传统人工 GUI 测试存在效率低、易出错、回归成本高等问题。为提升 QQ 音乐 PC 客户端的测试效率与质量保障能力,本项目采用pywinauto实现桌面端自动化操作,以pytest作为测试管理框架,使用YAML实现数据与代码分离,结合Allure生成可视化测试报告,完成对 QQ 音乐播放、搜索、歌单、设置等核心模块的自动化测试。本文从项目背景、技术选型、框架优势、代码实现、用例设计、执行流程与项目总结进行完整阐述,形成一套可直接落地的 Windows 桌面端 GUI 自动化测试方案。
二、项目背景
2.1 行业背景
数字音乐平台迭代速度快,客户端功能复杂,UI 交互密集,传统手工测试在频繁版本更新中压力巨大、耗时长、易漏测,已无法满足高效、稳定、可重复的质量保障需求。因此,引入自动化测试成为提升效率、降低成本、保障发布稳定性的必然选择。
2.2 产品背景
QQ 音乐 PC 客户端包含播放控制、歌曲搜索、歌单管理、音效设置、歌词展示、主题切换等高频功能,模块多、交互深、弹窗复杂,非常适合使用自动化完成回归测试。
2.3 传统测试痛点
重复操作多,人工执行效率低
多版本回归耗时长
易疲劳导致漏测、误测
测试结果不可量化
无法快速集成到持续交付流程
2.4 项目意义
通过自动化脚本替代人工重复劳动,实现一键执行、自动断言、自动报告,大幅提升回归效率与测试覆盖率,同时积累桌面端自动化工程化实践经验。
三、项目整体概述
测试对象:QQ 音乐 Windows PC 客户端
测试模块:播放模块、搜索模块、歌单模块、基础设置模块
技术栈:Python + pywinauto + pytest + YAML + Allure
测试类型:GUI 功能自动化测试、回归测试
四、测试环境与技术栈
4.1软硬件环境

4.2核心依赖清单

五、核心框架与工具优势详解
5.1 pywinauto 框架优势
专为 Windows 桌面应用设计,原生支持 EXE 程序自动化,完美适配 QQ 音乐这类闭源客户端。
支持 UIA 与 Win32 双后端,控件识别率高、稳定性强,可处理复杂多层级控件。
模拟真实用户操作:支持鼠标点击、键盘输入、滑块拖拽、窗口切换、弹窗处理等。
无需应用源码,支持纯黑盒自动化测试,无需依赖浏览器或额外驱动。
轻量简洁、易上手,基于 Python 语法,开发效率远超其他桌面自动化工具。
支持多窗口、动态加载等待,适配大型客户端的复杂交互场景。
5.2 pytest 框架优势
用例编写简洁,兼容 unittest 生态,语法直观易读。
Fixture 机制强大,可实现应用启动、初始化、清理等全局资源管理,大幅提升测试效率。
用例组织灵活,支持按模块、目录、标签分类执行,适配不同测试场景。
插件生态丰富,支持用例排序、失败重跑、日志记录、参数化测试等扩展功能。
自动发现用例,无需复杂配置,一键即可批量执行。
深度集成 Allure,可生成专业、美观、可追溯的可视化测试报告。
适配工程化落地,可轻松接入 CI/CD 流程,实现持续测试
5.3 YAML 测试数据管理优势
格式简洁易读,比 JSON 更轻量化,便于非技术人员维护。
支持层级结构,可存储复杂测试数据,如多组搜索关键词、歌单配置等。
实现数据与代码解耦,修改测试数据无需改动脚本,降低维护成本。
适配多场景测试,一套脚本可支持多组数据执行,提升脚本复用性。
Python 解析便捷,通过 PyYAML 库可快速读取配置,集成成本低。
六、项目架构与目录结构
QQMusic-AutoTest/
├── conftest.py # pytest全局Fixture配置(应用启动/关闭)
├── pytest.ini # pytest运行配置文件
├── requirements.txt # 项目依赖清单
├── Utils/ # 工具类目录
│ └── logUtils.py # 日志工具类
├── data/ # YAML测试数据目录
│ └── search_data.yaml # 搜索模块测试数据
├── tests/ # 测试用例目录
│ ├── test_play.py # 播放模块测试用例
│ ├── test_search.py # 搜索模块测试用例
│ ├── test_playlist.py # 歌单模块测试用例
│ └── test_settings.py # 设置模块测试用例
└── reports/ # Allure测试报告目录
└── source/ # 报告原始数据文件
七、核心代码解析
7.1 conftest.py(pytest 全局 Fixture)
实现 QQ 音乐客户端的启动、窗口等待、异常捕获与自动关闭,使用session级别的 Fixture,确保整个测试过程仅启动一次应用,大幅提升执行效率。
import pytest
from pywinauto import Application
from Utils.logUtils import Logger
class QQmusicApp:
def __init__(self):
self.app_path = r"E:\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("QQ音乐启动成功")
except Exception as e:
self.logger.error(f"启动失败: {e}")
raise
def close(self):
if self.win:
self.win.close()
self.logger.info("QQ音乐已关闭")
@pytest.fixture(scope="session")
def QQMusic_app():
app = QQmusicApp()
app.launch()
yield app
app.close()
7.2 pytest.ini(运行配置)
配置测试用例路径、Allure 报告输出路径,关闭与 pywinauto 冲突的插件,简化测试执行命令。
[pytest]
addopts = -vs -p no:faulthandler --alluredir=./reports/source --clean-alluredir
testpaths = tests
7.3 YAML 测试数据示例
logo:
auto_id: QQMusic.background.head.headLeft.logo
control_type: Text
search:
auto_id: QQMusic.background.head.headRight.searchBox.lineEdit
control_type: Edit
换肤:
auto_id: QQMusic.background.head.headRight.settingBox.skin
control_type: Button
最小化:
auto_id: QQMusic.background.head.headRight.settingBox.min
control_type: Button
导入音乐:
auto_id: QQMusic.background.body.bodyRight.controlBox.play2.addLocal
control_type: Button
本地下载:
auto_id: QQMusic.background.body.bodyLeft.leftBox.myMusic.local.btStyle
control_type: Group
播放全部:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.musicPlayBox.playAll.playAllBtn
control_type: Button
本地音乐文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.PageTittle
control_type: Text
歌曲名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.listLabelBox.musicNameLabel
control_type: Text
歌手名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.listLabelBox.musicSingerLabel
control_type: Text
专辑名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.listLabelBox.musicAlbumLabel
control_type: Text
播放控制:
播放总进度:
auto_id: QQMusic.background.body.bodyRight.progressBar.inLine
control_type: Custom
当前播放进度:
auto_id: QQMusic.background.body.bodyRight.progressBar.outLine
control_type: Custom
歌曲名:
auto_id: QQMusic.background.body.bodyRight.controlBox.play1.musicName
control_type: Text
歌手名:
auto_id: QQMusic.background.body.bodyRight.controlBox.play1.musicSinger
control_type: Text
模式切换:
auto_id: QQMusic.background.body.bodyRight.controlBox.play2.playMode
control_type: Button
播放:
auto_id: QQMusic.background.body.bodyRight.controlBox.play2.play
control_type: Button
歌曲列表:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.localPage.pageMusicList
control_type: List
推荐:
auto_id: QQMusic.background.body.bodyLeft.leftBox.onlineMusic.rec.btStyle
control_type: Group
推荐文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.recText
control_type: Text
今日为你推荐文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.recMusicText
control_type: Text
你的音乐补给文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.supplyMusicText
control_type: Text
今日为你推荐左滚动:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.recMusicBox.leftPage
control_type: Group
今日为你推荐右滚动:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.recMusicBox.rightPage
control_type: Group
今日为你推荐第一项文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.recMusicBox.musicContent.recListUp.RecBoxItem.recBoxItemText
control_type: Text
音乐补给左滚动:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.supplyMuscBox.leftPage
control_type: Group
音乐补给右滚动:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.supplyMuscBox.rightPage
control_type: Group
音乐补给第一排第一项文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.supplyMuscBox.musicContent.recListUp.RecBoxItem.recBoxItemText
control_type: Text
音乐补给第二排第一项文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.recPage.scrollArea.qt_scrollarea_viewport.scrollAreaWidgetContents_2.supplyMuscBox.musicContent.recListDown.RecBoxItem.recBoxItemText
control_type: Text
推荐整个模块:
auto_id: QQMusic.background.body.bodyRight.stackedWidget
control_type: Custom
我喜欢:
auto_id: QQMusic.background.body.bodyLeft.leftBox.myMusic.like.btStyle
control_type: Group
我喜欢文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.PageTittle
control_type: Text
歌曲名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.listLabelBox.musicNameLabel
control_type: Text
歌手名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.listLabelBox.musicSingerLabel
control_type: Text
专辑名称文本:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.listLabelBox.musicAlbumLabel
control_type: Text
播放全部:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.musicPlayBox.playAll.playAllBtn
control_type: Button
歌曲列表:
auto_id: QQMusic.background.body.bodyRight.stackedWidget.likePage.pageMusicList
control_type: List
歌词入口:
auto_id: QQMusic.background.body.bodyRight.controlBox.play3.lrcWord
control_type: Button
歌手标题文本:
auto_id: QQMusic.LrcPage.bgStyle.lrcTop.titleBox.musicSinger
control_type: Text
歌曲名标题文本:
auto_id: QQMusic.LrcPage.bgStyle.lrcTop.titleBox.musicName
control_type: Text
收起歌词:
auto_id: QQMusic.LrcPage.bgStyle.lrcTop.hideBtn
control_type: Button
歌词列表:
auto_id: QQMusic.LrcPage.bgStyle.lrcContent
control_type: Group
7.4 日志打印


import logging
import os.path
import time
class InfoFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.INFO
class ErrFileter(logging.Filter):
def filter(self, record):
return record.levelno == logging.ERROR
class Logger:
logger = None
@classmethod
def getlog(cls):
#创建日志对象
if cls.logger is None:
cls.logger = logging.getLogger(__name__)
#设置日志级别
cls.logger.setLevel(logging.DEBUG)
LOG_PATH = "logs/"
if not os.path.exists(LOG_PATH):
os.mkdir(LOG_PATH)
#2025-06-30.log 2025-06-30_err.log 2025-06-30_info.log
now = time.strftime("%Y-%m-%d")
logname = LOG_PATH + now + ".log"
info_logname = LOG_PATH + now + "_info.log"
err_logname = LOG_PATH + now + "_err.log"
#创建总日志文件处理器
handler = logging.FileHandler(logname,encoding="utf-8")
#创建info日志文件处理器
info_handler = logging.FileHandler(info_logname,encoding="utf-8")
#添加文件过滤
info_handler.addFilter(InfoFilter())
#创建err日志文件处理器
err_handler = logging.FileHandler(err_logname,encoding="utf-8")
err_handler.addFilter(ErrFileter())
#设置日志格式
formatter = logging.Formatter(
"%(asctime)s %(levelname)s [%(name)s] [%(filename)s (%(funcName)s:%(lineno)d)] - %(message)s"
)
handler.setFormatter(formatter)
info_handler.setFormatter(formatter)
err_handler.setFormatter(formatter)
#给logger对象添加handler
cls.logger.addHandler(handler)
cls.logger.addHandler(info_handler)
cls.logger.addHandler(err_handler)
return cls.logger
八、核心测试模块与用例设计
8.1 QQ音乐测试用例

8.2 测试五大模块核心功能

8.2.1 公共模块
测试公共模块核心功能:测试QQ音乐Logo、测试搜索功能、测试换皮肤功能、测试最小化、测试导入音乐、测试随机播放、测试单曲循环、测试顺序播放功能。
测试QQ音乐Logo:界面正常打开,测试通过
def test_logo(self,QQMusic_app):
logo_ele = read_yaml("logo")
logo = QQMusic_app.win.child_window(auto_id=logo_ele['auto_id'], control_type=logo_ele["control_type"])
logo.wait("visible")

测试搜索功能:界面打开,搜索框输入“林俊杰”,测试通过
def test_search(self,QQMusic_app):
edit_ele = read_yaml("search")
edit = QQMusic_app.win.child_window(auto_id=edit_ele['auto_id'], control_type=edit_ele["control_type"])
#唤起输入框
edit.click_input()
#ctrl+a全部选中之后再输入关键词,就不会存在追加的情况
edit.type_keys("^a林俊杰")

测试换皮肤功能:点击换皮肤按钮,出现文本框,测试通过
def test_skin(self,QQMusic_app):
skin_ele = read_yaml("换肤")
skin = QQMusic_app.win.child_window(auto_id=skin_ele['auto_id'],control_type=skin_ele['control_type'])
#点击换肤入口,唤起弹窗
skin.click_input()
#验证弹窗以及文本信息
warning = QQMusic_app.win.child_window(title="温馨提示", control_type="Window")
warning.wait("visible")
warn_text = warning.child_window(control_type="Text").window_text()
assert warn_text == "换肤功能小哥哥正在紧急支持中..."
#关闭温馨提示弹窗
warning.close()
#测试弹窗是否正确关闭
warning.wait_not("visible")

测试最小化功能:点击最小化按钮,页面隐藏,测试通过
def test_window_min(self,QQMusic_app):
window_min_ele = read_yaml("最小化")
window_min_btn = QQMusic_app.win.child_window(auto_id=window_min_ele['auto_id'],
control_type=window_min_ele['control_type'])
#点击最小化按钮
window_min_btn.click_input()
#测试一下QQ音乐窗口是否已经最小化了
assert QQMusic_app.win.is_minimized()
#还原
QQMusic_app.win.restore()

测试导入音乐功能:点击+按钮,添加所有音乐,歌曲导入成功,测试通过
def test_importMusic(self,QQMusic_app):
import_ele = read_yaml("导入音乐")
import_btn = QQMusic_app.win.child_window(auto_id=import_ele['auto_id'],
control_type=import_ele['control_type'])
#点击导入音乐按钮
import_btn.click_input()
#定位添加本地下载音乐窗口
import_win = QQMusic_app.win.child_window(title="添加本地下载音乐",control_type="Window")
import_win.wait("visible")
#选中所有音乐并添加
music_list = import_win.child_window(title="项目视图",control_type="List")
#打开音乐:1)通过“打开”按钮来实现 2)enter键实现
music_list.type_keys("^a")
open_btn = import_win.child_window(title="打开(O)", control_type="Button")
open_btn.click_input()
import_win.wait_not("visible")

测试随机播放功能:点击随机播放按钮,播放三首歌曲,三首歌曲不一样,测试通过
def test_play_random(self,QQMusic_app):
#点击播放全部
local_ele = read_yaml("本地下载")
play_all_ele = local_ele["播放全部"]
play_btn = QQMusic_app.win.child_window(auto_id=play_all_ele['auto_id'],
control_type=play_all_ele['control_type'])
for i in range(1,4):
#点击播放全部,从第一首歌曲开始播放(2002年的第一场雪)
play_btn.click_input()
#将歌曲播放进度拉到尾部
play_ele = read_yaml("播放控制")
process_line_ele = play_ele["播放总进度"]
process_line = QQMusic_app.win.child_window(auto_id=process_line_ele['auto_id'],
control_type=process_line_ele['control_type'])
#获取进度条的尺寸
rec = process_line.rectangle()
x = rec.right - 3
y = math.floor((rec.top + rec.bottom)/2)
#鼠标点击进度条的尾部
mouse.click(coords=(x,y))
#等待切换下一曲
time.sleep(2)
#检查下一步是否为列表中第二首歌曲
# 1)若是,随机播放模式不一定错误
# 2)若不是,随机播放模式正确
music_name_ele = play_ele["歌曲名"]
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(f"第{i}次判断随机播放下一曲正确")
return
else:
self.logger.info(f"第{i}次判断随机播放下一曲错误")
#走到这里还没有返回
raise Exception("随机播放下一曲三次判断均错误")

测试单曲循环功能:点击单曲循环,重复播放一首歌曲三次,测试通过
def test_play_single(self,QQMusic_app):
# 点击播放全部
local_ele = read_yaml("本地下载")
play_all_ele = local_ele["播放全部"]
play_btn = QQMusic_app.win.child_window(auto_id=play_all_ele['auto_id'],
control_type=play_all_ele['control_type'])
#切换模式:随机播放——单曲循环
play_ele = read_yaml("播放控制")
playMode_ele = play_ele["模式切换"]
playMode_btn = QQMusic_app.win.child_window(auto_id=playMode_ele['auto_id'],
control_type=playMode_ele['control_type'])
#点击切换模式按钮
playMode_btn.click_input()
for i in range(1,4):
#点击播放全部按钮
play_btn.click_input()
music_name_ele = play_ele["歌曲名"]
music_name_before = QQMusic_app.win.child_window(auto_id=music_name_ele['auto_id'],
control_type=music_name_ele['control_type']).window_text()
# 将歌曲播放进度拉到尾部
process_line_ele = play_ele["播放总进度"]
process_line = QQMusic_app.win.child_window(auto_id=process_line_ele['auto_id'],
control_type=process_line_ele['control_type'])
# 获取进度条的尺寸
rec = process_line.rectangle()
x = rec.right - 3
y = math.floor((rec.top + rec.bottom) / 2)
# 鼠标点击进度条的尾部
mouse.click(coords=(x, y))
# 等待切换下一曲
time.sleep(2)
#下一首播放的歌曲和前一首歌曲是否相同
# 1)相同,单曲循环模式不一定正确---多次验证
# 2)不相同,单曲循环模式错误
music_name_after = QQMusic_app.win.child_window(auto_id=music_name_ele['auto_id'],
control_type=music_name_ele['control_type']).window_text()
if music_name_before != music_name_after:
self.logger.error(f"单曲循环模式播放下一首歌曲校验错误,before:{music_name_before},after:{music_name_after}")
break
else:
self.logger.info(f"第{i}次校验单曲循环模式播放下一首歌曲正确")
if i == 3:
return
raise Exception(f"单曲循环模式播放下一首歌曲校验错误,before:{music_name_before},after:{music_name_after}")

测试列表循环功能:下拉至列表最后一首歌曲,拖到进度条,播放下一首歌曲,测试通过
def test_play_circle(self,QQMusic_app):
# 切换模式:单曲循环——列表循环
play_ele = read_yaml("播放控制")
music_name_ele = play_ele["歌曲名"]
playMode_ele = play_ele["模式切换"]
playMode_btn = QQMusic_app.win.child_window(auto_id=playMode_ele['auto_id'],
control_type=playMode_ele['control_type'])
# 点击切换模式按钮
playMode_btn.click_input()
for i in range(1,4):
#找到列表中最后一首歌曲
music_list_ele = read_yaml("歌曲列表")
music_list = QQMusic_app.win.child_window(auto_id=music_list_ele['auto_id'],
control_type=music_list_ele['control_type'])
#获取歌曲列表的中间坐标
list_mid = music_list.rectangle().mid_point()
#鼠标下拉列表使其展示最后一首歌曲
mouse.scroll(coords=(list_mid.x,list_mid.y),wheel_dist=-500)
#获取最后一首歌曲——求列表中列表项目数
list_size = music_list.item_count()
#双击最后一首歌曲,使其播放
last_music_mid = music_list.get_item(row=list_size-1).rectangle().mid_point()
mouse.double_click(coords=(last_music_mid.x,last_music_mid.y))
#拉取进度条到尾部,等待播放下一曲
process_line_ele = play_ele["播放总进度"]
process_line = QQMusic_app.win.child_window(auto_id=process_line_ele['auto_id'],
control_type=process_line_ele['control_type'])
# 获取进度条的尺寸
rec = process_line.rectangle()
x = rec.right - 3
y = math.floor((rec.top + rec.bottom) / 2)
# 鼠标点击进度条的尾部
mouse.click(coords=(x, y))
# 等待切换下一曲
time.sleep(2)
# 校验播放的下一首歌曲是否为“2002年的第一场雪(列表的第一首歌曲)”
# 1)是,列表循环校验不一定正确
# 2)不是,列表循环校验错误
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 != "2002年的第一场雪":
self.logger.error(f"列表循环下一曲错误,music_name:{music_name}")
break
else:
self.logger.info(f"第{i}次校验列表循环下一曲正确")
if i == 3:
return
raise Exception(f"列表循环下一曲错误,music_name:{music_name}")

8.2.2 我喜欢模块
测试我喜欢模块功能:测试我喜欢文本、测试我喜欢播放全部、测试我喜欢模块-标记喜欢、测试我喜欢模块—选择歌曲双击播放
测试我喜欢文本
def test_like_text(self,QQMusic_app):
like_ele = read_yaml("我喜欢")
#点击导航栏“我喜欢”进入到我喜欢模块
like_btn = QQMusic_app.win.child_window(auto_id=like_ele["auto_id"],
control_type=like_ele["control_type"])
like_btn.click_input()
#测试“我喜欢”文本
like_text_ele = like_ele["我喜欢文本"]
like_text = QQMusic_app.win.child_window(auto_id=like_text_ele["auto_id"],
control_type=like_text_ele["control_type"]).window_text()
assert like_text == "我喜欢"
#测试“歌曲名称”文本
songname_text_ele = like_ele["歌曲名称文本"]
songname_text = QQMusic_app.win.child_window(auto_id=songname_text_ele["auto_id"],
control_type=songname_text_ele["control_type"]).window_text()
assert songname_text == "歌曲名称"
#测试“歌手名称”文本
singername_text_ele = like_ele["歌手名称文本"]
singername_text = QQMusic_app.win.child_window(auto_id=singername_text_ele["auto_id"],
control_type=singername_text_ele["control_type"]).window_text()
assert singername_text == "歌手名称"
#测试“专辑名称”文本
albumrname_text_ele = like_ele["专辑名称文本"]
albumrname_text = QQMusic_app.win.child_window(auto_id=albumrname_text_ele["auto_id"],
control_type=albumrname_text_ele["control_type"]).window_text()
assert albumrname_text == "专辑名称"

测试我喜欢播放全部
def test_like_playAll(self,QQMusic_app):
playAll_ele = read_yaml("我喜欢")["播放全部"]
playAll_btn = QQMusic_app.win.child_window(auto_id=playAll_ele["auto_id"],
control_type=playAll_ele["control_type"])
#点击播放全部按钮
playAll_btn.click_input()
#获取播放进度
process_line_ele = read_yaml("播放控制")["当前播放进度"]
process_line_before = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_before = process_line_before.rectangle().right
#等待两秒
time.sleep(2)
#获取播放进度
process_line_after = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_after = process_line_after.rectangle().right
#比较前后两次进度变化,有变化则说明按钮没有问题
assert process_line_len_before != process_line_len_after
![]()
测试我喜欢模块歌曲双击播放
def test_like_playSingle(self,QQMusic_app):
music_list_ele = read_yaml("我喜欢")["歌曲列表"]
music_list = QQMusic_app.win.child_window(auto_id=music_list_ele["auto_id"],
control_type=music_list_ele["control_type"])
#获取歌曲列表歌曲数量
list_size = music_list.item_count()
if list_size <= 0:
assert 0,"歌曲列表为空"
#选择第一首歌曲双击播放
point = music_list.get_item(row=0).rectangle().mid_point()
mouse.double_click(coords=(point.x,point.y))
# 获取播放进度
process_line_ele = read_yaml("播放控制")["当前播放进度"]
process_line_before = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_before = process_line_before.rectangle().right
# 等待两秒
time.sleep(2)
# 获取播放进度
process_line_after = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_after = process_line_after.rectangle().right
# 比较前后两次进度变化,有变化则说明双击歌曲播放没有问题
assert process_line_len_before != process_line_len_after
'''
![]()
测试我喜欢模块标记喜欢
def test_mark_unLike(self,QQMusic_app):
#获取歌曲列表中歌曲的数量
music_list_ele = read_yaml("我喜欢")["歌曲列表"]
music_list_before = QQMusic_app.win.child_window(auto_id=music_list_ele["auto_id"],
control_type=music_list_ele["control_type"])
# 获取歌曲列表歌曲数量
list_size_before = music_list_before.item_count()
#取消标记喜欢
rec = music_list_before.get_item(row=0).rectangle()
y = math.floor((rec.top + rec.bottom)/2)
x = rec.left + 22
mouse.click(coords=(x,y))
#获取歌曲列表中歌曲的数量
music_list_after = QQMusic_app.win.child_window(auto_id=music_list_ele["auto_id"],
control_type=music_list_ele["control_type"])
# 获取歌曲列表歌曲数量
list_size_after = music_list_after.item_count()
#测试取消标记喜欢是否成功
assert list_size_after + 1 == list_size_before
![]()
8.2.3 本地下载模块
本地下载模块:测试本地下载模块—文本、测试本地下载播放全部功能、测试本地下载模块选择歌曲并双击播放、测试将歌曲标记喜欢
测试本地下载模块—文本
def test_local_text(self,QQMusic_app):
local_ele = read_yaml("本地下载")
#点击导航栏-本地下载,进入本地下载页面
local = QQMusic_app.win.child_window(auto_id=local_ele["auto_id"],
control_type=local_ele["control_type"])
local.click_input()
#测试“本地音乐文本"
local_text_ele = local_ele["本地音乐文本"]
local_text = QQMusic_app.win.child_window(auto_id=local_text_ele["auto_id"],
control_type=local_text_ele["control_type"]).window_text()
assert local_text == "本地音乐"
#测试“歌曲名称文本"
songname_text_ele = local_ele["歌曲名称文本"]
songname_text = QQMusic_app.win.child_window(auto_id=songname_text_ele["auto_id"],
control_type=songname_text_ele["control_type"]).window_text()
assert songname_text == "歌曲名称"
#测试“歌手名称文本"
singername_text_ele = local_ele["歌手名称文本"]
singername_text = QQMusic_app.win.child_window(auto_id=singername_text_ele["auto_id"],
control_type=singername_text_ele["control_type"]).window_text()
assert singername_text == "歌手名称"
#测试“专辑名称文本"
Albumname_text_ele = local_ele["专辑名称文本"]
Albumrname_text = QQMusic_app.win.child_window(auto_id=Albumname_text_ele["auto_id"],
control_type=Albumname_text_ele["control_type"]).window_text()
assert Albumrname_text == "专辑名称"
![]()
测试本地下载播放全部功能
def test_local_playAll(self,QQMusic_app):
local_ele = read_yaml("本地下载")
playAll_ele = local_ele["播放全部"]
playAll_btn = QQMusic_app.win.child_window(auto_id=playAll_ele["auto_id"],
control_type=playAll_ele["control_type"])
#点击播放全部按钮
playAll_btn.click_input()
#获取播放进度
process_line_ele = read_yaml("播放控制")["当前播放进度"]
process_line_before = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_before = process_line_before.rectangle().right
#等待两秒
time.sleep(2)
#获取播放进度
process_line_after = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_after = process_line_after.rectangle().right
#测试前后两个进度是否存在差别
assert process_line_len_before != process_line_len_after
![]()
测试本地下载模块选择歌曲并双击播放
def test_local_playSingle(self,QQMusic_app):
music_list_ele = read_yaml("歌曲列表")
music_list = QQMusic_app.win.child_window(auto_id=music_list_ele["auto_id"],
control_type=music_list_ele["control_type"])
#将歌曲列表还原到最上方——————公共模块测试循环播放找最后一首歌曲将列表拉到了最下面
point = music_list.rectangle().mid_point()
mouse.scroll(coords=(point.x,point.y),wheel_dist=500)
#获取歌曲列表中歌曲数量
if music_list.item_count() <= 0:
assert 0,"歌曲列表为空"
#选择一首歌曲并双击播放
point = music_list.get_item(row=0).rectangle().mid_point()
mouse.double_click(coords=(point.x,point.y))
# 获取播放进度
process_line_ele = read_yaml("播放控制")["当前播放进度"]
process_line_before = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_before = process_line_before.rectangle().right
# 等待两秒
time.sleep(2)
# 获取播放进度
process_line_after = QQMusic_app.win.child_window(auto_id=process_line_ele["auto_id"],
control_type=process_line_ele["control_type"])
process_line_len_after = process_line_after.rectangle().right
# 测试前后两个进度是否存在差别
assert process_line_len_before != process_line_len_after
![]()
测试将歌曲标记喜欢
def test_mark_like(self,QQMusic_app):
#获取歌曲列表中歌曲数量
music_list_ele = read_yaml("歌曲列表")
music_list = QQMusic_app.win.child_window(auto_id=music_list_ele["auto_id"],
control_type=music_list_ele["control_type"])
list_size = music_list.item_count()
#对每一首歌曲标记喜欢
for i in range(0,list_size):
if i != 0 and i % 6 == 0:
#6及以后的歌曲在标记喜欢之前需要先向下滑动,使其显示出来
point = music_list.rectangle().mid_point()
mouse.scroll(coords=(point.x,point.y),wheel_dist=-500)
rec = music_list.get_item(row=i).rectangle()
#获取爱心的中间位置(x,y)
y = math.floor((rec.top + rec.bottom)/2)
x = rec.left + 22
mouse.click(coords=(x,y))
![]()
8.2.4 推荐页面模块
推荐模块:测试—推荐页面的文本、测试今日为你推荐滚动区域——左滚动、测试今日为你推荐滚动区域—右滚动、测试你的音乐补给滚动区域—左滚动、测试你的音乐补给滚动区域—右滚动
测试—推荐页面的文本:推荐,今日为你推荐,你的音乐补给
def test_rec_text(self,QQMusic_app):
#点击左侧的推荐导航入口,进入到推荐页面
rec_ele = read_yaml("推荐")
rec_btn = QQMusic_app.win.child_window(auto_id=rec_ele["auto_id"],
control_type=rec_ele["control_type"])
rec_btn.click_input()
#获取“推荐”文本控件
rec_text_ele = rec_ele["推荐文本"]
#获取“今日为你推荐”文本控件
rec_foru_text_ele = rec_ele["今日为你推荐文本"]
#获取“你的音乐补给”文本控件
rec_supply_text_ele = rec_ele["你的音乐补给文本"]
#校验“推荐”文本控件
rec_text = QQMusic_app.win.child_window(auto_id=rec_text_ele["auto_id"],
control_type=rec_text_ele["control_type"])
assert rec_text.window_text() == "推荐"
# 校验“今日为你推荐”文本控件
rec_foru_text = QQMusic_app.win.child_window(auto_id=rec_foru_text_ele["auto_id"],
control_type=rec_foru_text_ele["control_type"])
assert rec_foru_text.window_text() == "今日为你推荐"
#校验“你的音乐补给”文本控件
rec_supply_text = QQMusic_app.win.child_window(auto_id=rec_supply_text_ele["auto_id"],
control_type=rec_supply_text_ele["control_type"])
assert rec_supply_text.window_text() == "你的音乐补给"
![]()
测试今日为你推荐滚动区域——左滚动
def test_recforu_scroll_left(self,QQMusic_app):
rec_ele = read_yaml("推荐")
item_text_ele = rec_ele["今日为你推荐第一项文本"]
item_text_before = QQMusic_app.win.child_window(auto_id=item_text_ele["auto_id"],
control_type=item_text_ele["control_type"],
found_index=0).window_text()
scroll_left_ele = rec_ele["今日为你推荐左滚动"]
scroll_left = QQMusic_app.win.child_window(auto_id=scroll_left_ele["auto_id"],
control_type=scroll_left_ele["control_type"])
#点击左滚动按钮
scroll_left.click_input()
#获取推荐项的名称,进行前后对比校验
item_text_after = QQMusic_app.win.child_window(auto_id=item_text_ele["auto_id"],
control_type=item_text_ele["control_type"],
found_index=0).window_text()
assert item_text_before != item_text_after
![]()
测试今日为你推荐滚动区域—右滚动
def test_recforu_scroll_right(self, QQMusic_app):
rec_ele = read_yaml("推荐")
item_text_ele = rec_ele["今日为你推荐第一项文本"]
item_text_before = QQMusic_app.win.child_window(auto_id=item_text_ele["auto_id"],
control_type=item_text_ele["control_type"],
found_index=0).window_text()
scroll_right_ele = rec_ele["今日为你推荐右滚动"]
scroll_right = QQMusic_app.win.child_window(auto_id=scroll_right_ele["auto_id"],
control_type=scroll_right_ele["control_type"])
# 点击右滚动按钮
scroll_right.click_input()
# 获取推荐项的名称,进行前后对比校验
item_text_after = QQMusic_app.win.child_window(auto_id=item_text_ele["auto_id"],
control_type=item_text_ele["control_type"],
found_index=0).window_text()
assert item_text_before != item_text_after
![]()
测试你的音乐补给滚动区域—左滚动
def test_supply_scroll_left(self,QQMusic_app):
rec_ele = read_yaml("推荐")
all_rec_area_ele = rec_ele["推荐整个模块"]
all_rec_area = QQMusic_app.win.child_window(auto_id=all_rec_area_ele["auto_id"],
control_type=all_rec_area_ele["control_type"])
#找推荐整个模块的中间坐标
point = all_rec_area.rectangle().mid_point()
#在推荐模块鼠标下拉,展示完整的为你推荐区域
mouse.scroll(coords=(point.x,point.y),wheel_dist=-500)
#点击左滚动按钮
scroll_left_ele = rec_ele["音乐补给左滚动"]
one_one_ele = rec_ele["音乐补给第一排第一项文本"]
two_one_ele = rec_ele["音乐补给第二排第一项文本"]
one_one_text_before = QQMusic_app.win.child_window(auto_id=one_one_ele["auto_id"],
control_type=one_one_ele["control_type"],
found_index=0).window_text()
two_one_text_before = QQMusic_app.win.child_window(auto_id=two_one_ele["auto_id"],
control_type=two_one_ele["control_type"],
found_index=0).window_text()
scroll_left_btn = QQMusic_app.win.child_window(auto_id=scroll_left_ele["auto_id"],
control_type=scroll_left_ele["control_type"])
scroll_left_btn.click_input()
#左滚动结果的校验--项目名称是否变化
one_one_text_after = QQMusic_app.win.child_window(auto_id=one_one_ele["auto_id"],
control_type=one_one_ele["control_type"],
found_index=0).window_text()
two_one_text_after = QQMusic_app.win.child_window(auto_id=two_one_ele["auto_id"],
control_type=two_one_ele["control_type"],
found_index=0).window_text()
assert one_one_text_after != one_one_text_before
assert two_one_text_after != two_one_text_before
![]()
测试你的音乐补给滚动区域—右滚动
def test_supply_scroll_right(self, QQMusic_app):
rec_ele = read_yaml("推荐")
all_rec_area_ele = rec_ele["推荐整个模块"]
all_rec_area = QQMusic_app.win.child_window(auto_id=all_rec_area_ele["auto_id"],
control_type=all_rec_area_ele["control_type"])
# 找推荐整个模块的中间坐标
point = all_rec_area.rectangle().mid_point()
# 在推荐模块鼠标下拉,展示完整的为你推荐区域
mouse.scroll(coords=(point.x, point.y), wheel_dist=-500)
# 点击右滚动按钮
scroll_left_ele = rec_ele["音乐补给右滚动"]
one_one_ele = rec_ele["音乐补给第一排第一项文本"]
two_one_ele = rec_ele["音乐补给第二排第一项文本"]
one_one_text_before = QQMusic_app.win.child_window(auto_id=one_one_ele["auto_id"],
control_type=one_one_ele["control_type"],
found_index=0).window_text()
two_one_text_before = QQMusic_app.win.child_window(auto_id=two_one_ele["auto_id"],
control_type=two_one_ele["control_type"],
found_index=0).window_text()
scroll_right_btn = QQMusic_app.win.child_window(auto_id=scroll_left_ele["auto_id"],
control_type=scroll_left_ele["control_type"])
scroll_right_btn.click_input()
# 左滚动结果的校验--项目名称是否变化
one_one_text_after = QQMusic_app.win.child_window(auto_id=one_one_ele["auto_id"],
control_type=one_one_ele["control_type"],
found_index=0).window_text()
two_one_text_after = QQMusic_app.win.child_window(auto_id=two_one_ele["auto_id"],
control_type=two_one_ele["control_type"],
found_index=0).window_text()
assert one_one_text_after != one_one_text_before
assert two_one_text_after != two_one_text_before
![]()
8.2.5 歌词页面模块
歌词页面模块:测试歌词页面的标题、歌手名、歌曲名、测试歌词页面--测试歌词
测试歌词页面的标题、歌手名、歌曲名
def test_titie_text(self,QQMusic_app):
song_word_page_ele = read_yaml("歌词入口")
song_word_btn = QQMusic_app.win.child_window(auto_id=song_word_page_ele["auto_id"],
control_type=song_word_page_ele["control_type"])
#点击页面的歌词入口,进入到歌词页面
song_word_btn.click_input()
#获取歌手名文本
singer_text_ele = song_word_page_ele["歌手标题文本"]
singer_text = QQMusic_app.win.child_window(auto_id=singer_text_ele["auto_id"],
control_type=singer_text_ele["control_type"]).window_text()
#校验歌手名文本
assert singer_text == "刀郎"
#获取歌曲名文本
song_text_ele = song_word_page_ele["歌曲名标题文本"]
song_text = QQMusic_app.win.child_window(auto_id=song_text_ele["auto_id"],
control_type=song_text_ele["control_type"]).window_text()
#校验歌曲名文本
assert song_text == "2002年的第一场雪"
测试歌词页面--测试歌词
def test_songwords(self,QQMusic_app):
likepage_ele = read_yaml("我喜欢")
wordspage_ele = read_yaml("歌词入口")
#收起歌词页面
hide_word_page_ele = wordspage_ele["收起歌词"]
hide_word_page_btn = QQMusic_app.win.child_window(auto_id=hide_word_page_ele["auto_id"],
control_type=hide_word_page_ele["control_type"])
hide_word_page_btn.click_input()
# 为后面的测试用例做准备————点击播放歌曲并立即暂停
playAll_ele = likepage_ele["播放全部"]
playAll_btn = QQMusic_app.win.child_window(auto_id=playAll_ele["auto_id"],
control_type=playAll_ele["control_type"])
playAll_btn.click_input()
# 立即暂停播放
play_ele = read_yaml("播放控制")["播放"]
play_btn = QQMusic_app.win.child_window(auto_id=play_ele["auto_id"],
control_type=play_ele["control_type"])
play_btn.click_input()
# 获取当前正在播放的歌手名和歌曲名
play_control_ele = read_yaml("播放控制")
singer_name_ele = play_control_ele["歌手名"]
song_name_ele = play_control_ele["歌曲名"]
singer_name = QQMusic_app.win.child_window(auto_id=singer_name_ele["auto_id"],
control_type=singer_name_ele["control_type"]).window_text()
song_name = QQMusic_app.win.child_window(auto_id=song_name_ele["auto_id"],
control_type=song_name_ele["control_type"]).window_text()
# songwordsText = f"{song_name} - {singer_name}"
#进入歌词页面
song_word_page_ele = read_yaml("歌词入口")
song_word_btn = QQMusic_app.win.child_window(auto_id=song_word_page_ele["auto_id"],
control_type=song_word_page_ele["control_type"])
# 点击页面的歌词入口,进入到歌词页面
song_word_btn.click_input()
#测试歌词
words_list_ele = wordspage_ele["歌词列表"]
words_list = QQMusic_app.win.child_window(auto_id=words_list_ele["auto_id"],
control_type=words_list_ele["control_type"])
for i in words_list.children():
if i.window_text() in song_name or i.window_text() in singer_name:
return
self.logger.info(f"获取到的歌词:{i.window_text()}")
#始终没有匹配上
raise Exception(f"歌词匹配失败,song_name:{song_name},singer_name{singer_name}")
![]()
8.3自动化测试用例全部通过实例

九、测试报告
allure generate .\reports\source\ -o .\reports\html 生成测试报告

点击进入谷歌浏览器,生成测试报告

十、完整项目地址
https://gitee.com/tang-hanjiang/class104/tree/85fe4ffe629f7e3f94ac7d9c7d911a57a81c8c7c/QQmusic_test
更多推荐
所有评论(0)