Sparkle CodesSparkle
项目 / APP测试

Appium 登录自动化:Page、Object 与 Module 的拆分实践

x
xpx
Mar 09, 2025
Editorial Insight
#Appium#POM#测试开发

Appium 登录自动化:Page、Object 与 Module 的拆分实践

系列导航

上一篇:Appium 自动化入门:为什么要从线性脚本走向 POM 分层 入口页:用 POM 分层、关键字封装和数据驱动搭建 Appium 自动化测试框架 下一篇:Appium 自动化数据驱动:Excel、Pytest 参数化与断言分流

上一篇讲的是为什么要拆,这一篇直接看怎么拆。

一个真实的登录流程,只要稍微复杂一点,就至少会包含这三层结构:

  • 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 分层最核心的价值:页面、动作、流程分开后,代码的可读性和复用性都会明显上来。

继续阅读:Appium 自动化数据驱动:Excel、Pytest 参数化与断言分流

BACK TO BLOG
The End of Interaction