selenium学习笔记:<18>PO模式

本文为霍格沃兹测试开发学社的学习经历分享,写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~

说明:本篇博客基于selenium 4.1.0

什么是PO模式

PO模式的全称page object model(POM),有时候也叫做page object pattern。由马丁福勒提出,受到了selenium自动化测试框架的大力推广,因而成为一种非常主流的自动化测试设计模式
PO模式是在UI自动化测试过程当中使用非常频繁的一种设计模式,使用这种模式,可以有效的提升代码的复用能力,并且让自动化测试代码维护起来更加方便


PO模式设计

在PO模式中,使用类来表示每个UI页面,类中的属性表示页面的信息,类中的方法表示页面的行为和操作

在将页面抽象化为类时,需要注意PO模式的设计原则

  • 对于属性
    • 不要暴露页面内部的元素给外部
    • 不需要建模 UI 内的所有元素
  • 对于方法
    • 对于方法:用公共方法代表 UI 所提供的功能
    • 方法应该返回其他的 PageObject 或者返回用于断言的数据
    • 同样的行为不同的结果可以建模为不同的方法
    • 不要在方法内加断言

image


PO模式优点

  • 便于维护:每个页面对应一个类文件,前端页面被修改后只需要维护对应的类文件,不会影响其他代码,符合单一职责原则
  • 便于阅读:页面的操作都被以函数的形式封装起来了。函数名就具备注释的作用,其他人阅读代码时可以通过函数了解业务
  • 便于使用:编写用例时只需要关注测试步骤和测试结果。用例与用例间的相同操作,不用重复编写

代码示例

import yaml
import os
from selenium import webdriver
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


class PageBase:
    """
    页面基类:处理页面公共业务、存放公共方法
    """
    def __init__(self, driver: WebDriver):
        self.driver = driver


class PageLogin(PageBase):
    """
    登录页面
    """
    # 页面url地址
    _page_url = "https://work.weixin.qq.com/wework_admin/loginpage_wx"

    def goto_index(self):
        """
        跳转到首页
        :return:
        """
        self.driver.get(self._page_url)

        if not os.path.exists("resource"):
            os.mkdir("resource")

        if os.path.exists("resource/cookie.yaml"):
            cookie = yaml.safe_load(open("resource/cookie.yaml"))
            for i in cookie:
                self.driver.add_cookie(i)
            self.driver.get("https://work.weixin.qq.com/wework_admin/frame#index")
        else:
            # 扫码登录,等待页面加载
            WebDriverWait(self.driver, 60).until(expected_conditions.url_contains("wework_admin/frame"))

            # 存入cookie
            cookie = self.driver.get_cookies()
            with open("resource/cookie.yaml", "w") as f:
                yaml.safe_dump(cookie, f)

        # 返回首页实例对象
        return PageIndex(self.driver)


class PageIndex(PageBase):
    """
    首页
    """
    # 页面url地址
    _page_url = "https://work.weixin.qq.com/wework_admin/frame#index"

    # 页面元素定位
    _path_contact = (By.ID, "menu_contacts")
    _path_addmember = (By.CSS_SELECTOR, '.ww_indexImg_AddMember')

    def goto_addmember(self):
        """
        跳转添加成员页面
        :return:
        """
        self.driver.find_element(*self._path_addmember).click()

        # 返回添加成员页面的实例对象
        return PageAddmember(self.driver)


class PageAddmember(PageBase):
    """
    添加成员页面
    """
    # 页面url地址
    _page_url = "https://work.weixin.qq.com/wework_admin/frame#contacts"

    # 页面元素定位
    _path_name = (By.ID, "username")
    _path_id = (By.ID, "memberAdd_acctid")
    _path_tel = (By.ID, "memberAdd_phone")
    _path_finish = (By.CSS_SELECTOR, ".js_btn_save")
    _path_tips = (By.CSS_SELECTOR, ".ww_inputWithTips_WithErr")

    def add_member_success(self, member_name, member_id, member_tel):
        """
        添加成员成功,跳转回通讯录页面
        :param member_name: 姓名
        :param member_id: 账号
        :param member_tel: 手机
        :return: 通讯录页面页面
        """
        self.driver.find_element(*self._path_name).send_keys(member_name)
        self.driver.find_element(*self._path_id).send_keys(member_id)
        self.driver.find_element(*self._path_tel).send_keys(member_tel)
        self.driver.find_element(*self._path_finish).click()

        # 返回页面通讯录页面实例对象
        return PageContact(self.driver)


class PageContact(PageBase):
    """
    通讯录页面
    """
    pass


# 测试用例
class TestCookie:
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(3)

    def teardown(self):
        self.driver.quit()

    # 一个简单的添加成员用例
    def test_addmember_success(self):
        data = ('llbai', '9527', '13122223333')

        # 基于已有的页面类和方法,可以快速实现登录-进入页面-进入添加成员页面-添加成员
        PageLogin(self.driver).goto_index().goto_addmember().add_member_success(*data)

更多文章推荐:
只需Docker,环境问题再也不是测开路上的『坑』_霍格沃兹测试开发学社的博客-CSDN博客

1 个赞

老师,请教下,为什么每个函数都返回一个示例对象,比如添加成员这个函数,它返回的具体信息是什么,返回的信息又用来做什么

返回的是执行结果:
如果是页面切换的函数,就应该返回目标页面的实例化类。比如进入主页,那就返回主页的对象
如果是业务操作的函数,就应该返回操作的结果。比如添加完成员,就可以去成员页面获取成员列表

这样的好处是,写用例时会符合自然操作逻辑。比如这个用例就是从完成了登录页-主页-添加成员页-添加成员的操作,后续还可以继续获取成员列表,校验执行结果

PageLogin(self.driver).goto_index().goto_addmember().add_member_success(*data)

最后,我也是一个普通同学哈 :ghost: