一、摘要

在软件产品快速迭代的背景下,传统人工 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

更多推荐