Python —— UI自动化之Page Object模式

1、Page Object模式简介

1、二层模型

Page Object Model(页面对象模型), 或者也可称之为POM。在UI自动化测试广泛使用的一种分层设计 模式。核心是通过页面层封装所有的页面元素及操作,测试用例层通过调用页面层操作组装业务逻辑。

1、实战

目录如下:

home_page.py文件的内容:

python 复制代码
from selenium.webdriver.common.by import By

class HomePage:
    login_link_locator = (By.LINK_TEXT, '登录')

    def click_login_link(self,driver):
        # 前面加*号是解包的操作,因为login_link_locator是元组
        driver.find_element(*self.login_link_locator).click()

login_page.py文件的内容:

python 复制代码
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By


class LoginPage:
    #属性->元素定位信息(元素定位方法+元素定位值)-元组类型
    phone_input_locator = (By.XPATH,'//input[@placeholder="请输入手机号/用户名"]')
    pwd_input_locator = (By.XPATH,'//input[@placeholder="请输入密码"]')
    login_button_locator = (By.CLASS_NAME,'login-button')

    #操作->元素行为,登录操作
    def page_login(self,driver,phone,pwd):
        # *self.phone_input_locator 前面的*号是解包使用的
        driver.find_element(*self.phone_input_locator).send_keys(phone)
        driver.find_element(*self.pwd_input_locator).send_keys(pwd)
        sleep(2)
        driver.find_element(*self.login_button_locator).click()

登录案例 - PO.py的文件内容:

python 复制代码
from selenium import webdriver
from d6_JavaScript处理.testcases.home_page import HomePage
from d6_JavaScript处理.testcases.login_page import LoginPage

driver = webdriver.Chrome()
driver.maximize_window()
driver.get("http://mall.banan.com:3344/")
HomePage().click_login_link(driver)

LoginPage().page_login(driver,"1723693766728","ydgcdlcb")

2、三层模型

基本的PO分层包括页面层与用例层,但是在编写页面层代码时会发现一个问题:页面中存在非常多相同 的操作方法,比如都需要等待元素、获取元素属性/文本信息、页面滚动等等,每个页面层类都需要有相 同的代码,代码存在非常多冗余。我们可以把这些**【公共】**的页面操作方法给提取出来放到一个类中进 行维护,其他的页面类共用该类中的操作方法即可(通过继承实现)。

UI自动化断言:
断言是自动化测试不可缺少的部分,当我们使用测试脚本对业务逻辑进行操作时,需要检查交互操作之 后结果的正确性,此时需要通过断言机制来进行验证。

Pytest测试框架内置丰富的断言,通过assert语句即可,常见的断言方法比较包括:

  • 比较相等
python 复制代码
assert a == b
  • 比较大小(大于/小于/大于等于/小于等于)
python 复制代码
assert a > b
  • 内容包含/内容不包含
python 复制代码
assert a in b
  • 验证表达式是否为真
python 复制代码
assert condition
UI自动化常见的断言条件包括:
  • 通过当前页面的URL地址

  • 通过当前页面的标题

  • 通过当前页面的提示文本信息

  • 通过当前页面的某些元素变化/显示

一句话总结:通过肉眼观察页面的变化检查

3、三层模型实战

思路:抽取一个公共类,这个类中可以写公共页面的属性和函数,也可以写每次都要使用的属性,从UI自动化的角度来看,浏览器驱动是每次执行用例都会用到的,所以也可以抽取出来。

三层模型的文件目录如下:

base_page.py文件中的内容:

python 复制代码
import os

from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

# BasePage 用来存放所有页面类的公共部分,一些公共的操作(显式封装)
class BasePage:
    def __init__(self,driver):
        self.driver = driver

    # 封装的意思:把相同类似的代码放到一个函数里面,重复使用
    # locator是元组的类型
    def wait_element_clickable(self, locator):
        web_element = WebDriverWait(self.driver,5,0.5).until(EC.element_to_be_clickable(locator))
        return web_element

    def wait_element_visible(self, locator):
        # web_element代表通过显示等待找到的元素
        web_element = WebDriverWait(self.driver,5,0.5).until(EC.visibility_of_element_located(locator))
        return web_element

    def wait_element_presence(self, locator):
        web_element = WebDriverWait(self.driver,5,0.5).until(EC.presence_of_all_elements_located(locator))
        return web_element

    # JavaScript进行元素的滚动
    def page_scroll(self,distance):
        self.driver.execute_script(f"document.documentElement.scrollTop={distance}")

    # JavaScript进行元素的点击
    def page_js_click(self,locator):
        element = self.wait_element_visible(self.driver,locator)
        self.driver.execute_script("arguments[0].click()",element)

    # JavaScript进行元素的移除
    def remove_element_attribute(self,locator,attribute):
        element = self.wait_element_visible(self.driver,locator)
        self.driver.execute_script(f"arguments[0].removeAttribute({attribute})", element)

    # 鼠标点击
    def mouse_click(self,locator):
        element = self.wait_element_visible(self.driver,locator)
        ActionChains(self.driver).click(element).perform()

    # 窗口切换
    def switch_to_window(self,title):
        handles = self.driver.window_handles
        for handle in handles:
            if self.driver.title == title:
                break
            else:
                self.driver.switch_to.window(handle)

    # 文件上传
    def update_file(self,filepath):
        os.system(f"test.exe {filepath}")

