首页 > 编程语言 > 详细

基于Python Selenium Unittest PO设计模式

时间:2021-05-06 09:55:40      阅读:50      评论:0      收藏:0      [点我收藏+]

一、什么是PO设计模式(Page Object Model)

1、Page Object是一种设计模式,它主要体现在对界面交互细节的封装上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性。

2、一般PO设计模式有三层

第一层:

  • 对Selenium 进行二次封装,定义一个所有页面都继承的 BasePage ,
  • 封装 Selenium 基本方法 例如:元素定位,元素等待,导航页面 ,
  • 不需要全部封装,用到多少方法就封装多少方法。

第二层:

  • 页面元素进行分离,每个元素只定位一次,隔离定位,如果页面改变,只需要改变相应的元素定位;
  • 业务逻辑分离 或 操作元素动作分离

第三层:

  • 使用单元测试框架对业务逻辑进行测试

技术分享图片

 二、为什么要使用PO设计模式

  • 页面频繁变化,(页面html结构等变化)导致页面UI元素频繁变动,元素定位改变
  • 传统线性自动化(面向过程开发),用例中需要反复的定位同一个元素
  • 每当页面发生变化的时候,需要在用例中寻找变动的部分,工作量大,容易产生遗漏,不容易维护

三、使用PO设计模式要点

  • 不要在page页面对象外做元素定位
  • 不在page页面对象里面写断言,除非是页面是否成功加载断言
  • 需要多少个元素就定位多少个,不需要对整个页面的元素进行定位
  • 当你的用例设计页面跳转时,例如登陆操作,登陆完成后跳转首页,当页面发生“跳转”
  • ,封装的业务逻辑需要返回(return)对应的页面对象的实例
  • BasePage封装Selenium 基础方法,不需要全部封装,用到多少方法就封装多少方法

四、PO设计模式实例

以公司的统一登录作为项目例子,用PO设计模式实现登陆:

1、手工用例:

用例编号 标题 前置条件 操作步骤 预期结果 实际结果
T-001 登录成功 输入正确的用户名和密码

1、打开统一登录页

2、输入用户名

3、输入密码

4、点击登录按钮

登录成功,正确跳转到应用系统界面 XXX
T-002 登录失败 输入错误的用户名

1、打开统一登录页

2、输入用户名

3、输入密码

4、点击登录按钮

登录失败,正确提示用户名或密码不正确 XXX

2、用PO模式实现自动化用例

项目目录

技术分享图片

Base.py

class BasePage:
    ‘‘‘基础Page层,封装一些常用方法‘‘‘
    ‘‘‘第一层:对selenium进行二次封装,定义一个所有页面都继承的BasePage,
        封装selenium基本方法,如元素定位、元素等待、导航页面,
        不需要全部封装,用到多少方法就封装多少方法‘‘‘
    def __init__(self,driver):
        self.driver = driver
    # 打开页面
    def open(self, url=None):
        ‘‘‘open()方法用于打开网页,它接收一个url参数,默认为None,如果url参数为None,
        则默认打开子类中定义的url‘‘‘
        if url is None:
            self.driver.get(self.url)
        else:
            self.driver.get(url)

    ‘‘‘以下的定位方法:selenium提供的元素定位方法很长,这里做了简化,只是为了在子类中使用更加简便‘‘‘
    # id定位
    def by_id(self,id):
        return self.driver.find_element_by_id(id)
    # name定位
    def by_name(self,name):
        return self.driver.find_element_by_name(name)
    # class定位
    def by_class(self,class_name):
        return self.driver.find_element_by_class_name(class_name)
    # xpath定位
    def by_xpath(self,xpath):
        return self.driver.find_element_by_xpath(xpath)
    # CSS定位
    def by_css(self,css):
        return self.driver.find_element_by_css_selector(css)
    # 获取title
    def get_title(self):
        return self.driver.title
    # 获取页面text,仅使用xpath定位
    def get_text(self,xpath):
        return self.by_xpath(xpath).text
    # 获取页面的URL
    def get_current_url(self):
        return self.driver.current_url
    # 执行js脚本
    def js(self,script):
        self.driver.execute_script(script)

login_page.py

from Page import Base

