Page Object 模式

分层模型:

一个页面分为三层:对象库层,操作层,业务层

对象库层:封装定位元素的方法

操作层:封装对元素的操作

业务层:将一个或多个操作组合起来完成一个业务功能

六大原则:

1、The public methods represent the services that the page offers 公共方法表示页面提供的服务

使用基类(BasePage)封装页面中要使用的方法,作为公共方法,其他页面继承基类的方法。

2、Try not to expose the internals of the page 不要暴露页面细节

如元素 、元素的定位方法等,只提供接口(方法)供测试用例使用,测试用例和页面对象(操作细节)相隔离。

3、Generally don't make assertions Page 不要在方法内加断言

断言相关的测试代码应该写在测试用例类中,不要写在页面中。

4、Methods return other PageObjects 方法返回其他页面对象

使用方法进行页面之间的关联、跳转。通常页面的方法返回其他页面或者需要断言的数据。

5、Need not represent an entire page 不需要代表整个页面

只需要对页面中需要使用的元素进行封装即可,不需要封装整个页面的内容。

6、Different results for the same action are modeled as different methods 同一动作的不同结果被建模为不同的方法

相同的操作带来的不同的结果可以封装成不同的方法。

使用

  1. 梳理测试用例:页面和页面的交互关系;编写时序图进行梳理
  2. 构造PO模型:构造页面相关的类和方法;实现暂时设置为空
  3. 编写测试用例:根据业务逻辑进行编写;链式调用——方法返回页面对象的实例
  4. 填充具体实现:元素定位;等待;base_page——避免重复实例化
  5. 优化用例:解决封装样板代码,解决技术栈更换问题;页面元素提取出来,增强复用性;对比PO六大原则

示例一:首页点击登录,输入账号密码后登录

base_page

from selenium import webdriver


class BasePage:
    def __init__(self, base_driver=None):
        if base_driver is not None:
            self.driver = base_driver
        else:
            self.driver = webdriver.Chrome()
            self.driver.get("https://testerhome.com/")
            self.driver.implicitly_wait(3)
            self.driver.maximize_window()

    def find(self, by, locator):
        return self.driver.find_element(by, locator)

main_page

from selenium.webdriver.common.by import By

from test_web.page.base_page import BasePage
from test_web.page.login_page import LoginPage


class MainPage(BasePage):
    def goto_login(self):
        '''
        跳转到登录页
        :return:
        '''
        self.find(By.XPATH, '//*[@id="main"]/section/div/p[3]/a[2]').click()
        return LoginPage(self.driver)

    def goto_register(self):
        pass

login_page

from selenium.webdriver.common.by import By

from test_web.page.base_page import BasePage


class LoginPage(BasePage):
    def login(self):
        '''
        登录:1.输入用户名;2.输入密码;3.点击登录;4.点击登录后的头像;5.获取登录后的用户名
        :return:
        '''
        self.find(By.ID, 'user_login').send_keys("cherishgf")
        self.find(By.ID, 'user_password').send_keys("********")
        self.find(By.XPATH, '//*[@id="new_user"]/div[4]/input').click()
        self.find(By.ID, 'navbar-user-menu').click()
        ele = self.find(By.XPATH, '/html/body/div[1]/div/div[3]/ul/li[3]/div/a[1]')
        return ele.text

test_login

import time

from test_web.page.main_page import MainPage


class TestLogin:
    def test_login(self):
        '''
        测试登录功能
        :return:
        '''
        self.main = MainPage()

        login_name = self.main.goto_login().login()
        assert login_name == 'cherishgf'

结果

示例二:首页点击登录,测试用例1账号密码错误,测试用例2密码为空

base_page

# 定义一个对象库层的基类
from selenium.webdriver.support.wait import WebDriverWait

from test_po.utils import UtilsDriver


class BasePage:
    def __init__(self):
        print("基类的")
        self.driver = UtilsDriver.get_driver()

    # 定义获取元素对象的方法
    def get_element(self, location):
        wait = WebDriverWait(self.driver, 10, 1)
        element = wait.until(lambda x: x.find_element(*location))
        return element


# 定义操作层的基类
class BaseHandle:
    # 定义针对元素的输入操作方法
    def input_text(self, element, text):
        """
        :param element: 表示的是元素对象
        :param text:  表示的是要输入的内容
        :return:
        """
        element.clear()
        element.send_keys(text)

main_page

from selenium.webdriver.common.by import By

from test_po.base.base_page import BasePage, BaseHandle


