POM设计模式

一、什么是POM

POM(page object model) 页面对象模型 WEB自动化测试框架应用最为广泛的一种框架设计模式。

设计思路:web项目由很多页面组成,把每个页面当做页面对象来进行设计

Python专题:什么是对象?通过类描述一组对象 对象=属性+方法
电商项目 = n个页面 = 对每个页面设计对应页面类 = 相同属性 + 相同的方法
class LoginPage:
  #属性? 元素、页面标题...
  #方法? 页面进行操作/行为:点击、定位、输入...等等元素及页面的操作

二、如何基于POM进行自动化框架架构?

每个页面有相同的属性及方法 比如:点击、输入、元素定位
基于POM进行设计分为四层进行架构:
  第一层: basepage层 每个页面有相同的属性及方法 比如:点击、输入、元素定位
  第二层: pageobjects层 针对每个页面定义页面类 每个页面有独有的属性及方法:
    登录页面 LoginPage类
    注册页面 RegisterPage类
  第三层: TestCases层 用例层包含项目的业务流程
  第四层: TestData 测试数据

2.1 base层封装

# base/basepage.py


import os
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.logger import FrameLog as  log
from common import dir_config as Dir


class BasePage:
    """
    BasePage:定义每个页面的相同属性及方法
    相同属性?获取浏览器驱动对象(数据)
    相同方法?元素定位、点击、输入...等等操作
    日志
    错误截图
    测试报告
    """

    def __init__(self, driver):
        self.driver = driver

    def locator(self, page_name, loc, ):
        """元素定位"""
        # WebElement对象
        try:
            el = self.driver.find_element(*loc)
            log().getLogger().info(f"在[{page_name}],定位到元素:{loc}")

        except:
            log().getLogger().error(f"在[{page_name}],未定位到元素:{loc}!!!")
            # 失败截图
            self.sava_page_shot(page_name)
            raise
        return el

    # 等待元素存在
    def wait_ele_visibility(self, page_name, loc, timeout=15, poll_fre=0.5):
        try:
            WebDriverWait(self.driver, timeout, poll_fre).until(EC.visibility_of_element_located(loc))
            # WebElement对象
            log().getLogger().info(f"在[{page_name}],找到元素:{loc}可见")
        except:
            log().getLogger().error(f"在[{page_name}],未找到元素:{loc}可见!!!")

    # 等待元素可见
    def wait_ele_presence(self, page_name, loc, timeout=15, poll_fre=0.5):
        try:
            WebDriverWait(self.driver, timeout, poll_fre).until(EC.presence_of_element_located(loc))
            log().getLogger().info(f"在[{page_name}],找到元素:{loc}存在")
        except:
            log().getLogger().error(f"在[{page_name}],未找到元素:{loc}存在!!!")

    def input(self, page_name, loc, value):
        """输入"""
        # 等待元素可见
        try:
            self.wait_ele_visibility(page_name, loc)
            self.locator(page_name, loc).send_keys(value)
            log().getLogger().info(f"在{page_name},元素{loc}输入:{value}!")

        except:
            log().getLogger().error(f"在{page_name},元素{loc}输入失败!")
            # 失败截图
            self.sava_page_shot(page_name)

    def click(self, page_name, loc):
        """点击"""
        try:
            # 等待元素可见
            self.wait_ele_visibility(page_name, loc)
            self.locator(page_name, loc).click()
            log().getLogger().info(f"在[{page_name}],点击元素{loc}成功!")
        except:
            log().getLogger().error(f"在[{page_name}],元素{loc}点击失败!")
            # 失败截图
            self.sava_page_shot(page_name)

    def sleep(self, s):
        time.sleep(s)

    # 鼠标键盘操作
    def move_element(self, page_name, loc):
        try:
            # 等待元素可见
            self.wait_ele_visibility(page_name, loc)
            ActionChains(self.driver).move_to_element(self.locator(page_name, loc)).perform()
            log().getLogger().info(f"在[{page_name}],鼠标移动到元素:{loc}")
        except:
            log().getLogger().error(f"在[{page_name}],鼠标移动到元素{loc}失败!")
            # 失败截图
            self.sava_page_shot(page_name)

    def sava_page_shot(self, img_name):
        filename = os.path.join(Dir.screen_dir, img_name + '.png')
        self.driver.save_screenshot(filename)
        log().getLogger().info(f"失败截图,截取当前网页,存储的路径:{filename}")

