多测师肖sir_高级金牌讲师_ui自动化po框架版本01

ui自动化po框架
一、po框架
1、基本介绍
(1)po模式是page object model的缩写(简称:po或pom)
(2) po模式的核心思想:分层,实现耦合 实现:业务流程与页面元素操作分离的模式。
(3)作用:
a、提高测试用例的可维护性、可读性
b、降低代码的耦合性
c、实现脚本重复使用

二、自动化测试框架分层如下:

结构图:
在这里插入图片描述
框架的思想:把整个用例进行拆分6个包
‘’'1、先创建一个dcscms项目
在创建一个conf包存放所有配置文件信息(比如项目路径和数据,用例的路径)
在创建一个包
放数据(测试数据)
在Data包中可以存放xlsx文件数据(放测试数据)
测试环境的一些url地址和账号密码可以放在TestDta中
在创建一个report包==存放测试报告
在创建一个public公共公开的包(存放一些功能用例)
在public包中创建pages存放元素层流程层(封装所有页面的公共方法,基类)
在public包中创建utils包(处理公共类公共函数都存放在此)
可以在utils中来读取pages中封装的登录的流程(封装读取ini文件或者EXCEL表格的工具类和工具函数
在创建一个TestCase用例包用来存放用例(编写测试用例)
在创建一个run_all用来运行
通过运行测试用例中封装好的用例在运行然后在repot中生成测试报告

================================

实战:
一、新建项目
在这里插入图片描述
二、在新建6个包

详解
1、在conf包中先把项目的路径和每个包的路径写好,
作用:方便我们后面引用
定义cms_path的py文件:

"""
此模块用来定义项目及其下面的包的路径
"""
import os  #导入os模块
#定义项目路径
base_path = os.path.dirname(os.path.dirname(__file__))    # 项目所在的路径
# print(base_path)
# data路径
data_path = os.path.join(base_path,'data')
# pages路径
pages_path = os.path.join(base_path,'public','pages')
# utils路径
utils_path = os.path.join(base_path,'public','utils')
# report路径
report_path = os.path.join(base_path,'report')
# run路径
run_path = os.path.join(base_path,'run')
# testcase路径
testcase_path = os.path.join(base_path,'testcase')

在创建一个ini文件命名:conf.ini 这里都是写配置文件, 存放我们测试数据url,账号,密码,url等

[test_data]  #节点#section     #option
url = http://cms.duoceshi.cn/cms/manage/login.do
username = admin
password = 123456
[exception_data_1]
username = admin
password = 654321
[exception_data_2]
username = admin111
password = 654321
[mysql_data]
host_name = 192.168.1.1
port = 3306
username = 1233333
[useradd_data]
username = dcs
password = 123456
phoneno = 13566667777
useremail = 12345@qq.com
useraccount = admin8765
userpwd = 123456

===============================================
在data中也可以用excel表格写数据(和ini一样都可以存放数据)
文件名:cmsdata.xlsx
编写url和username和passwor
在这里插入图片描述

在工具类中封装读取ini文件 read_conf文件

from configparser import ConfigParser
from conf.cms_path import *
import os
class R_conf(ConfigParser):
    '''
    当前这个类是用来处理conf.ini文件的工具类
    '''
    def __init__(self,filename):
        super(R_conf,self).__init__()  #继承父类的构造方法
        self.filename = filename  #把传进来的形式参数赋值给到实例变量self.filename
        self.read(self.filename)  #打开ini文件进行读取

    def get_value(self,section,option):
        '''
        获取ini文件中的section下面对应的option的value值
        '''
        value = self.get(section,option)
        return value
path = os.path.join(conf_path,'conf.ini')
conf =R_conf(path)
print(conf.get_value('test_data','url'))

在封装一个读取excel表格类

import openpyxl   #操作Excel表格的库
from conf.cms_path import *
import os

class R_Excel(object):
    '''封装一个班读取Excel表格的工具类'''

    def __init__(self,filename,sheet_name):
        self.filename = filename
        self.sheet_name = sheet_name

    def open(self):
        '''封装一个打开Excel表格的工具方法'''
        #通过openpyxl模块调用load_workbook函数打开filename文件
        self.wb = openpyxl.load_workbook(self.filename)
        # 通过self.wb这个Excel文件的对象读取对应的sheet页面的
        self.sh = self.wb[self.sheet_name]
    def read_data(self,row,col):
        self.open()
        cell = self.sh.cell(row=row, column=col)
        return cell.value

# if __name__ == '__main__':
#     path = os.path.join(data_path, 'testdata.xlsx')
#     read_excel = R_Excel(path, 'cms_data')
#     print(read_excel.read_data(1,1))

以上都是数据的准备,和我们讲的接口自动化前面差不多。

====================================
接下来我们开始封装基类:在public中的page下新建一个basepage的py文件,
比如: 所有用例要用到的元素定位,以及输入框输入,点击,下拉等公共方法
我们所有的用例都要继承它:(,跑完,关闭,我们换一个思路,让所有的用例都在在同一个对象里面跑)
注解:
首先我们创建一个类,BasePage(object):
BasePage 类是一个基础页面类,它使用单例设计模式来确保在整个应用中只创建一个 WebDriver 对象。WebDriver 通常用于自动化浏览器交互,例如打开网页、填写表单、点击按钮等。

_driver 是一个类变量,用于存储 WebDriver 对象。由于它在类级别定义,所以所有实例都可以访问和共享这个变量。
get_driver 是一个类方法(用 @classmethod 装饰器表示)。它用于获取 _driver 变量。
如果 _driver 为空(即还没有创建 WebDriver 对象),则创建一个新的 WebDriver 对象并将其赋值给 _driver。
如果 _driver 不为空,则直接返回它。
这种设计使得在整个应用中,不论你创建多少个 BasePage 的实例,你都会得到同一个 _driver 对象。这对于在多线程环境下进行自动化测试或网页交互非常有用,因为它确保了所有线程都使用同一个 WebDriver 实例;
driver = None是一种常见的写法,用于声明一个变量并初始化为None。这种写法表明driver变量在初始状态下没有赋值。
这个写法通常用于以下情况:

初始化变量:当你需要声明一个变量,但不确定它的初始值时,可以将它初始化为None。这表示该变量当前没有有效的值。
判断变量是否被赋值:通过检查变量是否为None,可以判断该变量是否已经被赋值。如果变量为None,则说明它还没有被赋值。
占位符:在编写代码时,有时候需要声明一个变量,但暂时不需要使用它。为了不影响代码的执行,可以将它初始化为None,作为占位符使用。

所以我们创建类方法: @classmethod (为什么我们创建类方法,因为类方法可以直接被类调用)

class BasePage(object):

    _driver = None  #声明一个变量并初始化为None

    @classmethod
    def get_driver(cls): #通过get_driver获取driver对象
        #单例设计模式:是为了保证每个用例用的都是同一个浏览器对象
        #(比如:篮球场上进行100次进行投篮练习,100个篮球练习;用同一个篮球,进行100次投篮,这就是单例模式)
        if not cls._driver:
            cls._driver = webdriver.Chrome()   #
        return cls._driver
#以上是一个单例模式
#除了打开浏览器以外,我们还要进行元素定位,我们也封装一个类方法,名字是find_elemnet
#比如 baidu=(id,‘kw’)
    @classmethod    #
    def find_element(cls,element):#定位元素,我们用了很多种元素定位方法,
        """
        对selenium的元素定位方法进行二次封装,到时候进行元素定位的时候,只需要调用同一个方法就行了,参数不一样
        :param element: 元素属性
        """
        type_name = element[0]
        value_name = element[1]  
        if type_name == 'id': #可以写一个if多分支方法
            ele = cls._driver.find_element_by_id(value_name)
        elif type_name == 'name':
            ele = cls._driver.find_element_by_name(value_name)
        elif type_name == 'class':
            ele = cls._driver.find_element_by_class_name(value_name)
        elif type_name == 'xpath':
            ele = cls._driver.find_element_by_xpath(value_name)
        elif type_name == "tag_name":
            ele = cls._driver.find_element_by_tag_name(value_name)
        elif type_name == 'link_text':
            ele = cls._driver.find_element_by_link_text(value_name)
        elif type_name == 'partial_link_text':
            ele = cls._driver.find_element_by_partial_link_text(value_name)
        elif type_name == 'css':
            ele = cls._driver.find_element_by_css_selector(value_name)
        else:
            raise ValueError("请输入正确的属性参数") #如果不是输入定位方法不对,手动触犯异常,请输入正确的参数
        return ele  #把网页元素返回去
  这里我们封装一个定位元素工具方法,方便,只要你传元素方法和元素属性值
    @classmethod
    def click(cls,ele):
        return ele.click()

    @classmethod
    def sendKeys(cls,ele,text):
        """
        封装输入内容的方法
        :param ele:  元素对象
        :param text:  文本信息
        :return:
        """
        return ele.send_keys(text)

    @classmethod
    def sleep(cls,sec):
        """
        封装一个线程等待方法
        :param sec: 等待的时间,单位为秒
        :return:
        """
        return sleep(sec)

    @classmethod
    def wait(cls,sec):
        """
        封装一个隐式等待
        :param sec:
        :return:
        """
        return cls._driver.implicitly_wait(sec)

    @classmethod
    def get_text(cls,ele):
        """
        封装一个获取元素的文本信息的方法
        :param ele:
        :return:
        """
        return ele.text

    @classmethod
    def get_attribute(cls,ele,typename):
        return ele.get_attribute(typename)

    @classmethod
    def maximize_window(cls):
        return cls._driver.maximize_window()

    @classmethod
    def switch_frame(cls,ele):
        return cls._driver.switch_to.frame(ele)


if __name__ == '__main__':
    driver = BasePage.get_driver()
    driver.get('http://cms.duoceshi.cn/cms/manage/login.do')
    sleep(2)
    ele = BasePage.find_element(('id','userAccount'))
    ele.send_keys('admin')

接下来我们写用例:
写下登录和用户中心的用例

"""
此模块用来编写登录的测试用例
"""

from config.config import *
from public.pages.base_page import BasePage
from public.pages.pages_element import Pages_Element as p
from public.utils.handle_excel import Read_Excel
from public.utils.handle_ini import Read_Ini
import unittest
import os

#获取访问页面的url地址
ini_file = os.path.join(data_path,'data.ini')
read = Read_Ini(ini_file)
url = read.read_ini('test_data','url')
username = read.read_ini('test_data','username')
passwd = read.read_ini('test_data','password')

ex_username = read.read_ini('exception_data_1','username')
ex_passwd = read.read_ini('exception_data_1','password')

class Test_Login(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = BasePage.get_driver()

    @classmethod
    def tearDownClass(cls):
        BasePage.sleep(3)
        del cls.driver

    def setUp(self):
        self.driver.get(url)
        self.driver.maximize_window()
        BasePage.sleep(3)

    def test_1_login(self):
        ele = BasePage.find_element(p.userName)
        BasePage.sendKeys(ele,username)
        BasePage.wait(5)
        ele2 = BasePage.find_element(p.passWord)
        BasePage.sendKeys(ele2,passwd)
        BasePage.wait(5)
        ele3 = BasePage.find_element(p.loginBtn)
        BasePage.click(ele3)
        ele4 = BasePage.find_element(p.exitBtn)
        value = BasePage.get_attribute(ele4,'title')
        self.assertEqual(value,'退出')
        BasePage.sleep(2)

    def test_2_login(self):
        ele = BasePage.find_element(p.userName)
        BasePage.sendKeys(ele,ex_username)
        BasePage.wait(5)
        ele2 = BasePage.find_element(p.passWord)
        BasePage.sendKeys(ele2,ex_passwd)
        BasePage.wait(5)
        ele3 = BasePage.find_element(p.loginBtn)
        BasePage.click(ele3)
        BasePage.sleep(2)

    def test_3_login(self):
        pass

if __name__ == '__main__':
    unittest.main()

添加用户

#coding:utf-8

from config.config import *
from public.pages.base_page import BasePage
from public.pages.pages_element import Pages_Element as p
from public.utils.handle_excel import Read_Excel
from public.utils.handle_ini import Read_Ini
from public.utils.login import Cms_Login as cm
import unittest
import os

ini_path = os.path.join(data_path,'data.ini')
read = Read_Ini(ini_path)

usrname = read.read_ini('useradd_data','username')
pwd = read.read_ini('useradd_data','password')
phoneno = read.read_ini('useradd_data','phoneno')
useremail = read.read_ini('useradd_data','useremail')
useraccount = read.read_ini('useradd_data','useraccount')
userpwd = read.read_ini('useradd_data','userpwd')

class Test_Adduser(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = cm().suc_login()

    @classmethod
    def tearDownClass(cls):
        del cls.driver

    def play(self):
        pass

    def test_1_adduser(self):
        ele1 = BasePage.find_element(p.menuUser)
        BasePage.click(ele1)
        BasePage.sleep(1)
        ele2 = BasePage.find_element(p.userMana)
        BasePage.click(ele2)
        BasePage.sleep(1)
        ele3 = BasePage.find_element(p.iframe1)
        BasePage.switch_frame(ele3)
        BasePage.wait(3)
        ele4 = BasePage.find_element(p.addUserBtn)
        BasePage.click(ele4)
        BasePage.wait(3)
        ele5 = BasePage.find_element(p.iframe2)
        BasePage.switch_frame(ele5)
        BasePage.wait(3)
        ele6 = BasePage.find_element(p.customerName)
        BasePage.sendKeys(ele6,usrname)


if __name__ == '__main__':
    unittest.main()


==========================================
我们针对定位元素也可以封装:
建py文件一个命名:pages_elemnet,我们可以把所有的元素定位放在这个文件中,
按照定位流程书写:
我们把页面元素定位进行抽离的四项被称为:po模式

"""
此模块用来保存各个页面的网页元素的属性信息
"""

class Pages_Element():
    """
    此类用来存放网页元素的属性信息
    """
    # 1、用户名输入框的属性
    userName = ('id','userAccount')
    # 2、密码输入框的属性
    passWord = ('id','loginPwd')
    # 3、登录按钮的属性
    loginBtn = ('id','loginBtn')
    # 4、登录页取消按钮的属性
    cancelBtn = ('xpath','//*[@id="userloginform"]/div[4]/input[2]')
    # 5、退出按钮的属性
    exitBtn = ('xpath','/html/body/header/span[2]/a')
    # 6、个人中心按钮的属性
    menuUser = ('xpath','//*[@id="menu-user"]/dt')
    # 7、用户管理选项的属性
    userMana = ('link_text','用户管理')
    # 8、第一层iframe框的属性
    iframe1 = ('name','/cms/manage/user-list.html')
    # 9、添加用户按钮的属性
    addUserBtn = ('partial_link_text','添加用户')
    # 10、第二层iframe框的属性
    iframe2 = ('name','xubox_iframe1')
    # 11、用户姓名输入框属性 ----- 添加用户操作
    customerName = ('name','userName')
    # 12、手机号输入框属性  ----- 添加用户操作
    phoneNumber = ('id','user-tel')
    # 13、登录用户输入框属性  ---- 添加用户操作
    userAccount = ('id','userAccount')
    # 14、登录用户密码输入框属性 ---- 添加用户操作
    loginPwd = ('id','loginPwd')
    # 15、二次确认密码输入框属性  ------- 添加用户操作
    confirmPwd = ('id','confirmPwd')
    # 16、邮箱输入框属性   ------- 添加用户操作
    userEmail = ('id','user-email')
    # 17、提交按钮属性  ----------  添加用户操作
    submitBtn = ('id','submitBtn')


==========================
假如我要写第二条用例的时候

==========================
然后再运行用例

#coding:utf-8

import sys
sys.path.append('.')    #添加当前所在的目录到环境变量中
sys.path.append('..')   #添加上级目录到环境变量中

import unittest
from public.utils.HTMLTestRunnerNew import HTMLTestRunner
import os
from config.config import *
def run_all(report_file,sd,pt,tester):
    f = open(report_file,'wb')
    suite1 = unittest.defaultTestLoader.discover(start_dir=sd,
                                                 pattern=pt)
    runner = HTMLTestRunner(stream=f,
                         title='ui自动化测试报告',
                         description='运行结果如下',
                         tester=tester)
    runner.run(suite1)
if __name__ == '__main__':
    report_file = os.path.join(report_path,'report_ui.html')
    run_all(report_file,testcase_path,'test*.py','dcs')




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/133365.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于java+springboot+vue的幼儿园信息网站

项目介绍 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于幼儿园管理系统当然也不能排除在外,随着网络技术的不断成熟,带动了幼儿园管理系统,它彻底改变了过…

[LeetCode] 5.最长回文子串

一、题目描述 给你一个字符串 s,找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 示例 1: 输入:s "babad" 输出:"bab" 解释:"aba&…

Python零基础小白常遇到的问题总结

文章目录 一、注意你的Python版本1.print()函数2.raw_input()与input()3.比较符号&#xff0c;使用!替换<>4.repr函数5.exec()函数 二、新手常遇到的问题1、如何写多行程序&#xff1f;2、如何执行.py文件&#xff1f;3、and&#xff0c;or&#xff0c;not4、True和False…

C语言----静态链接库和动态链接库

在前面的文章中讲到可执行程序的生成需要经过预处理&#xff0c;编译&#xff0c;汇编和链接四个步骤&#xff0c;链接阶段是链接器将该目标文件与其他目标文件、库文件、启动文件等链接起来生成可执行文件。 需要解读一下库文件&#xff0c;我们可以将库文件等价为压缩包文件&…

【电路笔记】-节点电压分析和网状电流分析

节点电压分析和网状电流分析 文章目录 节点电压分析和网状电流分析1、节点电压分析1.1 概述1.2 示例 2、网格电流分析2.1 概述2.2 示例 3、总结 正如我们在上一篇介绍电路分析基本定律的文章中所看到的&#xff0c;基尔霍夫电路定律 (KCL) 是计算任何电路中未知电压和电流的强大…

基于STC12C5A60S2系列1T 8051单片机串口通信信应用

基于STC12C5A60S2系列1T 8051单片机串口通信应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串口通信的特殊功能寄存器列表基于STC12C5A60S2系列1T 8051单片…

基于DS1302时钟液晶12864显示2路闹钟仿真及源程序

一、系统方案 1、本设计采用51单片机作为主控器。 2、DS1302采集年月日时分秒送到液晶12864显示。 3、按键年月日时分秒&#xff0c;两路闹钟。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 uchar clock_time[6] {0X00,0X59,0X23,0X09,0X…

【VsCode】SSH远程连接Linux服务器开发,搭配cpolar内网穿透实现公网访问

文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…

火星加载WMTS服务

这是正常的加载瓦片 http://192.168.1.23:8008/geoserver/mars3d/gwc/service/wmts?tilematrixEPSG%3A4326%3A7&layermars3d%3Abuffer&style&tilerow46&tilecol197&tilematrixsetEPSG%3A4326&formatimage%2Fpng&serviceWMTS&version1.0.0&…

ENVI IDL:如何基于气象站点数据进行反距离权重插值?

01 前言 仅仅练习&#xff0c;大可使用ArcGIS或者已经封装好的python模块进行插值&#xff0c;此处仅仅从底层理解如何从公式和代码理解反距离权重插值的过程&#xff0c;从而更深刻的理解IDL的使用和插值的理解。 02 函数说明 2.1 Read_CSV()函数 官方语法如下&#xff1a…

国企业务变革-管理变革-IT支撑

&#xff08;1&#xff09;业务变革 国家-国资委-央国企这条链&#xff0c;有一主一副两个战略嘱托&#xff1a; 一个是&#xff1a;做大&#xff0c;并且做强-自主可控产业链供应链。 一个是&#xff1a;支撑国家一带一路战略落地 一、做大&#xff0c;做强-自主可控产业链供应…

常见面试题-分布式锁

Redisson 分布式锁&#xff1f;在项目中哪里使用&#xff1f;多久会进行释放&#xff1f;如何加强一个分布式锁&#xff1f; 答&#xff1a; 什么时候需要使用分布式锁呢&#xff1f; 在分布式的场景下&#xff0c;使用 Java 的单机锁并不可以保证多个应用的同时操作共享资源…

影响金融软件开发价格的因素有哪些?

随着科技的发展&#xff0c;金融行业逐渐向数字化和信息化转型&#xff0c;在这个过程中&#xff0c;金融软件开发成为了重要的支撑&#xff0c;然而&#xff0c;金融软件开发的价格是一个复杂的问题&#xff0c;受到多种因素的影响&#xff0c;本文将详细解析影响金融软件开发…

【Nginx40】Nginx学习:动静分离与日志分割

Nginx学习&#xff1a;动静分离与日志分割 放轻松放轻松&#xff0c;最后两篇文章学习的内容是比较轻松的。首先&#xff0c;我们来看看 Nginx 动静分离的概念&#xff0c;然后再看看怎么为 Nginx 做日志分割。内容都很简单&#xff0c;完全不需要有任何的压力。 动静分离 动静…

win下安卓打包指南

win下安卓打包指南 0、缘起 换了台电脑竟然忘了怎么打包&#xff0c;还好有笔记&#xff0c;用软件打包也挺好&#xff0c;但是我感觉用 命令行 更有操作感&#xff0c;分享下。 1、下载并配置apktool&#xff08;放在C://Windows无需配置环境变量&#xff0c;需要java环境&…

JVM虚拟机:垃圾回收器之Parallel Old(老年代)

本文重点 本文将学习老年代的另外一种垃圾回收器Parallel Old(PO)&#xff0c;这是一种用于老年代的并行化垃圾回收器&#xff0c;它使用标记整理算法进行垃圾回收。 历史 在1.6之前&#xff0c;新生代使用Parallel Scavenge只能搭配老年代的Serial Old收集器&#xff0c;而…

入选《人工智能领域内容榜》

入选《人工智能领域内容榜》第 23名 C# OpenCvSharp DNN HybridNets 同时处理车辆检测、可驾驶区域分割、车道线分割-CSDN博客

比亚迪推动启动电池无铅化 车主有福了

时间过得很快&#xff0c;又到了立冬&#xff0c;意味着冬季已经开始。此时的北方已经下起了大雪&#xff0c;即便是艳阳高照的粤北山区&#xff0c;早晚也出现了较大的温差。笔者不禁想起此前做二手车时期的尴尬场面——三年以上的老车&#xff0c;尤其是没有更换过启动电池的…

【解决方案】pytion 运行时提示 import psutil ModuleNotFoundError: No module named ‘psutil‘

报错原因分析 import psutil ModuleNotFoundError: No module named psutil报错原因分析 当前环境pytion中缺少了psutil包&#xff0c;使用pip命令进行安装 解决方案 pip install psutil

土壤含水量的计算

土壤含水量的计算 土壤水分的表示方法 一般所说的土壤水分&#xff0c;实际上是指用烘干法在105-110摄氏度温度下能从土壤中被驱逐出来的水。土壤水分含量即土壤含水量&#xff0c;它是指土壤中所含有的水分的数量。土壤含水量可以用不同的方法表示&#xff0c;最常用的表示方…