# 对象库层  主要用来定位元素
class MainPage(BasePage):
    def __init__(self):
        super().__init__()
        self.login_btn = By.XPATH, '//*[@id="main"]/section/div/p[3]/a[2]'  # 登录按钮

    def find_login_btn(self):
        # 登录按钮
        return self.get_element(self.login_btn)


# 操作层
class MainHandle(BaseHandle):
    def __init__(self):
        # 实例化对象库层
        self.home_page = MainPage()

    # 点击登录按钮,跳转到登录页面
    def click_login_bin(self):
        self.home_page.find_login_btn().click()


# 业务层
class MainProxy:
    def __init__(self):
        # 实例化操作层对象
        self.home_handle = MainHandle()

    # 点击登录按钮跳转到登录页面
    def go_login_page(self):
        self.home_handle.click_login_bin()

login_page

# 表示的登录页面对象

# 对象库层
from selenium.webdriver.common.by import By

from test_po.base.base_page import BasePage, BaseHandle


class LoginPage(BasePage):
    def __init__(self):
        super().__init__()
        self.username = By.ID, "user_login"  # 用户名输入框
        self.password = By.ID, "user_password"  # 密码输入框
        self.login_btn = By.XPATH, '//*[@id="new_user"]/div[4]/input'  # 表示的是登录按钮

    # 用户名输入框
    def find_username(self):
        return self.get_element(self.username)

    # 密码输入框
    def find_password(self):
        return self.get_element(self.password)

    # 登陆按钮
    def find_login(self):
        return self.get_element(self.login_btn)


# 操作层
class LoginHandle(BaseHandle):
    def __init__(self):
        self.login_page = LoginPage()

    # 输入用户名
    def input_username(self, username):
        self.input_text(self.login_page.find_username(), username)

    # 输入密码
    def input_password(self, password):
        self.input_text(self.login_page.find_password(), password)

    # 点击登录按钮
    def click_login(self):
        self.login_page.find_login().click()


# 业务层
class LoginProxy:
    def __init__(self):
        self.login_handle = LoginHandle()

    # 实现登录功能
    def login(self, username, password):
        # 输入用户名
        self.login_handle.input_username(username)
        # 输入密码
        self.login_handle.input_password(password)
        # 点击登录按钮
        self.login_handle.click_login()

utils

# 定义一个工具类
import time

from selenium import webdriver

from selenium.webdriver.common.by import By


class UtilsDriver:
    _driver = None

    # 定义一个获取浏览器驱动对象的方法
    @classmethod  # 表示这个方法是一个类级别的方法
    def get_driver(cls):
        if cls._driver is None:  # 判断driver是否已创建,如果为None就需要去创建浏览器驱动对象,如果不为None,则直接返回driver
            cls._driver = webdriver.Chrome()
            cls._driver.maximize_window()
            cls._driver.implicitly_wait(10)
            cls._driver.get("https://testerhome.com/")
        return cls._driver

    # 定义一个退出浏览器驱动对象的方法  浏览器驱动对象退出之后,还有一串效的字符串存在
    @classmethod
    def quit_driver(cls):
        if cls._driver is not None:
            cls.get_driver().quit()
            cls._driver = None


# 定义一个获取弹出框消息的方法
def get_msg():
    time.sleep(1)
    return UtilsDriver.get_driver().find_element(By.XPATH, '//*[@id="main"]/div[1]').text

test_login

# 定义测试类

from test_po.page.login_page import LoginProxy
from test_po.page.main_page import MainProxy
from test_po.utils import UtilsDriver, get_msg


class TestLogin:
    def setup_class(self):
        # 实例化首页和登录的业务对象
        self.main_proxy = MainProxy()
        self.login_proxy = LoginProxy()

        # 通过首页页面的业务层操作实现登录的跳转
        self.main_proxy.go_login_page()

    # 创建方法级别的fixture初始化的操作方法
    def setup(self):
        UtilsDriver.get_driver().refresh()

    # 创建类级别fixture销毁的操作方法
    def teardown_class(self):
        UtilsDriver.quit_driver()

    # 定义测试方法  账号不存在
    def test_login_01(self):
        self.login_proxy.login("13333337777", "123456")
        msg = get_msg()
        assert "账号或密码错误,请重试。" in msg

    # 定义测试方法  密码错误
    def test_login_02(self):
        self.login_proxy.login("cherishgf", "123456")
        # 获取提示信息
        msg = get_msg()
        assert "账号或密码错误。" in msg

    # 定义测试方法  用户名为空
    def test_login_03(self):
        self.login_proxy.login(" ", "123456")
        # 获取提示信息
        msg = get_msg()
        assert "继续操作前请注册或者登录。" in msg

结构

结果:1个通过,两个assert错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值