# 日志+错误截图+测试报告输出

2.2 pageobjects层封装

# pageobjects/goodspay_page.py


import time

from selenium.webdriver.common.by import By

from base.basepage import BasePage
from pagelocations.goodspay_page_locs import GoodsPayLoc  as Locs


class GoodspayPage(BasePage):
    """
    GoodpayPage:支付页面
    """

    def select_goods(self, goodsname):
        # 输入搜索内容
        self.input("支付页面", Locs.selectcontent_loc, goodsname)
        # 点击搜索
        self.click("支付页面", Locs.select_loc)
        self.sleep(1)

    # 加入购物车
    def gointocart(self):
        # 鼠标移动到商品
        self.move_element("支付页面", Locs.goods_loc)
        # 点击购物车
        self.click("支付页面", Locs.addgoodscart_loc)
        time.sleep(1)

    # 结算商品
    def paygoods(self):
        # 鼠标移动到购物车结算菜单
        self.move_element("支付页面", Locs.mycartmenu_loc)
        # 点击结算按钮
        self.click("支付页面", Locs.allprice_loc)
        self.sleep(1)

    def inputpasswd(self, passwd):
        # 核对购物车商品
        self.click("支付页面", Locs.nextsubmit_loc)
        self.sleep(1)
        # 输入支付密码
        self.input("支付页面", Locs.paypasswd_loc, passwd)
        # 点击使用
        self.click("支付页面", Locs.userpaypasswd_loc)

    def paymoney(self):
        self.click("支付页面", Locs.submit_loc)
        print("完成支付")

    # 获取支付的状态的信息
    def paystateMsg(self):
        print("取支付的状态的信息")
        el = self.locator("支付页面", Locs.paystate_loc)
        return el.text
# pageobjects/login_page.py


import time
from selenium.webdriver.common.by import By
from base.basepage import BasePage
from pagelocations.login_page_locs import LoginPageLoc as Locs
from testdatas import Globle_Datas as  GD


class LoginPage(BasePage):
    """
    登录页面类=页面独有的属性及方法
    页面独有的属性:页面元素定位
    方法:登录页面的操作
    """

    # url="http://47.107.116.139/shopnc/shop/index.php?act=login&op=index"

    # 方法
    def login(self, usname, passwd):
        # 实现登录的步骤
        self.driver.get(GD.login_url)
        self.sleep(2)
        # 输入用户名
        self.input(loc=Locs.el_username, value=usname, page_name="登录页面")
        # 输入密码
        self.input(loc=Locs.el_password, value=passwd, page_name="登录页面")
        # 点击登录
        self.click(loc=Locs.el_login, page_name="登录页面")
        time.sleep(5)

    def back_login(self):
        self.driver.get("")
        el_username = self.driver.find_element()
        el_passwd = self.driver.find_element()
        el_username.send_keys("admin")
        el_passwd.send_keys("msjy123")
        self.driver.get_cookies()
        # 输入验证码
        time.sleep(8)
        # 手工输入验证码
        # 点击登录
        el_login = self.driver.find_element()
        el_login.click()
        self.driver.get_cookies()
        # name/value  添加cookies

        self.driver.get_cookies()

    #     绕过验证码登录
    def login_nousername(self):
        self.driver.get("登录地址")
        # 获取成功登录后的cook信息进行代码添加
        self.driver.add_cookie()
        # self.driver.get(首页地址)

