Page Object设计模式

Page Object 设计思想

Page Object 就是在测试中将操作细节和验证分开的

设计原则 一

只为页面中重要的元素创建page类

设计原则 二

如果页面A 导航到页面B, Page A 应当return Page B

Pg object

PO 六大原则

1、一个 public 方法代表一个公共的服务。就是说一个方法代替页面上的某个操作
2、PageObject 中的方法细节不可暴露在外,通过提供公共服务接口的形式提供给外部
3、一般不需要在 PageObject 中断言
4、当有页面跳转的操作时候,执行这个方法时应该在方法结束返回时能够跳转到另一个页面中
5、我们只需要对页面中我们需要的重要的内容进行封装
6、页面中相同的组件,但是不同的操作应该要被拆成不同的方法进行封装

面试提问

1、Page Object 设计思想解决的是什么样的问题

1、代码冗余明显降低:二次封装Selenium方法和提取公共方法,提高代码复用性
2、代码的阅读性明显提升:因为三层分级,将不同内容进行不同的封装,整体代码阅读性提升
3、代码维护性明显提升:UI测试中,页面若经常变动,代码的维护量随之增多;因为三层分级,我们只需要修改页面对象的代码,如元素对象或者操作对象的方法,不用修改测试用例的代码,也不影响测试用例的正常执行
4、降低代码耦合性

2、Page Object 编写的思路

一个添加成员的步骤
1、登录_login页面
2、登录之后进入首页_main页面
3、点击添加成员_main页面
4、填写添加信息_add_member页面
5、点击保存_add_member页面
6、返回通讯录_add_membe页面
7、加断言做验证_contact页面

实战时序图建立

项目开始前,建立自己的一个时序图,便于思考

https://plantuml.ceshiren.com/uml/IomjoSyhpKrABU8gI2mgoKpEB4ZCAr5uigVnoOvPJ_UjUx9_uPCTkryktFfoxYA5n6A5tCJCF2u55BKl6sQzMBV-wTwf25N9-NabYSabQ0gVRT_zj6l1iuu9JoVEGAXDJ4c9pqq5kq212sIyWguTp00hXUTDEv-sxuPRWQG4eG-c2jbye4Ae6KIfHIX0iVtfmjLFssP2LBtOsl9isiptUtgW9ZpPF_VfMZHxu1fVz6r_sZyDel5fn_GN2rSycxRsnOe60000

@startuml
autonumber
participant 企业微信主页面 as main
 
participant 通讯录页面 as contact
 
participant 添加成员页面 as add_member
 
main -> contact: 点击通讯录
 
main -> add_member: 点击添加成员
 
contact -> add_member: 点击添加成员
 
add_member -> contact: 填写成员资料, 点击保存
 
contact -> contact: 获取成员列表断言
@enduml

黄色长方形代表所在页面, 线代表在这个页面的操作,箭头代表跳转到哪个页面

实战练习

PO 构建图

1、返回值需要设置到箭头指向的地方

项目结构

注意:

在vscode中编辑,需要导入包的时候所有包结构下都要加入__init__.py

PO 文件夹为 构造PO模型

Po/base_function.py 基础的公用函数类,比如浏览器复用初始化

"""
公共函数类的继承
"""
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import TouchActions


class PoBase:
    # 添加baseurl, 可以支持测试用例的灵活配置起始页
    # basepage 完全和项目解耦
    _base_url = ""

    def __init__(self, base_driver=None):
        # base_driver, 解决链接不断初始化的过程
        if base_driver is None:
            self.chrome_options = Options()
            self.chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")  # 指定配置好的 chrom
            self.chrome_options.add_experimental_option("w3c", False)
            self.chrome_driver = r"D:\my_project\git_deve\development\python_work\test_selenium\chromedriver.exe"  # 驱动路径
            self.driver = webdriver.Chrome(self.chrome_driver, chrome_options=self.chrome_options)  # 加入驱动设置
            self.driver.get(self._base_url)  # 发起请求
            # self.driver.maximize_window()  # 设置为最大化
            self.driver.implicitly_wait(5)  # 添加一个隐式等待默认等待3秒
        else:
            self.driver = base_driver

    # 封装driver,以便于添加Logger
    def find_xpath(self, locator):
        return self.driver.find_element_by_xpath(locator)

    def finds_xpath(self, locator):
        return self.driver.find_elements_by_xpath(locator)

testcase/test_add_member.py 为测试用例得一个编写文件是入口文件

"""
根据业务需要添加断言
通过链式调用的方式更加方便的描述业务逻辑
"""

