feat: 移除DDT模式的支持,改用POM模式
- 删除 data_loader 数据驱动加载器。 - 删除 test_keyword_sample 测试执行代码 - 新增 base_page [DDT模式极大的限制了灵活性,增加了代码的编写难度,另外项目使用者都会编码故而转用只针对POM模式进行优化]
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
@author: CNWei,ChenWei
|
||||
@Software: PyCharm
|
||||
@contact: t6g888@163.com
|
||||
@file: test_keyword_sample
|
||||
@date: 2026/1/23 17:48
|
||||
@desc:
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import logging
|
||||
from core.driver import CoreDriver
|
||||
from utils.data_loader import DataLoader
|
||||
from core.settings import BASE_DIR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 假设数据文件路径
|
||||
DATA_FILE = BASE_DIR / "test_cases" / "data" / "login_flow.xlsx"
|
||||
|
||||
|
||||
# 或者 "login_flow.yaml" —— 代码不需要改动,只需要改文件名
|
||||
|
||||
class TestKeywordDriven:
|
||||
|
||||
def run_step(self, driver: CoreDriver, step: dict):
|
||||
"""
|
||||
核心执行引擎:反射调用 CoreDriver 的方法
|
||||
"""
|
||||
action_name = step.get("action")
|
||||
if not action_name:
|
||||
return # 跳过无效行
|
||||
|
||||
# 1. 获取 CoreDriver 中对应的方法
|
||||
if not hasattr(driver, action_name):
|
||||
raise ValueError(f"CoreDriver 中未定义方法: {action_name}")
|
||||
|
||||
func = getattr(driver, action_name)
|
||||
|
||||
# 2. 准备参数
|
||||
# 你的 CoreDriver 方法签名通常是 (by, value, [args], timeout)
|
||||
# 我们从 step 字典中提取这些参数
|
||||
kwargs = {}
|
||||
if "by" in step and step["by"]: kwargs["by"] = step["by"]
|
||||
if "value" in step and step["value"]: kwargs["value"] = step["value"]
|
||||
|
||||
# 处理特殊参数,比如 input 方法需要的 text,或者 assert_text 需要的 expected_text
|
||||
# 这里做一个简单的映射,或者在 Excel 表头直接写对应参数名
|
||||
if "args" in step and step["args"]:
|
||||
# 假设 input 的第三个参数是 text,这里简单处理,实际可根据 func.__code__.co_varnames 动态匹配
|
||||
if action_name == "input":
|
||||
kwargs["text"] = str(step["args"]) # 确保 Excel 数字转字符串
|
||||
elif action_name == "assert_text":
|
||||
kwargs["expected_text"] = str(step["args"])
|
||||
elif action_name == "explicit_wait":
|
||||
# 支持你封装的 resolve_wait_method
|
||||
# 此时 method 参数就是 args 列的内容,例如 "toast_visible:成功"
|
||||
kwargs["method"] = step["args"]
|
||||
|
||||
logger.info(f"执行步骤 [{step.get('desc', '无描述')}]: {action_name} {kwargs}")
|
||||
|
||||
# 3. 执行调用
|
||||
func(**kwargs)
|
||||
|
||||
def test_execute_from_file(self, driver):
|
||||
"""
|
||||
主测试入口
|
||||
"""
|
||||
# 1. 加载数据 (自动识别 Excel/YAML)
|
||||
# 注意:实际使用时建议把 load 放在 pytest.mark.parametrize 里
|
||||
# 这里为了演示逻辑写在函数内
|
||||
if not DATA_FILE.exists():
|
||||
pytest.skip("数据文件不存在")
|
||||
|
||||
steps = DataLoader.load(DATA_FILE)
|
||||
|
||||
# 2. 遍历执行
|
||||
for step in steps:
|
||||
self.run_step(driver, step)
|
||||
78
test_cases/test_login_demo.py
Normal file
78
test_cases/test_login_demo.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
@desc: 模拟登录功能测试用例
|
||||
"""
|
||||
import pytest
|
||||
import logging
|
||||
from core.driver import CoreDriver
|
||||
from core.modules import AppPlatform
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class TestLogin:
|
||||
|
||||
driver: CoreDriver = None
|
||||
|
||||
def setup_method(self):
|
||||
"""
|
||||
每个测试用例开始前执行:初始化 Driver 并连接设备
|
||||
"""
|
||||
# 定义测试设备的 Capabilities
|
||||
# 注意:实际使用时,appPackage 和 appActivity 需要替换为被测 App 的真实值
|
||||
caps = {
|
||||
"platformName": "Android",
|
||||
"automationName": "UiAutomator2",
|
||||
"deviceName": "Android Emulator",
|
||||
"appPackage": "com.example.android.apis", # 替换为你的 App 包名
|
||||
"appActivity": ".ApiDemos", # 替换为你的 App 启动 Activity
|
||||
"noReset": True, # 不清除应用数据
|
||||
"newCommandTimeout": 60
|
||||
}
|
||||
|
||||
self.driver = CoreDriver()
|
||||
# 连接 Appium Server
|
||||
self.driver.server_config(host="127.0.0.1", port=4723)
|
||||
self.driver.connect(platform=AppPlatform.ANDROID, caps=caps)
|
||||
|
||||
def teardown_method(self):
|
||||
"""
|
||||
每个测试用例结束后执行:退出 Driver
|
||||
"""
|
||||
if self.driver:
|
||||
self.driver.quit()
|
||||
|
||||
def test_login_success(self):
|
||||
"""
|
||||
测试场景:使用正确的用户名和密码登录成功
|
||||
"""
|
||||
# 1. 定位元素信息 (建议后续抽离到 Page Object 层)
|
||||
# 假设登录页面的元素 ID 如下:
|
||||
input_user = "id:com.example.app:id/et_username"
|
||||
input_pass = "id:com.example.app:id/et_password"
|
||||
btn_login = "id:com.example.app:id/btn_login"
|
||||
txt_welcome = "xpath://*[@text='登录成功']"
|
||||
|
||||
# 2. 执行操作步骤
|
||||
# 显式等待并输入用户名
|
||||
self.driver.input(input_user, "", "test_user_001")
|
||||
|
||||
# 输入密码 (开启敏感模式,日志中脱敏)
|
||||
self.driver.input(input_pass, "", "Password123!", sensitive=True)
|
||||
|
||||
# 点击登录按钮
|
||||
self.driver.click(btn_login, "")
|
||||
|
||||
# 3. 断言结果
|
||||
# 方式 A: 检查特定文本是否存在
|
||||
# self.driver.assert_text(txt_welcome, "", "登录成功")
|
||||
|
||||
# 方式 B: 检查跳转后的页面元素是否可见
|
||||
is_login_success = self.driver.is_visible(txt_welcome, "")
|
||||
assert is_login_success, "登录失败:未检测到欢迎提示或主页元素"
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 允许直接运行此文件进行调试
|
||||
pytest.main(["-v", "-s", __file__])
|
||||
37
test_cases/test_settings.py
Normal file
37
test_cases/test_settings.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
@author: CNWei,ChenWei
|
||||
@Software: PyCharm
|
||||
@contact: t6g888@163.com
|
||||
@file: test_settings
|
||||
@date: 2026/1/16 15:56
|
||||
@desc:
|
||||
"""
|
||||
import logging
|
||||
|
||||
from utils.logger import trace_step
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@trace_step("验证失败",)
|
||||
def test_settings_page_display(driver):
|
||||
"""
|
||||
测试设置页面是否成功加载
|
||||
"""
|
||||
# 此时 driver 已经通过 fixture 完成了初始化
|
||||
current_act = driver.driver.current_activity
|
||||
logger.info(f"捕获到当前 Activity: {current_act}")
|
||||
|
||||
assert ".unihome.UniHomeLauncher" in current_act
|
||||
|
||||
|
||||
def test_wifi_entry_exists(driver):
|
||||
"""
|
||||
简单的元素查找示例
|
||||
"""
|
||||
# 这里的 driver 就是 appium.webdriver.Remote 实例
|
||||
# 假设我们要查找“网络”相关的 ID
|
||||
# el = driver.find_element(by='id', value='android:id/title')
|
||||
assert driver.driver.session_id is not None
|
||||
Reference in New Issue
Block a user