home_page.py文件中的内容:

python 复制代码
import time

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PO模式优化与自动化用例断言.common.base_page import BasePage


class HomePage(BasePage):
    # 属性-登录链接
    login_link_locator = (By.LINK_TEXT,'登录')
    # 欢迎提示信息
    welcome_tips_locator = (By.XPATH,'//span[text()="欢迎来到天使的世界"]')
    welcome_tips_text_locator = (By.XPATH,'//span[@class="text"]')
    # 用户名
    username_text_locator = (By.XPATH,'//a[@class="link-name"]')

    def click_login_link(self):
        self.wait_element_clickable(self.login_link_locator).click()

    def is_display_welcome_tips(self):
        time.sleep(2)
        # return  self.wait_element_clickable(self.welcome_tips_locator).is_displayed()
        return self.wait_element_clickable(self.welcome_tips_text_locator).text

    def get_username_text(self):
        return self.wait_element_visible(self.username_text_locator).text

login_page.py文件中的内容:

python 复制代码
import time

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PO模式优化与自动化用例断言.common.base_page import BasePage


class LoginPage(BasePage):
    # 属性->元素定位信息(元素定位方法+元素定位值)-元组类型
    phone_input_locator = (By.XPATH, '//input[@placeholder="请输入手机号/用户名"]')
    pwd_input_locator = (By.XPATH, '//input[@placeholder="请输入密码"]')
    login_button_locator = (By.CLASS_NAME, 'login-button')
    login_tips_locator = (By.XPATH, '//p[@class="el-message__content"]')

    def login(self,phone,pwd):
        # 因为继承了 basepage类,所以克不用再写driver参数
        # self.wait_element_visible(self.driver,self.phone_input_locator).send_keys("17728373518")
        self.wait_element_visible(self.phone_input_locator).send_keys(phone)
        self.wait_element_visible(self.pwd_input_locator).send_keys(pwd)
        self.wait_element_clickable(self.login_button_locator).click()
        time.sleep(3)

    def get_login_tips(self):
        # 这里比较文本内容
        return self.wait_element_visible(self.login_tips_locator).text
        print(self.wait_element_visible(self.login_tips_locator).text)

test_login.py文件中的内容

python 复制代码
from selenium import webdriver
from d7_PO模式优化与自动化用例断言.pageobjects.home_page import HomePage
from d7_PO模式优化与自动化用例断言.pageobjects.login_page import LoginPage

def test_login_success():
    driver = webdriver.Chrome()
    driver.get("http://mall.banan.com:3344/")
    driver.maximize_window()
    # 点击首页的登录操作
    homepage = HomePage(driver)
    homepage.click_login_link()
    # 在登陆页面进行登录操作
    loginpage = LoginPage(driver)
    loginpage.login("le_auto","le123456")
    # 断言检测测试是否成功(通过预期结果和实际结果的比较)
    #  检查点:1、欢迎页,如果函数的返回值为True,那么断言之后返回是通过的
    assert homepage.is_display_welcome_tips()
    # assert loginpage.get_login_tips() == '账号或者密码不正确'
    assert homepage.get_username_text() == 'lemon_auto'


# 对登录结果的断言
def test_login_uncorrect_username():
    driver = webdriver.Chrome()
    driver.get("http://mall.banan.com:3344/")
    homepage = HomePage(driver)
    homepage.click_login_link()
    # 在登陆页面进行登录操作
    loginpage = LoginPage(driver)
    loginpage.login("le_auto1", "le123456")
    # 页面登录过程中的提示信息断言
    assert loginpage.get_login_tips() == '账号或密码不正确'

2、PO模式的优点

1、提高测试用例的可读性

2、提高测试用例可维护性

3、减少代码重复

相关推荐
荣--21 小时前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森21 小时前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜2 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB3 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode4 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220705 天前
如何搭建本地yum源(上)
运维
大树888 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠8 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质8 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工8 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信