from page_object_base_demo.po.main_page import MainPage
import pytest


class TestAddMember:

    @pytest.mark.parametrize("name, id, phone", [('od', '123', '13111111111')])  # 参数化数据驱动
    def test_add_member(self, name, id, phone):
        main_page = MainPage()
        # 1.跳转到add_member页面
        # 2.做一个添加成员操作,然后点击保存 跳转到通讯录页面
        # 3。通讯录页面获取成员信息。作为断言
        assert "od" in main_page.goto_contact().goto_add_member().add_member(name, id, phone).get_member_list()

    @ pytest.mark.parametrize("name, id, phone", [('keke', '1235', '13111111112')])
    def test_index_member(self, name, id, phone):
        main_page = MainPage()
        # 1.直接从首页进入到添加成员
        # 2.做一个添加成员操作,然后点击保存 跳转到通讯录页面
        # 3。通讯录页面获取成员信息。作为断言
        assert "keke" in main_page.goto_add_member().add_member(name, id, phone).get_member_list()

PO / main_page.py 根据上面的时序图编写,为入口文件, 里面的方法即为时序图下的两个箭头过程

两个函数返回的值为箭头指向的,另一个类里面的过程。

"""
编码第一步,构造PO模型,实现设置为空
构造页面相关类和方法
黄色的方块代表一个类
每条线代表这个页面提供的操作
箭头的始端为开始页面
箭头的末端为跳转页面, 即对应方法需要返回跳转页面的实例对象
实现暂时实际为空
这是个主页面
"""

import time
from page_object_base_demo.po.base_function import PoBase


class MainPage(PoBase):
    _base_url = "https://work.weixin.qq.com/wework_admin/frame#index"
    contact = "//span[@class='frame_nav_item_title'][contains(text(),'通讯录')]"
    add_index = '//*[@id="_hmt_click"]/div[1]/div[4]/div[2]/a[1]/div/span[2]'

    def goto_contact(self):
        '''
        跳转到通讯页
        :return:ContactPage
        '''
        from page_object_base_demo.po.contact_page import ContactPage
        self.find_xpath(self.contact).click()
        return ContactPage(self.driver)

    def goto_add_member(self):
        '''
        跳转到添加成员页
        :return:
        '''
        from page_object_base_demo.po.add_member_page import AddMemberPage
        self.find_xpath(self.add_index).click()
        return AddMemberPage(self.driver)

PO / add_member_page.py 根据上面的时序图编写,为添加用户的两个过程办法:

'''
添加成员页面
'''


# from page_object_base_demo.po.contact_page import ContactPage


from page_object_base_demo.po.base_function import PoBase


class AddMemberPage(PoBase):
    user_name = '//*[@id="username"]'
    user_acctid = '//*[@id="memberAdd_acctid"]'
    user_phone = '//*[@id="memberAdd_phone"]'
    user_save = "//div[contains(@class,'member_edit')]//div[1]//a[2]"

    def add_member(self, name, id, phone):
        '''
        添加成员操作
        :return:
        '''
        from page_object_base_demo.po.contact_page import ContactPage
        print('添加成员')
        self.find_xpath(self.user_name).send_keys(name)
        self.find_xpath(self.user_acctid).send_keys(id)
        self.find_xpath(self.user_phone).send_keys(phone)
        self.find_xpath(self.user_save).click()
        return ContactPage(self.driver)

PO / contact_page.py 根据上面的时序图编写,跳转到通讯录页面的方法

'''
通讯录页面
'''

import time
from page_object_base_demo.po.base_function import PoBase


class ContactPage(PoBase):
    add_people = "//div[contains(@class,'js_operationBar_footer ww_operationBar')]//a[contains(@class,'qui_btn ww_btn js_add_member')][contains(text(),'添加成员')]"
    t_body = '//*[@id="member_list"]/tr'

    def goto_add_member(self):
        '''
        点击跳转添加成员页
        :return:
        '''
        time.sleep(1)
        from page_object_base_demo.po.add_member_page import AddMemberPage
        print('准备开始添加成员。。。')
        self.find_xpath(self.add_people).click()
        return AddMemberPage(self.driver)

    def get_member_list(self):
        '''
        获取成员列表
        :return: list
        '''
        time.sleep(1)
        print('获取成员列表')
        t_body = self.finds_xpath(self.t_body)
        name_list = []
        for i in t_body:
            tdList = i.find_elements_by_tag_name("td")
            name_list.append(tdList[1].text)
        print(name_list)
        return name_list

完。

posted @ 2021-07-05 18:16  陈科科  阅读(386)  评论(0编辑  收藏  举报