Appium 登录自动化:Page、Object 与 Module 的拆分实践
系列导航
上一篇讲的是为什么要拆,这一篇直接看怎么拆。
一个真实的登录流程,只要稍微复杂一点,就至少会包含这三层结构:
- Page:界面元素定位;
- Object:关键字或步骤函数;
- Module:测试流程和断言。
第一层:Page 只负责元素
在登录场景里,Page 层最重要的任务就是维护元素定位,比如:
PYTHON
from appium.webdriver.common.appiumby import AppiumBy
tip_commit = (AppiumBy.ID, 'com.tal.kaoyan:id/tip_commit')
tv_ok = (AppiumBy.ID, 'com.tal.kaoyan:id/tv_ok')
loginRegistorcodeAndPassword = (
AppiumBy.ID,
'com.tal.kaoyan:id/loginRegistorcodeAndPassword'
)
loginTreatyCheckboxPassword = (
AppiumBy.ID,
'com.tal.kaoyan:id/loginTreatyCheckboxPassword'
)
loginEmailEdittext = (AppiumBy.ID, 'com.tal.kaoyan:id/loginEmailEdittext')
loginPasswordEdittext = (AppiumBy.ID, 'com.tal.kaoyan:id/loginPasswordEdittext')
loginLoginBtn = (AppiumBy.ID, 'com.tal.kaoyan:id/loginLoginBtn')
loginCodeLoginBtn = (AppiumBy.ID, 'com.tal.kaoyan:id/loginCodeLoginBtn')
kylogin_perfect_tag_jump_button = (
AppiumBy.ID,
'com.tal.kaoyan:id/kylogin_perfect_tag_jump_button'
)
widgetToast = (AppiumBy.XPATH, '/hierarchy/android.widget.Toast')
为什么这一层值得单独放
- 登录页有哪些元素,一眼就能看清;
- 页面变动时,定位不需要满项目查找替换;
- 成功断言和失败断言依赖的元素也能统一管理;
- 逻辑代码不会被定位细节淹没。
Note
在这套思路里,Page 层的重点不是“写很多类”,而是先把定位集中起来。
第二层:Object 把动作和流程封成关键字
Object 层负责把测试动作和业务步骤封装起来。
先封基础动作
PYTHON
import allure
from selenium.webdriver.support.wait import WebDriverWait
@allure.title('单击元素')
@allure.step('单击元素')
def click_ele(driver, place):
driver.find_element(*place).click()
@allure.title('输入内容')
@allure.step('输入内容')
def input_text(driver, place, atext):
driver.find_element(*place).send_keys(atext)
@allure.title('通过定位得到元素')
@allure.step('通过定位得到元素')
def get_element(driver, place):
return driver.find_element(*place)
这些基础函数的意义在于统一常见操作,让测试代码不需要每次都从 driver.find_element() 开始写。
再封完整登录流程
PYTHON
def kw_login_operational_steps(driver, usrname, pw, **kwargs):
tip_commit = kwargs.get('tip_commit')
tv_ok = kwargs.get('tv_ok')
loginRegistorcodeAndPassword = kwargs.get('loginRegistorcodeAndPassword')
loginTreatyCheckboxPassword = kwargs.get('loginTreatyCheckboxPassword')
loginEmailEdittext = kwargs.get('loginEmailEdittext')
loginPasswordEdittext = kwargs.get('loginPasswordEdittext')
loginLoginBtn = kwargs.get('loginLoginBtn')
click_ele(driver, tip_commit)
click_ele(driver, tv_ok)
click_ele(driver, loginRegistorcodeAndPassword)
click_ele(driver, loginTreatyCheckboxPassword)
input_text(driver, loginEmailEdittext, usrname)
input_text(driver, loginPasswordEdittext, pw)
click_ele(driver, loginLoginBtn)
还可以封局部前置步骤
PYTHON
def partial_oparetion_step_login(driver, **kwargs):
tip_commit = kwargs.get('tip_commit')
tv_ok = kwargs.get('tv_ok')
loginRegistorcodeAndPassword = kwargs.get('loginRegistorcodeAndPassword')
loginTreatyCheckboxPassword = kwargs.get('loginTreatyCheckboxPassword')
click_ele(driver, tip_commit)
click_ele(driver, tv_ok)
click_ele(driver, loginRegistorcodeAndPassword)
click_ele(driver, loginTreatyCheckboxPassword)
这种局部封装适合:
- 只验证按钮置灰;
- 只验证表单校验;
- 多个用例共享相同前置步骤。
等待为什么也应该进 Object 层
PYTHON
def wait_element(driver, place):
ele = WebDriverWait(driver, 10, 0.5).until(
lambda x: x.find_element(*place)
)
return ele
移动端自动化里,很多失败其实不是逻辑问题,而是时机问题。把等待做成可复用函数,是后续提升稳定性的基础。
第三层:Module 组织测试流程
到了 Module 层,测试代码的任务就收敛成两件事:
- 组织操作步骤;
- 完成断言。
例如登录成功场景:
PYTHON
@allure.title('登录成功测试')
def test_login_success(abcd):
driver = abcd
with allure.step('登录操作步骤'):
click_ele(driver, tip_commit)
click_ele(driver, tv_ok)
click_ele(driver, loginRegistorcodeAndPassword)
click_ele(driver, loginTreatyCheckboxPassword)
input_text(driver, loginEmailEdittext, '15361088123')
input_text(driver, loginPasswordEdittext, '!abcd1234')
click_ele(driver, loginLoginBtn)
with allure.step('登录成功的断言'):
result = get_element(driver, kylogin_perfect_tag_jump_button).text
assert result == '跳过'
这里最重要的认知是:
- 测试用例不是“把所有动作都堆起来”;
- 它本质上是“让页面进入某种状态,然后验证结果”;
- 分层的价值就是让步骤和断言都更清楚。
拆开之后,直接得到什么
- 页面元素集中管理;
- 登录流程可复用;
- 断言逻辑更容易单独扩展;
- 以后接 Excel、YAML、Allure 时不用推倒重来。
还可以继续优化什么
当前这套示例已经能体现分层思路,但如果继续往工程化走,还可以再优化两点:
kwargs.get()太多时,可以考虑更清晰的参数对象;- 页面元素多起来后,可以按页面或功能再细分模块。
小结
如果只看登录案例,这套结构已经足够说明 POM 分层最核心的价值:页面、动作、流程分开后,代码的可读性和复用性都会明显上来。