更新mumu-pytest测试框架,修复build.gradle.kts依赖问题
This commit is contained in:
@@ -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)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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>
|
|
||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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. 测试图片文件名不要包含空格,使用下划线分隔
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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(), "模拟器关闭失败"
|
||||||
|
|||||||
Reference in New Issue
Block a user