2.3 testcases 层封装

# testcases/test_goods.py 


import unittest
from selenium import webdriver
from common.getdatas import Data
from pageobjects.goodspay_page import GoodspayPage
from pageobjects.login_page import LoginPage


class TestGoods(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        cls.datas = Data(key="").datas

    # 很多用例包含前置条件,当前用例属于关联功能用例
    def setUp(self) -> None:
        self.driver = webdriver.Chrome()
        loginpage = LoginPage(self.driver)
        username = self.datas.get("login").get('sucess')[0]
        passwd = self.datas.get("login").get('sucess')[1]
        loginpage.login(usname=username, passwd=passwd)

    def test_goodsPay(self):
        goodsname = self.datas.get("goodsPay").get("goodsname")
        passwd = self.datas.get("goodsPay").get("passwd")
        # 2、走支付流程
        goodspage = GoodspayPage(self.driver)
        # 选择商品
        goodspage.select_goods(goodsname)
        # 加入购物车
        goodspage.gointocart()
        # 结算商品
        goodspage.paygoods()
        # 支付密码,
        goodspage.inputpasswd(passwd)
        # 提交订单
        goodspage.paymoney()
        # 断言   验证包含支付成功字符
        # 获取支付状态
        paystate = goodspage.paystateMsg()
        self.assertIn("订单支付成功", paystate)
# testcases/testcase_login.py


import unittest
from selenium.webdriver.common.by import By
from pageobjects.login_page import LoginPage
import time
from selenium import webdriver
from testdatas import login_datas as ld


class TestLogin(unittest.TestCase):

    def test_login(self):
        # 实例化对象
        self.driver = webdriver.Chrome()
        loginpage = LoginPage(driver=self.driver)
        print(ld.sucess[0])
        loginpage.login(ld.sucess[0], ld.sucess[1])
        loginpage.sleep(2)

# 每个用例对应一个.py的文件? 不是这样
# 用例基于模块进行管理
# test_good.py    设计关于这个模块下的所有自动化测试用例

2.4 testdatas 层封装

# testdatas/Globle_datas.py


# 根路径
base_url = "http://101.34.221.219:8010/"
# 登录路径
login_url = base_url + "?s=user/loginInfo.html"
#  testdatas/login_Datas.py


sucess=('laoban',"laoban123")
# testdatas/testdatas.yaml


baseurl: "http://47.107.116.139/shopnc/shop"
login:
  url: /?s=user/loginInfo.html
  success:
    - laoban
    - laoban123
goodsPay:
  goodsname: Python全栈自动化
  passwd: mashang

2.5 其他辅助层封装

2.5.1 common层封装

# common/dir_config.py


import os

# 根路径
base_dir=os.path.dirname( os.path.dirname(os.path.abspath(__file__)))
# 用例路径
testcases_dir=os.path.join(base_dir,"testcases")
# 数据路径
testdatas_dir=os.path.join(base_dir,"testdatas")
# 测试报告路径
reports_dir=os.path.join(base_dir,"Outputs/reports")
# 日志路径
logs_dir=os.path.join(base_dir,"Outputs/logs")
# 失败截图
screen_dir=os.path.join(base_dir,"Outputs/screenshots")

# print(base_dir)
# common/getdatas.py


import yaml
import os
import jsonpath
from common import dir_config as Dir


class Data:

    def __init__(self, key=None, filename="testdatas.yaml"):

        filepath = os.path.join(Dir.testdatas_dir, filename)
        with open(filepath, encoding="utf-8") as yaml_file:
            self.datas = yaml.load(yaml_file, Loader=yaml.FullLoader)
            if key:
                self.data = jsonpath.jsonpath(self.datas, f"$..{key}", )

if __name__ == '__main__':
    print(Data().datas)
# common/logger.py


import logging
from common import dir_config as Dir
import os
import time


class FrameLog:

    def getLogger(self):
        # 创建日志器
        logger = logging.getLogger("logger")
        # 日志输出当前级别及以上级别的信息,默认日志输出最低级别是warning
        if not logger.handlers:
            logger.setLevel(logging.INFO)
            # 创建控制台处理器----》输出控制台
            SH = logging.StreamHandler()
            # 创建文件处理器----》输出文件
            logpath = os.path.join(Dir.logs_dir, f"log_{time.strftime('%Y%m%d%H%M%S', time.localtime())}.txt")
            FH = logging.FileHandler(logpath)

            # 日志包含哪些内容    时间  文件  日志级别 :事件描述/问题描述
            formatter = logging.Formatter(fmt="[%(asctime)s] [%(filename)s] %(levelname)s :%(message)s",
                                          datefmt='%Y/%m/%d %H:%M:%S')
            logger.addHandler(SH)
            logger.addHandler(FH)
            SH.setFormatter(formatter)
            FH.setFormatter(formatter)

        return logger

    def sum(self, a, b):
        sum = a + b
        self.getLogger().info(f"实现求和:{sum}")
        return sum


if __name__ == '__main__':
    FrameLog().sum(1, 2)
    # 调试代码

2.5.2 pagelocation层封装

# pagelocations/goodspay_page_locs.py


from selenium.webdriver.common.by import By


class GoodsPayLoc:
    # 属性:元素定位
    # 搜索内容框
    selectcontent_loc=(By.XPATH,"//input[@id='keyword']")
    # 搜索按钮
    select_loc=(By.ID,"button")
    # 商品
    goods_loc=(By.XPATH,"//div[@class='goods-name']/a")
    #加入购物车按钮
    addgoodscart_loc=(By.XPATH,"//div[@class='add-cart']/a")
    #购物车结算菜单
    mycartmenu_loc=(By.XPATH,"//div[@class='head-user-menu']/dl[@class='my-cart']/dt")
    # 结算按钮
    allprice_loc=(By.XPATH,"//div[@class='checkout']/a")
    # 核对购物车商品
    nextsubmit_loc=(By.XPATH,"//div[@class='ncc-bottom']/a")
    # 支付密码框定位语句
    paypasswd_loc = (By.XPATH, "//input[@id='password']")
    #使用按钮
    userpaypasswd_loc=(By.XPATH,"//div[@id='pd_password']/a")
    #提交订单
    submit_loc = (By.XPATH, "//div[@class='ncc-bottom']/a")
    # 订单状态
    paystate_loc=(By.XPATH,"//div[@class='ncc-finish-a']")
# pagelocations/login_page_locs.py


from selenium.webdriver.common.by import By


class LoginPageLoc:
    # 登录页面的属性
    el_username = (By.NAME, "accounts")
    el_password = (By.NAME, "pwd")
    el_login = (By.XPATH, "//form[@class='am-form form-validation-username']/div[3]/button")

2.5.3 run层封装

# run.py


import os
import unittest
from HTMLTestRunner import HTMLTestRunner
from common import dir_config as Dir
import time

# 加载用例集
testcases = unittest.defaultTestLoader.discover(Dir.testcases_dir, 'test*.py')
reportpath = os.path.join(Dir.reports_dir, f"{time.strftime('%Y%m%d%H%M%S', time.localtime())}_rport.html")
with open(reportpath, "wb+") as htmlf:
    HTMLTestRunner(stream=htmlf, title="测试报告").run(testcases)
# 执行用例,生产测试报告

 

三、POM设计的意义

一种封装思想,旨在为每个待测页面创建一个页面对象,从而将繁琐的定位操作、操作细节封装到这个页面对象中,对外只提供必要的操作接口,

在调用的时候只调用提供的接口,不用去调用操作细节,最终实现程序的高内聚低耦合,使程序模块的可重用性、移植性大大增强 

 

posted on 2024-09-26 22:15  软饭攻城狮  阅读(5)  评论(0编辑  收藏  举报

导航