# 创建LoginPage类继续BasePage类
class LoginPage(Base.BasePage):
    ‘‘‘统一平台登录Page层,登录页面封装操作到的元素‘‘‘
    ‘‘‘第二层:页面元素进行分离,每个元素只定位一次,操作元素动作分离‘‘‘
    # 定义url变量,供父类中的open()方法使用
    url ="https://test01....cn/#/login"
    # 用户名输入框定位
    def form_username(self,user_name):
        return self.by_id("name").send_keys(user_name) # 使用了父类的self.by_id()方法定位元素,简洁了不少
    # 密码输入框定位
    def form_password(self,pass_word):
        return self.by_id("password").send_keys(pass_word)
    # 登录按钮定位
    def button_login(self):
        return self.by_xpath("//*[text()=‘登录‘]").click()

test_login.py

from Page import login_page
import unittest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.keys import Keys
from CommonMethod import LogUtil

logger = LogUtil.logs() # 调用封装的日志方法,将日志输出到控制台以及写入文件

class LoginCase(unittest.TestCase):
    ‘‘‘第三层:用单元测试框架对业务逻辑进行测试‘‘‘
    ‘‘‘使用LoginPage类及它所继承的父类中的方法‘‘‘
    @classmethod
    def setUpClass(cls):
        # 实例化webdriver,俗称:打开浏览器
        cls.driver = webdriver.Firefox(executable_path=E:\\UI test\\UnittestProject\\Driver\\geckodriver.exe)
        cls.driver.implicitly_wait(10)
    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()
    def test_login_success(self):
        page = login_page.LoginPage(self.driver) # 需要用到哪个Page类时,只需要将它传入浏览器驱动,就可以使用该类中提供的方法了
        page.open()
        page.form_username("XXX")
        page.form_password("123456")
        page.button_login()
        sleep(2)
        self.assertEqual(page.get_current_url(), "https://test01....cn/#/home")
        print("登录成功,用例执行结果通过,当前的url为"+ page.get_current_url())
        sleep(1)
    def test_login_fail(self):
        page = login_page.LoginPage(self.driver)
        page.open()
        page.form_username("XXX11")
        page.form_password("123456")
        page.button_login()
        self.assertNotEqual(page.get_current_url(), "https://test01....cn/#/home")
        print("登录失败,用例执行结果通过,当前的url为"+ page.get_current_url())
        page.form_username(Keys.CONTROL+a) # 输入组合键Ctrl+a,全选输入框内容
        page.form_username(Keys.BACK_SPACE) # 删除键,删除选中的内容
        page.form_password(Keys.CONTROL + a)
        page.form_password(Keys.BACK_SPACE)
        sleep(1)
if __name__ == __main__:
    unittest.main(verbosity=2)

执行结果

技术分享图片

在test_login.py中有调用封装的日志方法,这里把封装的日志附上,在CommonMethod目录下的LogUtil.py

技术分享图片
import logging
import logging.handlers
import os
import time

class logs(object):
    def __init__(self):
        self.logger=logging.getLogger("")
        # 设置输出的等级
        LEVELS={
            NOSET:logging.NOTSET,
            DEBUG:logging.DEBUG,
            INFO:logging.INFO,
            WARNING:logging.WARNING,
            ERROR:logging.ERROR,
            CRITICAL:logging.CRITICAL
        }
        # 创建文件目录
        logs_dir = "E:\\UI test\\UnittestProject\\TestLog"
        if os.path.exists(logs_dir) and os.path.isdir(logs_dir):
            pass
        else:
            os.mkdir(logs_dir)
        # 修改log保存位置
        timestamp = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime())
        logfilename = %s.txt % timestamp
        logfilepath = os.path.join(logs_dir, logfilename)
        rotatingFileHandler = logging.handlers.RotatingFileHandler(filename=logfilepath,
                                                                   maxBytes=1024 * 1024 * 50,
                                                                   backupCount=5)
        # 设置输出格式
        formatter = logging.Formatter([%(asctime)s] [%(levelname)s] %(message)s, %Y-%m-%d %H:%M:%S)
        rotatingFileHandler.setFormatter(formatter)
        # 控制台句柄
        console = logging.StreamHandler()
        console.setLevel(logging.NOTSET)
        console.setFormatter(formatter)
        # 添加内容到日志句柄中
        self.logger.addHandler(rotatingFileHandler)
        self.logger.addHandler(console)
        self.logger.setLevel(logging.NOTSET)

    def info(self, message):
        self.logger.info(message)

    def debug(self, message):
        self.logger.debug(message)

    def warning(self, message):
        self.logger.warning(message)

    def error(self, message):
        self.logger.error(message)
LogUtil

 

基于Python Selenium Unittest PO设计模式

原文:https://www.cnblogs.com/Chilam007/p/14732679.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!