更新mumu-pytest测试框架,修复build.gradle.kts依赖问题

This commit is contained in:
2026-03-12 20:13:24 +08:00
parent 9b3c878159
commit cb5c3c7dc9
7 changed files with 168 additions and 64 deletions

View File

@@ -9,20 +9,42 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.example.flomo_ai" applicationId = "com.example.flomo_ai"
minSdk = 28 minSdk = 24
//noinspection OldTargetApi
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 2
versionName = "1.1" versionName = "1.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true
} }
// 启用 View Binding
buildFeatures {
viewBinding = true
}
}
sourceSets {
getByName("main") {
java.srcDirs("src/main/java")
res.srcDirs("src/main/res")
manifest.srcFile("src/main/AndroidManifest.xml")
}
getByName("test") {
java.srcDirs("src/test/java")
}
getByName("androidTest") {
java.srcDirs("src/androidTest/java")
}
} }
buildTypes { buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
applicationIdSuffix = ".debug"
}
release { release {
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles( proguardFiles(
@@ -63,6 +85,30 @@ dependencies {
implementation(libs.androidx.material3) implementation(libs.androidx.material3)
implementation(libs.appcompat) implementation(libs.appcompat)
implementation(libs.material) implementation(libs.material)
// 网络请求相关
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
// JSON解析
implementation(libs.gson)
implementation(libs.moshi.kotlin)
// 协程支持
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)
// JWT处理
implementation(libs.kjwt.jwks)
implementation(libs.nimbus.jose.jwt)
// 权限处理 (已移除PermissionX代码中未使用)
// 图片加载
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
// 测试依赖
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
@@ -70,13 +116,4 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4) androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest) debugImplementation(libs.androidx.ui.test.manifest)
implementation(libs.gson)
implementation(libs.kjwt.jwks)
implementation(libs.nimbus.jose.jwt)
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.moshi.kotlin)
} }

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -1,6 +1,6 @@
[versions] [versions]
agp = "8.6.0" agp = "8.13.2"
gradle = "7.0.4" gradle = "8.13.2"
gson = "2.10.1" gson = "2.10.1"
kjwtJwks = "0.9.0" kjwtJwks = "0.9.0"
kotlin = "1.9.0" kotlin = "1.9.0"
@@ -8,7 +8,7 @@ coreKtx = "1.10.1"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"
kotlinGradlePlugin = "1.6.10" kotlinGradlePlugin = "1.6.21"
kotlinxCoroutinesAndroid = "1.7.1" kotlinxCoroutinesAndroid = "1.7.1"
kotlinxCoroutinesCore = "1.7.1" kotlinxCoroutinesCore = "1.7.1"
lifecycleRuntimeKtx = "2.6.1" lifecycleRuntimeKtx = "2.6.1"
@@ -21,6 +21,7 @@ material = "1.12.0"
moshiKotlin = "1.12.0" moshiKotlin = "1.12.0"
nimbusJoseJwt = "9.40" nimbusJoseJwt = "9.40"
okhttp = "4.12.0" okhttp = "4.12.0"
permissionx = "1.7.1"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -50,6 +51,7 @@ material = { group = "com.google.android.material", name = "material", version.r
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshiKotlin" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshiKotlin" }
nimbus-jose-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbusJoseJwt" } nimbus-jose-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbusJoseJwt" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
permissionx = { group = "com.permissionx.guolindev", name = "permissionx", version.ref = "permissionx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }

View File

@@ -33,28 +33,54 @@ pip install -r requirements.txt
```python ```python
MUMU_EXE_PATH = r"C:\Program Files\Netease\MuMu\nx_main\MuMuNxMain.exe" 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" ADB_PATH = r"C:\Program Files\Netease\MuMu\shell\adb.exe"
WEB_URL = "http://192.168.3.15/" WEB_URL = "http://192.168.3.15/"
INSTALLED_PACKAGE_NAME = "com.example.flomo_ai" INSTALLED_PACKAGE_NAME = "com.example.flomo_ai"
APK_DOWNLOAD_URL = "http://your-server.com/app-release.apk"
``` ```
## 图片说明 ## 功能说明
将以下图片放入 `mumu-pytest` 目录: ### MumuEmulator 类
| 文件名 | 说明 | | 方法 | 说明 |
|--------|------| |------|------|
| start.png | 启动按钮 | | `is_running()` | 检查模拟器是否运行 |
| running.png | 运行中标识 | | `start()` | 启动模拟器并置顶窗口 |
| web.png | Web按钮 | | `bring_to_front()` | 将模拟器窗口置顶 |
| web_address.png | 地址输入框 | | `find_and_click(image_name, confidence, timeout)` | 查找图片并点击 |
| web_goon.png | 继续按钮 | | `type_text(text)` | 输入文本 |
| web_debug.png | 调试按钮 | | `press_enter()` | 按回车 |
| web_debug_apk.png | APK按钮 | | `wait_for_boot(timeout)` | 等待模拟器启动完成 |
| web_debug_apk_download.png | 下载按钮 | | `check_image_exists(image_name)` | 检查图片是否存在 |
| web_debug_apk_open.png | 打开文件按钮 | | `run_sequence()` | 按顺序执行任务 |
| web_debug_apk_install.png | 安装按钮 | | `close()` | 关闭模拟器 |
| web_debug_apk_run.png | 运行按钮 |
### AdbHelper 类
| 方法 | 说明 |
|------|------|
| `exec_cmd(*args)` | 执行ADB命令 |
| `install_apk(apk_path)` | 安装APK |
| `uninstall_app(package_name)` | 卸载应用 |
| `launch_app(package_name, activity)` | 启动应用 |
| `is_app_installed(package_name)` | 检查应用是否已安装 |
| `pull_file(remote_path, local_path)` | 从模拟器拉取文件 |
## 测试流程 (run_sequence)
1. 置顶模拟器窗口
2. 等待30秒
3. 点击 web.png
4. 检测 web_address.png 出现后,输入 URL (http://192.168.3.15/)
5. 点击 web_goon.png
6. 点击 web_debug.png
7. 点击 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
## 运行测试 ## 运行测试
@@ -62,27 +88,18 @@ INSTALLED_PACKAGE_NAME = "com.example.flomo_ai"
# 运行所有测试 # 运行所有测试
pytest pytest
# 运行指定测试
pytest test_mumu.py::test_start_emulator_sequence
pytest test_mumu.py::test_run_sequence
# 显示详细输出 # 显示详细输出
pytest -v -s 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模拟器已正确安装 1. 确保MuMu模拟器已正确安装
2. 需要先截取各个步骤对应的图片 2. 需要先截取各个步骤对应的图片放入 mumu-pytest 目录
3. pyautogui需要配置合适的置信度和超时时间 3. pyautogui需要配置合适的置信度(默认0.8)和超时时间
4. 确保模拟器路径和ADB路径正确 4. 确保模拟器路径和ADB路径正确
5. 测试图片文件名不要包含空格,使用下划线分隔

View File

@@ -4,6 +4,7 @@ Mumu模拟器 pytest 测试框架
import pytest import pytest
import pyautogui import pyautogui
import pygetwindow as gw
import time import time
import os import os
import subprocess import subprocess
@@ -48,6 +49,24 @@ class MumuEmulator:
if not self.is_running(): if not self.is_running():
subprocess.Popen(MUMU_EXE_PATH) subprocess.Popen(MUMU_EXE_PATH)
time.sleep(5) time.sleep(5)
self.bring_to_front()
def bring_to_front(self):
"""将模拟器窗口置顶"""
try:
windows = gw.getWindowsWithTitle("MuMu")
for window in windows:
if window.title:
window.activate()
window.restore()
time.sleep(0.5)
print(f"已将窗口置顶: {window.title}")
return True
print("未找到MuMu模拟器窗口")
return False
except Exception as e:
print(f"置顶窗口失败: {e}")
return False
def find_and_click(self, image_name, confidence=0.8, timeout=30): def find_and_click(self, image_name, confidence=0.8, timeout=30):
"""查找图片并点击""" """查找图片并点击"""
@@ -106,11 +125,11 @@ class MumuEmulator:
def run_sequence(self): def run_sequence(self):
"""按顺序执行任务""" """按顺序执行任务"""
self.bring_to_front()
time.sleep(30)
sequence = [ sequence = [
"start",
"running",
"web", "web",
"web.address", "web_address",
"web_goon", "web_goon",
"web_debug", "web_debug",
"web_debug_apk", "web_debug_apk",
@@ -121,16 +140,19 @@ class MumuEmulator:
] ]
for image_name in sequence: for image_name in sequence:
if image_name == "web.address": if image_name == "web_address":
if self.check_image_exists("web.address"): for _ in range(60):
if self.check_image_exists("web_address"):
time.sleep(1) time.sleep(1)
self.type_text(WEB_URL) self.type_text(WEB_URL)
time.sleep(0.5) time.sleep(0.5)
self.press_enter() self.press_enter()
break
time.sleep(1)
else: else:
print("web.address 图片不存在跳过输入URL") print("web_address 图片不存在跳过输入URL")
else: else:
self.find_and_click(image_name, timeout=10) self.find_and_click(image_name, timeout=60)
time.sleep(2) time.sleep(2)
def close(self): def close(self):

View File

@@ -3,17 +3,48 @@ Mumu模拟器测试
""" """
import pytest import pytest
import pyautogui
import os
import time
def test_start_emulator_sequence(emulator): def test_start_emulator_sequence(emulator):
"""测试模拟器启动和操作序列""" """测试模拟器启动和操作序列"""
assert not emulator.is_running(), "模拟器已启动" time.sleep(3)
emulator.start() emulator.start()
time.sleep(3)
script_dir = os.path.dirname(__file__)
start_image_path = os.path.join(script_dir, "start.png")
if not os.path.exists(start_image_path):
pytest.fail(f"start.png 图片文件不存在: {start_image_path}")
print("正在查找 start.png 图片...")
location = None
for _ in range(10):
try:
location = pyautogui.locateOnScreen(start_image_path, confidence=0.8)
if location:
break
except Exception:
pass
time.sleep(1)
if not location:
print("未找到 start.png退出 pytest")
pytest.exit("未在屏幕上找到 start.png 图片,测试终止")
center = pyautogui.center(location)
pyautogui.click(center)
print(f"已点击 start.png位置: {center}")
assert emulator.is_running(), "模拟器启动失败" assert emulator.is_running(), "模拟器启动失败"
def test_run_sequence(emulator): def test_run_sequence(emulator):
"""测试按顺序执行任务""" """测试按顺序执行任务"""
time.sleep(3)
if not emulator.is_running(): if not emulator.is_running():
pytest.skip("模拟器未运行") pytest.skip("模拟器未运行")
@@ -22,6 +53,7 @@ def test_run_sequence(emulator):
def test_close_emulator(emulator): def test_close_emulator(emulator):
"""测试关闭模拟器""" """测试关闭模拟器"""
time.sleep(3)
if emulator.is_running(): if emulator.is_running():
emulator.close() emulator.close()
assert not emulator.is_running(), "模拟器关闭失败" assert not emulator.is_running(), "模拟器关闭失败"