
1. 项目概述当游戏测试遇上Playwright最近在做一个HTML5小游戏的测试项目团队里有人提出能不能把那些重复的点击、滑动验证给自动化掉。我一想这不正是Playwright的拿手好戏吗虽然Playwright在Web应用自动化测试领域已经名声在外但用它来测游戏尤其是基于Canvas的HTML5游戏很多人可能还没怎么尝试过。这活儿本质上就是用代码模拟一个真实玩家去完成游戏里的各种操作然后验证游戏的反应是否符合预期。它特别适合用来做回归测试——每次游戏更新后快速跑一遍核心玩法确保没把老功能搞坏也适合做兼容性测试看看游戏在不同浏览器内核Chromium, Firefox, WebKit下表现是否一致。这个思路的核心价值在于解放人力。想象一下一个简单的“点击开始-躲避障碍物-到达终点”的跑酷游戏手动测一遍可能要5分钟但一旦写成自动化脚本可能10秒就跑完了而且可以24小时不间断、零误差地重复执行。这对于追求快速迭代的休闲游戏或H5小游戏项目来说效率提升是巨大的。当然它不能完全替代探索性测试和用户体验测试但对于保障基础功能稳定绝对是一把利器。接下来我就结合一个模拟的“太空射击”HTML5游戏测试场景拆解一下如何用Playwright Python搭建这套自动化验证框架。2. 核心思路与框架选型2.1 为什么是Playwright而不是Selenium或Puppeteer选择Playwright作为游戏自动化测试的工具是经过一番考量的。首先Selenium虽然是元老但对现代Web技术的支持有时会力不从心特别是在处理复杂的Canvas渲染和WebGL游戏时定位元素和模拟交互可能会遇到障碍。它的执行速度相对较慢对于需要快速反馈的游戏测试来说是个短板。而Puppeteer是Chrome的亲儿子对Chromium系浏览器的控制力一流但它的主要短板在于只支持JavaScript/TypeScript。对于已经习惯用Python进行测试开发或者团队技术栈以Python为主的团队来说引入Node.js环境会增加复杂度。Playwright则完美地结合了二者的优点并做了增强多语言支持官方支持Python、Java、.NET和Node.jsPython API非常友好这让Python测试团队可以无缝接入。多浏览器引擎原生支持Chromium、Firefox和WebKit这意味着你可以用同一套脚本测试游戏在Chrome、Firefox和Safari上的表现对于HTML5游戏的跨浏览器兼容性测试至关重要。自动等待这是Playwright的一大杀器。它内置了智能等待机制在执行操作如点击、填充前会自动等待元素可操作。在游戏测试中页面元素如游戏加载界面、按钮的加载和渲染时间可能不稳定这个特性可以极大减少编写显式等待time.sleep的代码让脚本更健壮。强大的网络与资源控制可以拦截和修改网络请求模拟弱网环境这对于测试游戏资源加载、断线重连等场景非常有用。对Canvas和WebGL的支持虽然不能直接“看到”Canvas里的具体图形但Playwright可以通过截图对比、像素检测以及模拟全局坐标点击等方式与Canvas内容交互这为游戏测试提供了可能。注意Playwright并非为“识别游戏画面内容”而设计。它无法直接读取Canvas里绘制的“敌人飞机”或“金币”。它的交互是基于DOM元素和页面坐标的。因此我们的测试设计需要围绕可交互的DOM元素如按钮、输入框或已知的固定坐标区域展开。2.2 测试金字塔在游戏测试中的应用我们不能指望用Playwright自动化一切。合理的测试策略应该是“测试金字塔”模型在游戏领域的应用底层大量单元测试。测试游戏的核心逻辑如碰撞检测算法、分数计算、角色状态机等。这部分通常由游戏开发人员用Jest、Pytest等框架在代码层面完成。中层适量集成/接口测试。测试游戏与后端服务器的交互如登录、存档、排行榜数据获取等。可以用Python的Requests库或Playwright的API请求功能来完成。顶层少量UI自动化测试即本项目重点。测试完整的用户流程如从启动游戏、完成一局对战到查看结果。这部分脚本运行慢、维护成本高但价值在于验证端到端的用户体验。我们的Playwright脚本就位于金字塔的顶端。目标不是覆盖所有边界情况而是保障核心用户旅程Core User Journey的畅通。例如对于一个塔防游戏核心旅程可能就是加载游戏 - 选择关卡 - 放置防御塔 - 击败所有敌人 - 进入下一关。2.3 项目结构与技术栈规划一个清晰的项目结构有助于长期维护。我建议的目录结构如下game_auto_test/ ├── requirements.txt # Python依赖包列表 ├── conftest.py # Pytest全局配置和Fixture ├── pages/ # 页面对象模型Page Object Model │ ├── __init__.py │ ├── base_page.py # 基础页面类 │ └── game_home_page.py # 游戏首页类 ├── tests/ # 测试用例 │ ├── __init__.py │ ├── test_game_start.py # 测试游戏启动 │ └── test_game_play.py # 测试游戏过程 ├── utils/ # 工具函数 │ ├── __init__.py │ ├── screenshot_helper.py # 截图工具 │ └── coordinate_helper.py # 坐标计算工具 ├── assets/ # 测试资源 │ ├── expected_screenshots/ # 预期截图 │ └── test_data/ # 测试数据 └── reports/ # 测试报告自动生成 └── allure-results/ # Allure报告数据核心技术栈Python 3.8: 主编程语言。Playwright for Python: 自动化测试框架。Pytest: 测试运行和用例管理框架与Playwright集成良好。Allure-pytest: 生成美观的HTML测试报告。PixelMatch或OpenCV-Python: 用于截图对比验证游戏画面。3. 环境搭建与核心工具链配置3.1 Python与Playwright环境搭建第一步是准备好Python环境。我强烈建议使用虚拟环境Virtual Environment来隔离项目依赖避免包冲突。# 1. 创建项目目录并进入 mkdir game_auto_test cd game_auto_test # 2. 创建Python虚拟环境以venv为例 python -m venv venv # 3. 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate # 4. 升级pip pip install --upgrade pip接下来安装Playwright。官方推荐使用playwright这个PyPI包它会安装Playwright库和命令行工具。# 安装Playwright库 pip install playwright # 安装Playwright所需的浏览器内核Chromium, Firefox, WebKit playwright install实操心得playwright install这一步会下载几百MB的浏览器二进制文件请确保网络通畅。如果只为测试Chromium可以运行playwright install chromium来节省时间和磁盘空间。在国内网络环境下如果下载缓慢可以尝试设置环境变量PLAYWRIGHT_DOWNLOAD_HOST为国内镜像源。3.2 辅助测试框架与工具安装我们将使用Pytest来组织测试用例并用Allure来生成报告。# 安装Pytest和Playwright的Pytest插件 pip install pytest pytest-playwright # 安装Allure报告生成器需要Java环境 # 首先确保系统已安装Java 8 # 然后安装allure-pytest pip install allure-pytest # 可选安装用于图像对比的库 pip install opencv-python pillow # 或者安装pixelmatch更轻量专门用于像素对比 pip install pixelmatch创建requirements.txt文件记录所有依赖playwright1.40.0 pytest7.4.0 pytest-playwright0.4.0 allure-pytest2.13.0 pixelmatch0.2.0 opencv-python4.8.0 # 可选3.3 初始化项目与编写第一个测试让我们验证环境是否正常工作。创建一个最简单的测试文件tests/test_smoke.pyimport re from playwright.sync_api import Page, expect def test_game_page_title(page: Page): 冒烟测试访问游戏页面验证标题是否正确。 # 导航到游戏URL这里用一个本地示例服务器实际替换为你的游戏地址 page.goto(http://localhost:8000) # 使用expect断言进行验证Playwright会自动等待条件满足 expect(page).to_have_title(re.compile(My HTML5 Game)) # 或者验证页面中是否存在某个关键元素比如“开始游戏”按钮 start_button page.get_by_role(button, name开始游戏) expect(start_button).to_be_visible()然后使用Pytest运行这个测试pytest tests/test_smoke.py --headed # --headed 表示打开浏览器窗口便于调试如果一切顺利你会看到浏览器打开访问指定页面然后测试通过。这就是我们的起点。4. 游戏元素定位与交互策略4.1 定位Canvas内的“不可见”元素这是游戏自动化最大的挑战。HTML5游戏的核心渲染区域通常是一个canvas标签里面的图形不是DOM元素无法用常规的CSS选择器或XPath定位。策略一定位Canvas外的控制元素很多游戏UI层是叠加在Canvas之上的HTML元素。例如开始按钮、暂停菜单、技能图标、分数显示等。这些是我们可以正常定位和交互的。优先使用语义化定位器它们更稳定。# 不推荐使用脆弱的CSS选择器 page.click(#root div button.start-btn) # 推荐使用角色Role和文本定位 page.get_by_role(button, name开始游戏).click() page.get_by_text(暂停).click() # 或者使用Placeholder、Label等 page.get_by_placeholder(输入玩家名).fill(PlaywrightTester)策略二基于固定坐标的交互当必须在Canvas区域内交互时如点击游戏画面某个位置进行攻击我们只能使用坐标。# 获取Canvas元素 canvas page.locator(canvas#gameCanvas) # 方法1点击Canvas元素的相对坐标相对于该元素左上角 canvas.click(position{x: 100, y: 200}) # 方法2使用page.mouse在绝对坐标上操作 page.mouse.click(300, 400) # 点击页面坐标(300, 400)重要注意事项基于坐标的交互是极其脆弱的。游戏分辨率、浏览器缩放、窗口位置的变化都会导致坐标失效。因此必须将其作为最后的手段并尽可能通过计算相对坐标来增加鲁棒性。例如根据Canvas元素的实际位置和大小来计算点击点。策略三通过截图与预期图对比进行验证对于无法通过文本断言验证的游戏状态如“敌人被击败”的动画我们可以通过截图对比来验证。def test_enemy_defeated(page: Page): # ... 执行击败敌人的操作 ... # 对游戏区域截图 canvas page.locator(canvas#gameCanvas) screenshot canvas.screenshot() # 与预存的“敌人被击败后”的预期截图进行像素对比 # 这里需要用到pixelmatch或OpenCV库 # 伪代码 # difference compare_screenshots(screenshot, expected_after_defeat.png) # assert difference threshold # 差异像素小于阈值则认为通过4.2 模拟复杂的游戏输入Playwright可以模拟几乎所有用户输入。键盘事件用于控制角色移动、释放技能。page.keyboard.press(ArrowUp) # 按下上箭头 page.keyboard.down(Space) # 按住空格键 page.wait_for_timeout(500) # 按住500毫秒 page.keyboard.up(Space) # 松开空格键鼠标事件除了点击还有拖拽、右键、滚轮。# 拖拽从(x1,y1)拖到(x2,y2) page.mouse.move(100, 150) page.mouse.down() page.mouse.move(300, 350, steps10) # steps模拟平滑拖拽 page.mouse.up() # 右键点击 page.locator(canvas).click(buttonright) # 滚轮缩放地图等 page.mouse.wheel(0, 100) # 向下滚动100像素触摸事件针对移动端模拟# 在移动设备上下文中可以通过触摸API模拟 # 通常更简单的方法是直接使用page.touchscreen如果支持或坐标点击来模拟触屏。4.3 处理游戏状态与等待游戏是状态驱动的。自动化脚本必须能感知游戏状态的变化。利用Playwright的自动等待expect()断言和大多数操作click,fill都内置了等待。# 等待“游戏结束”文本出现最多等10秒 expect(page.get_by_text(游戏结束)).to_be_visible(timeout10000)自定义等待条件有时需要等待一个特定的游戏状态比如分数增加到某个值。def wait_for_score(page: Page, target_score: int, timeout: int 5000): 等待分数元素显示的值达到或超过目标分数 start_time time.time() while time.time() - start_time timeout / 1000: score_text page.locator(#score).inner_text() try: current_score int(score_text) if current_score target_score: return True except ValueError: pass page.wait_for_timeout(200) # 每200毫秒检查一次 raise TimeoutError(f分数在{timeout}ms内未达到{target_score})监听网络请求游戏的关键状态变化常伴随特定的网络请求如提交分数、加载下一关。with page.expect_response(**/api/submit-score) as response_info: page.get_by_text(提交分数).click() response response_info.value assert response.ok assert response.json()[success] is True5. 实战构建一个“太空射击游戏”测试用例让我们模拟一个经典的2D太空射击游戏测试场景。游戏流程加载 - 点击开始 - 用方向键移动飞船 - 按空格键射击 - 击毁一定数量敌机后通关。5.1 使用页面对象模型POM封装首先在pages/game_home_page.py中创建首页的页面对象from playwright.sync_api import Page class GameHomePage: def __init__(self, page: Page): self.page page self.start_button page.get_by_role(button, name开始游戏) self.game_canvas page.locator(#gameCanvas) self.score_display page.locator(#scoreValue) def navigate(self, url): self.page.goto(url) return self def start_game(self): self.start_button.click() # 等待游戏界面加载完成例如等待一个特定的游戏元素出现 self.page.wait_for_selector(.player-ship, statevisible) return GamePlayPage(self.page) # 返回游戏进行中的页面对象 class GamePlayPage: def __init__(self, page: Page): self.page page self.canvas page.locator(#gameCanvas) def move_ship(self, direction: str): 方向: left, right, up, down key_map {left: ArrowLeft, right: ArrowRight, up: ArrowUp, down: ArrowDown} self.page.keyboard.press(key_map[direction]) def fire(self): self.page.keyboard.press(Space) def get_score(self) - int: # 分数可能动态更新这里获取当前文本并转换 score_text self.page.locator(#scoreValue).inner_text() return int(score_text) if score_text.isdigit() else 05.2 编写核心玩法测试用例在tests/test_space_shooter.py中import pytest from pages.game_home_page import GameHomePage pytest.fixture(scopefunction) def game_page(page): 每个测试用例提供一个已导航到游戏首页的页面对象 home_page GameHomePage(page) home_page.navigate(http://localhost:8000) return home_page def test_complete_first_wave(game_page): 测试用例完成第一波敌机攻击。 步骤1. 启动游戏 2. 移动飞船躲避 3. 射击敌机 4. 验证分数增加。 # 1. 启动游戏 play_page game_page.start_game() # 2. 执行游戏操作这里我们模拟一个简单的策略 # 假设敌机从上方出现我们向右移动并连续射击 play_page.move_ship(right) play_page.page.wait_for_timeout(200) # 移动一小段时间 initial_score play_page.get_score() # 3. 连续射击5次每次间隔150ms模拟攻击频率 for _ in range(5): play_page.fire() play_page.page.wait_for_timeout(150) # 4. 等待一小段时间让分数更新 play_page.page.wait_for_timeout(1000) # 5. 验证分数是否增加假设击毁一架敌机得100分 final_score play_page.get_score() assert final_score initial_score, f分数未增加。初始: {initial_score}, 最终: {final_score} # 更精确的断言可以根据游戏逻辑判断至少得了多少分 # assert final_score initial_score 100 def test_game_over_scenario(game_page): 测试用例触发游戏结束条件并验证结束画面。 play_page game_page.start_game() # 模拟送死行为将飞船移动到危险区域假设左上角(50,50)有立即死亡的障碍 # 注意这是基于坐标的脆弱操作仅作示例。 play_page.canvas.click(position{x: 50, y: 50}) # 等待并验证“游戏结束”画面出现 expect(play_page.page.get_by_text(游戏结束, exactTrue)).to_be_visible(timeout5000) expect(play_page.page.get_by_role(button, name再玩一次)).to_be_visible()5.3 集成截图对比验证在utils/screenshot_helper.py中创建一个工具类from PIL import Image, ImageChops import io import math class ScreenshotComparator: staticmethod def compare_screenshots(img_a_bytes, img_b_bytes, threshold0.01): 比较两张截图返回差异比例。 :param img_a_bytes: 截图A的字节数据 :param img_b_bytes: 截图B的字节数据 :param threshold: 可接受的差异比例阈值 :return: (bool是否通过, float差异比例) img_a Image.open(io.BytesIO(img_a_bytes)).convert(RGB) img_b Image.open(io.BytesIO(img_b_bytes)).convert(RGB) # 确保图片尺寸相同 if img_a.size ! img_b.size: # 可以尝试调整尺寸这里直接返回失败 return False, 1.0 # 计算差异 diff ImageChops.difference(img_a, img_b) diff_pixels sum(diff.getdata(band0)) / 255.0 # 简化计算实际可用更精确方法 total_pixels img_a.size[0] * img_a.size[1] difference_ratio diff_pixels / total_pixels return difference_ratio threshold, difference_ratio在测试用例中使用def test_ui_layout_consistent(game_page): 验证游戏主界面UI布局与基准图一致 # 1. 导航到页面后等待UI稳定 game_page.page.wait_for_load_state(networkidle) game_page.page.wait_for_timeout(1000) # 额外等待动画 # 2. 对特定区域截图例如整个游戏容器 container game_page.page.locator(.game-container) current_screenshot container.screenshot() # 3. 读取预存的基准截图 with open(assets/expected_screenshots/main_ui_baseline.png, rb) as f: baseline_screenshot f.read() # 4. 对比 from utils.screenshot_helper import ScreenshotComparator is_match, diff_ratio ScreenshotComparator.compare_screenshots( current_screenshot, baseline_screenshot, threshold0.005 # 允许0.5%的像素差异抗锯齿、字体渲染可能不同 ) assert is_match, fUI布局差异过大差异比例为{diff_ratio:.2%}超过阈值0.5%。请检查UI变更或更新基准图。6. 高级技巧与最佳实践6.1 测试数据驱动与参数化使用pytest.mark.parametrize可以轻松用不同数据运行同一测试逻辑非常适合测试不同游戏关卡或难度。import pytest pytest.mark.parametrize(level, expected_min_score, [ (1, 500), (2, 1000), (3, 2000), ]) def test_game_level_completion(game_page, level, expected_min_score): 测试不同关卡的通关分数要求 # 假设有选择关卡的下拉菜单 game_page.page.select_option(#levelSelect, valuestr(level)) play_page game_page.start_game() # ... 执行一套固定的“通关”操作 ... # 例如调用一个通用的通关脚本函数 complete_level_routine(play_page) final_score play_page.get_score() assert final_score expected_min_score, f关卡{level}得分{final_score}未达到最低要求{expected_min_score}6.2 模拟网络条件与性能测试Playwright可以模拟不同的网络环境测试游戏在弱网下的表现。def test_game_loads_in_slow_network(page): 测试在慢速3G网络下游戏资源加载和超时处理 # 从Playwright预置的网络配置中导入 from playwright.sync_api import BrowserContext # 设置网络为“慢速3G” context page.context context.set_default_timeout(30000) # 设置全局超时延长 context.route(**/*, lambda route: route.continue_()) # 可以配合拦截器 # 更直接的方式在创建Browser Context时指定 # 但这里我们通过CDP会话模拟如果浏览器支持 cdp_session page.context.new_cdp_session(page) cdp_session.send(Network.emulateNetworkConditions, { offline: False, downloadThroughput: 500 * 1024 / 8, # 500 Kbps uploadThroughput: 500 * 1024 / 8, latency: 400 # 400ms延迟 }) page.goto(http://localhost:8000) # 验证在恶劣条件下游戏至少能显示加载界面或错误提示 expect(page.get_by_text(加载中...)).to_be_visible(timeout10000) # 或者验证关键资源是否在超时前加载完成6.3 并行测试与CI/CD集成为了提高测试效率可以在多个浏览器或设备上并行运行测试。使用Pytest-xdist进行并行化pip install pytest-xdist # 使用2个worker并行运行测试 pytest tests/ -n 2在CI中运行测试以GitHub Actions为例# .github/workflows/playwright.yml name: Playwright Game Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: { python-version: 3.10 } - name: Install dependencies run: | pip install -r requirements.txt playwright install chromium # 只安装需要的浏览器 - name: Start local game server (if needed) run: python -m http.server 8000 --directory ./game_dist - name: Run tests run: pytest tests/ --browser chromium --headedfalse --alluredir./reports/allure-results - name: Generate Allure report if: always() uses: simple-elf/allure-report-actionmaster with: allure_results: ./reports/allure-results allure_report: ./reports/allure-report - name: Upload report uses: actions/upload-artifactv3 with: name: allure-report path: ./reports/allure-report6.4 测试报告与失败分析清晰的报告能快速定位问题。Allure报告非常强大。运行测试并生成Allure数据pytest tests/ --alluredir./reports/allure-results本地查看报告allure serve ./reports/allure-results在测试中附加截图和日志当测试失败时自动截取当前页面和游戏Canvas附加到报告中。import allure from playwright.sync_api import Page def test_with_screenshot_on_failure(page: Page): try: # ... 测试步骤 ... assert 1 2 # 故意失败 except AssertionError as e: # 测试失败时截取全屏和Canvas full_screenshot page.screenshot(full_pageTrue) canvas_screenshot page.locator(canvas).screenshot() allure.attach(full_screenshot, name失败时全屏截图, attachment_typeallure.attachment_type.PNG) allure.attach(canvas_screenshot, name失败时游戏画面截图, attachment_typeallure.attachment_type.PNG) # 也可以附加页面HTML或控制台日志 console_log page.evaluate(() JSON.stringify(console.logs)) # 需要提前监听console allure.attach(console_log, name控制台日志, attachment_typeallure.attachment_type.TEXT) raise e # 重新抛出异常让测试标记为失败这样在Allure报告中每个失败的测试用例下都会有丰富的上下文信息极大方便了问题排查。7. 常见问题与调试技巧实录在实际操作中你肯定会遇到各种稀奇古怪的问题。这里记录了一些典型坑点和解决思路。7.1 元素定位失败动态Canvas与浮动UI问题游戏画面是CanvasUI元素可能是动态生成或绝对定位的用常规选择器找不到。排查打开Playwright的调试工具playwright codegen录制你的操作看它生成了什么定位器。playwright codegen http://localhost:8000在浏览器开发者工具中检查Canvas上层是否有DIV容器。有时UI是挂在Canvas的兄弟节点或父节点上。如果UI是动态插入的确保你的操作前有足够的等待。使用page.wait_for_selector或expect(locator).to_be_visible()。解决优先使用get_by_role(),get_by_text(),get_by_label()等语义化定位器。如果必须用CSS尽量使用稳定的属性如>context browser.new_context(viewport{width: 1280, height: 720}) page context.new_page()解决永远不要使用绝对坐标。应该先获取目标元素的边界框bounding box然后计算相对坐标进行点击。canvas_box canvas.bounding_box() # 返回 {x, y, width, height} # 点击Canvas中心点 canvas.click(position{ x: canvas_box[width] / 2, y: canvas_box[height] / 2 })7.3 游戏状态同步问题操作太快或太慢问题脚本执行速度太快游戏逻辑还没处理完上一个操作下一个操作就触发了导致状态错乱。排查在关键操作之间加入适当的等待。但不要滥用page.wait_for_timeout应尽量等待特定的游戏状态信号。解决事件驱动等待等待某个游戏内元素出现、消失或状态改变。# 等待“攻击完成”的视觉反馈比如一个特效消失 page.wait_for_selector(.attack-effect, statehidden)网络请求等待等待一个标志性的API调用完成。# 点击“购买道具”后等待购买确认的请求返回 with page.expect_response(**/api/buy-item) as response: page.click(#buyButton)自定义轮询如前所述编写一个轮询函数不断检查游戏内的某个值如分数、倒计时。7.4 截图对比误报抗锯齿与字体渲染差异问题在不同操作系统、不同浏览器版本上相同的游戏画面截图对比总是失败因为字体渲染、颜色抗锯齿有细微差别。解决设置阈值在对比函数中设置一个合理的像素差异阈值如0.5%-1%而不是要求100%一致。预处理图片对比前将图片转换为灰度图或进行高斯模糊以消除抗锯齿带来的高频噪声。from PIL import ImageFilter img img.convert(L).filter(ImageFilter.GaussianBlur(radius1))对比特定区域只对比UI的关键区域如按钮、分数文本区域忽略动态变化的游戏背景。使用更高级的对比库如opencv的模板匹配或特征匹配对于UI元素位置有微小偏移的情况更鲁棒。7.5 测试稳定性处理随机性与非确定性行为游戏常常包含随机元素怪物刷新位置、掉落物品。策略种子控制如果游戏支持在测试开始时通过URL参数或API设置随机数种子确保每次测试运行都产生相同的随机序列。page.goto(http://localhost:8000?seed12345)测试“范围”而非“精确值”断言分数“大于100”而不是“等于150”。抽象测试逻辑将测试重点放在“玩家能否完成核心目标”上而不是具体的每一步。例如测试“能否在60秒内击败至少一个敌人”而不是“能否在坐标(100,200)点击敌人”。7.6 性能监控与内存泄漏检测自动化测试也可以用来监控游戏性能。def test_memory_leak(page: Page): 简单检测重复进行同一场景是否导致内存持续增长 # 此测试需要浏览器启动时开启性能监控通常通过CDP cdp_session page.context.new_cdp_session(page) cdp_session.send(Performance.enable) memory_samples [] for i in range(10): # 执行一轮游戏操作 play_game_one_round(page) # 获取内存指标 metrics cdp_session.send(Performance.getMetrics) memory_metric next(m for m in metrics[metrics] if m[name] JSHeapUsedSize) memory_samples.append(memory_metric[value]) page.wait_for_timeout(1000) # 简单分析如果内存持续增长且不回落可能有问题 # 这里可以计算增长趋势或设置一个阈值 print(f内存使用样本: {memory_samples}) # 断言最后一轮的内存不应比第一轮高太多例如20% assert memory_samples[-1] memory_samples[0] * 1.2, 潜在的内存泄漏风险游戏自动化测试是一个需要不断磨合和调整的过程。没有一劳永逸的脚本随着游戏版本的更新UI和逻辑都可能变化测试脚本也需要相应维护。但一旦建立起稳定的核心流程测试套件它所带来的回归保障和效率提升对于任何严肃的游戏项目来说都是不可或缺的。关键在于找到平衡点自动化那些稳定、重复、高价值的场景把人的创造力留给探索那些真正有趣和复杂的边界情况。