diff --git a/mumu-pytest/README.md b/mumu-pytest/README.md new file mode 100644 index 0000000..f8e231c --- /dev/null +++ b/mumu-pytest/README.md @@ -0,0 +1,88 @@ +# Mumu 模拟器 pytest 测试框架 + +## 目录结构 + +``` +mumu-pytest/ +├── conftest.py # pytest配置和公共fixture +├── test_mumu.py # 测试用例 +├── start.png # 启动按钮图片 +├── running.png # 运行中图片 +├── web.png # web按钮图片 +├── web_address.png # 地址输入框图片 +├── web_goon.png # 继续按钮图片 +├── web_debug.png # 调试按钮图片 +├── web_debug_apk.png # APK按钮图片 +├── web_debug_apk_download.png # 下载按钮图片 +├── web_debug_apk_open.png # 打开文件按钮图片 +├── web_debug_apk_install.png # 安装按钮图片 +├── web_debug_apk_run.png # 运行按钮图片 +├── requirements.txt # Python依赖 +└── README.md # 说明文档 +``` + +## 安装依赖 + +```bash +pip install -r requirements.txt +``` + +## 配置 + +在 `conftest.py` 中修改以下配置: + +```python +MUMU_EXE_PATH = r"C:\Program Files\Netease\MuMu\nx_main\MuMuNxMain.exe" +ADB_PATH = r"C:\Program Files\Netease\MuMu\shell\adb.exe" +WEB_URL = "http://192.168.3.15/" +INSTALLED_PACKAGE_NAME = "com.example.flomo_ai" +``` + +## 图片说明 + +将以下图片放入 `mumu-pytest` 目录: + +| 文件名 | 说明 | +|--------|------| +| start.png | 启动按钮 | +| running.png | 运行中标识 | +| web.png | Web按钮 | +| web_address.png | 地址输入框 | +| web_goon.png | 继续按钮 | +| web_debug.png | 调试按钮 | +| web_debug_apk.png | APK按钮 | +| web_debug_apk_download.png | 下载按钮 | +| web_debug_apk_open.png | 打开文件按钮 | +| web_debug_apk_install.png | 安装按钮 | +| web_debug_apk_run.png | 运行按钮 | + +## 运行测试 + +```bash +# 运行所有测试 +pytest + +# 显示详细输出 +pytest -v -s +``` + +## 测试流程 + +1. 启动模拟器 (start.png) +2. 等待运行中 (running.png) +3. 点击Web按钮 (web.png) +4. 输入URL地址 (web_address.png -> http://192.168.3.15/) +5. 点击继续 (web_goon.png) +6. 点击调试 (web_debug.png) +7. 点击APK (web_debug_apk.png) +8. 点击下载 (web_debug_apk_download.png) +9. 点击打开 (web_debug_apk_open.png) +10. 点击安装 (web_debug_apk_install.png) +11. 点击运行 (web_debug_apk_run.png) + +## 注意事项 + +1. 确保MuMu模拟器已正确安装 +2. 需要先截取各个步骤对应的图片 +3. pyautogui需要配置合适的置信度和超时时间 +4. 确保模拟器路径和ADB路径正确 diff --git a/mumu-pytest/conftest.py b/mumu-pytest/conftest.py new file mode 100644 index 0000000..8e7a57c --- /dev/null +++ b/mumu-pytest/conftest.py @@ -0,0 +1,211 @@ +""" +Mumu模拟器 pytest 测试框架 +""" + +import pytest +import pyautogui +import time +import os +import subprocess +import requests +from pathlib import Path + +# 配置 +MUMU_EXE_PATH = r"C:\Program Files\Netease\MuMu\nx_main\MuMuNxMain.exe" +MUMU_PROCESS_NAME = "MuMuNxMain.exe" +ADB_PATH = r"C:\Program Files\Netease\MuMu\shell\adb.exe" +APK_DOWNLOAD_URL = "http://your-server.com/app-release.apk" +LOCAL_APK_PATH = os.path.join(os.path.dirname(__file__), "test_app.apk") +INSTALLED_PACKAGE_NAME = "com.example.flomo_ai" +WEB_URL = "http://192.168.3.15/" + +SCRIPT_DIR = os.path.dirname(__file__) + + +class MumuEmulator: + """Mumu模拟器控制类""" + + def __init__(self): + self.process_name = MUMU_PROCESS_NAME + self.script_dir = SCRIPT_DIR + pyautogui.PAUSE = 1 + pyautogui.FAILSAFE = True + + def is_running(self) -> bool: + """检查模拟器是否运行""" + try: + result = subprocess.run( + ["tasklist"], + capture_output=True, + text=True + ) + return self.process_name in result.stdout + except Exception: + return False + + def start(self): + """启动模拟器""" + if not self.is_running(): + subprocess.Popen(MUMU_EXE_PATH) + time.sleep(5) + + def find_and_click(self, image_name, confidence=0.8, timeout=30): + """查找图片并点击""" + image_path = os.path.join(self.script_dir, f"{image_name}.png") + if not os.path.exists(image_path): + print(f"图片不存在: {image_path}") + return False + + start_time = time.time() + while time.time() - start_time < timeout: + try: + location = pyautogui.locateOnScreen(image_path, confidence=confidence) + if location: + center = pyautogui.center(location) + pyautogui.click(center) + print(f"点击了: {image_name}") + return True + except Exception as e: + pass + time.sleep(1) + print(f"未找到图片: {image_name}") + return False + + def type_text(self, text): + """输入文本""" + pyautogui.write(text, interval=0.1) + print(f"输入了文本: {text}") + + def press_enter(self): + """按回车""" + pyautogui.press("enter") + print("按下回车") + + def wait_for_boot(self, timeout=120): + """等待模拟器启动完成""" + boot_completed = False + start_time = time.time() + while time.time() - start_time < timeout: + if self.is_running(): + time.sleep(5) + boot_completed = True + break + time.sleep(2) + return boot_completed + + def check_image_exists(self, image_name, confidence=0.8) -> bool: + """检查图片是否存在""" + image_path = os.path.join(self.script_dir, f"{image_name}.png") + if not os.path.exists(image_path): + return False + try: + location = pyautogui.locateOnScreen(image_path, confidence=confidence) + return location is not None + except Exception: + return False + + def run_sequence(self): + """按顺序执行任务""" + sequence = [ + "start", + "running", + "web", + "web.address", + "web_goon", + "web_debug", + "web_debug_apk", + "web_debug_apk_download", + "web_debug_apk_open", + "web_debug_apk_install", + "web_debug_apk_run" + ] + + for image_name in sequence: + if image_name == "web.address": + if self.check_image_exists("web.address"): + time.sleep(1) + self.type_text(WEB_URL) + time.sleep(0.5) + self.press_enter() + else: + print("web.address 图片不存在,跳过输入URL") + else: + self.find_and_click(image_name, timeout=10) + time.sleep(2) + + def close(self): + """关闭模拟器""" + try: + subprocess.run(["taskkill", "/F", "/IM", self.process_name]) + except Exception: + pass + + +class AdbHelper: + """ADB命令辅助类""" + + def __init__(self): + self.adb_path = ADB_PATH + + def exec_cmd(self, *args): + """执行ADB命令""" + cmd = [self.adb_path] + list(args) + result = subprocess.run(cmd, capture_output=True, text=True) + return result.stdout, result.stderr + + def install_apk(self, apk_path: str) -> bool: + """安装APK""" + stdout, stderr = self.exec_cmd("install", "-r", apk_path) + return "Success" in stdout + + def uninstall_app(self, package_name: str) -> bool: + """卸载应用""" + stdout, stderr = self.exec_cmd("uninstall", package_name) + return "Success" in stdout + + def launch_app(self, package_name: str, activity: str): + """启动应用""" + self.exec_cmd("shell", "am", "start", "-n", f"{package_name}/{activity}") + + def is_app_installed(self, package_name: str) -> bool: + """检查应用是否已安装""" + stdout, _ = self.exec_cmd("shell", "pm", "list", "packages", package_name) + return package_name in stdout + + def pull_file(self, remote_path: str, local_path: str): + """从模拟器拉取文件""" + self.exec_cmd("pull", remote_path, local_path) + + +@pytest.fixture(scope="session") +def emulator(): + """模拟器fixture""" + mumu = MumuEmulator() + yield mumu + # 测试结束后清理 + if mumu.is_running(): + mumu.close() + + +@pytest.fixture(scope="session") +def adb(): + """ADB fixture""" + return AdbHelper() + + +@pytest.fixture(scope="session") +def download_apk(): + """下载APK""" + if os.path.exists(LOCAL_APK_PATH): + return LOCAL_APK_PATH + + response = requests.get(APK_DOWNLOAD_URL, stream=True) + with open(LOCAL_APK_PATH, "wb") as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + + yield LOCAL_APK_PATH + + # 清理 + if os.path.exists(LOCAL_APK_PATH): + os.remove(LOCAL_APK_PATH) diff --git a/mumu-pytest/requirements.txt b/mumu-pytest/requirements.txt new file mode 100644 index 0000000..ede4101 --- /dev/null +++ b/mumu-pytest/requirements.txt @@ -0,0 +1,3 @@ +pytest>=7.0.0 +pyautogui>=0.9.54 +requests>=2.28.0 diff --git a/mumu-pytest/running.png b/mumu-pytest/running.png new file mode 100644 index 0000000..d25fead Binary files /dev/null and b/mumu-pytest/running.png differ diff --git a/mumu-pytest/start.png b/mumu-pytest/start.png new file mode 100644 index 0000000..bca07bd Binary files /dev/null and b/mumu-pytest/start.png differ diff --git a/mumu-pytest/test_mumu.py b/mumu-pytest/test_mumu.py new file mode 100644 index 0000000..b86edf4 --- /dev/null +++ b/mumu-pytest/test_mumu.py @@ -0,0 +1,27 @@ +""" +Mumu模拟器测试 +""" + +import pytest + + +def test_start_emulator_sequence(emulator): + """测试模拟器启动和操作序列""" + assert not emulator.is_running(), "模拟器已启动" + emulator.start() + assert emulator.is_running(), "模拟器启动失败" + + +def test_run_sequence(emulator): + """测试按顺序执行任务""" + if not emulator.is_running(): + pytest.skip("模拟器未运行") + + emulator.run_sequence() + + +def test_close_emulator(emulator): + """测试关闭模拟器""" + if emulator.is_running(): + emulator.close() + assert not emulator.is_running(), "模拟器关闭失败" diff --git a/mumu-pytest/web.png b/mumu-pytest/web.png new file mode 100644 index 0000000..62f5ab9 Binary files /dev/null and b/mumu-pytest/web.png differ diff --git a/mumu-pytest/web_address.png b/mumu-pytest/web_address.png new file mode 100644 index 0000000..7782355 Binary files /dev/null and b/mumu-pytest/web_address.png differ diff --git a/mumu-pytest/web_debug.png b/mumu-pytest/web_debug.png new file mode 100644 index 0000000..1a64ff4 Binary files /dev/null and b/mumu-pytest/web_debug.png differ diff --git a/mumu-pytest/web_debug_apk.png b/mumu-pytest/web_debug_apk.png new file mode 100644 index 0000000..0c347cc Binary files /dev/null and b/mumu-pytest/web_debug_apk.png differ diff --git a/mumu-pytest/web_debug_apk_download.png b/mumu-pytest/web_debug_apk_download.png new file mode 100644 index 0000000..38a0940 Binary files /dev/null and b/mumu-pytest/web_debug_apk_download.png differ diff --git a/mumu-pytest/web_debug_apk_install.png b/mumu-pytest/web_debug_apk_install.png new file mode 100644 index 0000000..fa6c1ca Binary files /dev/null and b/mumu-pytest/web_debug_apk_install.png differ diff --git a/mumu-pytest/web_debug_apk_open.png b/mumu-pytest/web_debug_apk_open.png new file mode 100644 index 0000000..5436d89 Binary files /dev/null and b/mumu-pytest/web_debug_apk_open.png differ diff --git a/mumu-pytest/web_debug_apk_run.png b/mumu-pytest/web_debug_apk_run.png new file mode 100644 index 0000000..483e4b7 Binary files /dev/null and b/mumu-pytest/web_debug_apk_run.png differ diff --git a/mumu-pytest/web_goon.png b/mumu-pytest/web_goon.png new file mode 100644 index 0000000..6cf79b2 Binary files /dev/null and b/mumu-pytest/web_goon.png differ