selenium UI自动化PO模式测试框架搭建

1、UI自动化规划

熟悉业务-》确定方案-》选取场景-》了解前端-》定位元素-》编写脚本-》运行优化-》回归报告-》项目汇总
价值意义:
自动化执行需要:模块化
需要可制定化执行
可复用性
PO模式:
将页面定位和业务分开,元素的定位单独处理,执行脚本单独封装。维护方便。
封装BasePage类,在基类中具有webdriver的实例的属性,把每个页面继承BasePage,通过driver管理每个页面的元素,将页面再细分一个个方法。Testcase依赖page类,进行组织化的测试步骤。
维护:
元素变化了:维护每个page
测试步骤变化:维护Testcase
每个page的常规操作封装基类:
点击动作、输入、文本处理、滑动、弹窗……
业务分类:
登录模块(登录、注册新用户)、
首页模块(菜单栏、主页显示)、
管理模块(数据列表展示、新增、分类、……)、
用户模糊(用户数据信息展示)……
功能测试用例依据以此分类设计

2、PageObject设计模式

系统梳理分类

代码层面分类:
系统System:
在这里插入图片描述
其中:
pageObjects
testCase
common
configs
utils
outFiles文件夹(logs/reports/screenshoot
docs文件夹
data文件夹
在这里插入图片描述
PO模式从下往上,从底层开始写:driver-》BasePage-》pages-》test执行-》报告
python+selenium+pytest+allure .

3、PO模式封装

1、驱动模块(固定写法

封装驱动写在:common包下
新建myDriver.py文件,建一个兼容浏览器的驱动类,就是dirver

"""
驱动模块
    1、考虑到浏览器的兼容性,可以分为主流的几个浏览器驱动
        1、谷歌
        2、火狐
        3、其他
"""

from selenium import webdriver
from configs.config import implicitly_wait_time

# 设置单例模式
'''
类创建实例:
    1、先调用—__new__()创建方法,一般类中不写,自动调用
    2、再调用初始化方法__init__()
  判断一个类是否有实例,如果有,就不会创建新的实例  
'''

# 哪一个类的实例需要使用单例模式,就直接继承这个类(`固定写法`)
class Single(object):
	#new方法创建对象
	def __new__(cls, *args, **kwargs):
	# 判断当前的类是否已经实例化
		if not hassttr(cls, '_instance'):
			cls._instance = super().__new__(cls)# super是父类的new方法
		return cls._instance
	

class Driver(Single):
    # 新建一个初始值
    _driver = None

    # 判断使用浏览器,---------默认指定谷歌浏览器
    def get_driver(self, browser_name='chrome'):
        if self._driver is None:
            if browser_name == "chrome":
                self._driver = webdriver.Chrome()
            elif browser_name == "firefox":
                self._driver = webdriver.Firefox()
            else:
                raise (f"没有这个{browser_name}浏览器,请使用可用浏览器打开")
            # 设置隐式等等时间---设置`**全局变量`**等等时间5s
            self._driver.implicitly_wait(implicitly_wait_time)
            # 浏览器对大化
            self._driver.maximize_window()
        return self._driver  # 返回浏览器对象


if __name__ == '__main__':
    Driver().get_driver('chrome')# 创建实例对象,调用对应的方法,传什么浏览器使用什么浏览器

驱动模块封装完毕,后续只需要调用Driver().get_driver('chrome')就可以使用webdriver进行元素定位
等同于对driver= webdriver.Chrome()的封装,方便兼容不同浏览器。

1、隐式等待时间全局变量参数化
configs包下创建config文件,设置隐式等待时间参数5s或者10s,自定义

implicitly_wait_time = 5

2、基类封装(基本框架固定写法)

基类写在:common包下,新建一个文件basePage.py

  • 创建基类basePage
    作用:1、把基本的页面操作封装好,给其他页面继承使用
    包含:2、点击操作(click)、输入框输入(input_text)、获取元素属性值(get_attrbute)、元素可见(visibility)、截图(save_screenshot)、清除(clear),获取文本内容(get_text)、显示等待presence_of(元素可见(visibility_of),可操作点击(clickable))……等操作指令的封装
from common.myDriver import Driver
import time
import os.path
from utils.handle_logs import logger
from utils.handle_path import screenshot_path, config_path
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from utils.handle_yaml import get_yaml_data
from common.myDriver import Driver
from configs_delivery.config import wait_timeout, wait_poll_frequency

class BasePage:
    def __init__(self):  # 调用basepage 需要的浏览器对象,默认谷歌浏览器
        self._driver = Driver().get_driver()

    # ------------------------封装截图方法---------------------------
    # 截图操作:可以使用浏览器对象去调用,也可以使用元素调用
    def get_page_screenshot(self, action=None):
        # 注意截图的描述:哪个动作+时间
        curTime = time.strftime('%Y%m%d%H%M%S', time.localtime())
        filepath = os.path.join(screenshot_path, f'{action}_{curTime}.png')
        # 截图操作
        self._driver.save_screenshot(filepath)

    # ----------------------封装页面操作------------------------
    # 1、封装-输入-操作
    def input_text(self, locator, text, action=None):
        '''

        :param locator: 元素定位: locator定义为包含-元素定位方法-和-定位方式-的包 ----['id','name']
        :param text: 输入文本的内容
        :param action: 执行的动作描述,缺省参数
        
        '''
        # 1、元素定位
        # find_element(定位方式,定位表达式)
        element = self._driver.find_element(*locator)  # "*"号解包
        # 2、很多输入框有默认提示文本,需要先清除
        element.clear()
        # 3、输入值
        element.send_keys(text)
    # 2、封装-点击-操作
    def click(self, locator, action=None):
        # 1、元素定位
        # find_element(定位方式,定位表达式)
        element = self._driver.find_element(*locator)  # "*"号解包
        # 2、点击
        element.click()
    # 3、封装-清空-操作
    def clear(self, locator, action=None):
        # 1/元素定位
        element = self._driver.find_element(*locator)
        # 2/清空
        element.clear()    
	# 4、封装 -获取文本内容-操作
    def get_text(self, locator, action=None):
        return WebDriverWait(
            self._driver,
            timeout=wait_timeout,
            poll_frequency=wait_poll_frequency).until(
            EC.visibility_of_element_located(locator)).text 
  
   # ----封装显式等待---判断元素是否存在,如果没有获取到元素,使用显示等待判断----日志及截图-------
    def element_is_presence(
            self,
            locator,
            action=None,
            timeout=5,
            poll_frequency=0.5):
        '''

        :param locator: 定位元素和定位方法和表达式,包('','')
        :param action: 动作描述,
        :param timeout: 等待时间设置
        :param poll_frequency: 等待频率
        '''
        # 1/设置显式等待时间
        try:
            WebDriverWait(
                self._driver,
                timeout=timeout,
                poll_frequency=poll_frequency).until(
                EC.presence_of_element_located(locator))
        except BaseException:
            # 1/打印log信息
            log.error(action)
            # 2/截图
            self.get_page_screenshot(action)
            # 3/元素不存在返回False
            return False
        # 如果存在返回True
        return True

---------------------------------------------------

  • 1、路径配置
    上述:screenshot_path,在公共方法utils中封装,新建handle_path.py
    在这里插入图片描述
    需要的路径都可以提前封装好:(固定写法
import os

# 1、工程路径
# os.path.obspath(__file__)  当前文件路径
# os.path.dirname()------获取上一层路径
project_path = os.path.dirname(os.path.dirname(__file__))
# 2、截图路径
screenshot_path = os.path.join(project_path, r'outFiles\screenshot')
# 3、日志路径
logs_path = os.path.join(project_path, r'outFiles\logs')
# 4、测试数据路径
testcase_path = os.path.join(project_path, 'data')
# 5、测试报告路径
reports_path = os.path.join(project_path, r'outFiles\reports\tmp')
# 6、配置路径
config_path = os.path.join(project_path, 'configs')

if __name__ == '__main__':
    # print(project_path)
    # print(screenshot_path)
    # print(logs_path)
    # print(reports_path)
    # print(testcase_path)
    print(config_path)
  • 2、log的封装(固定方法)
    utils包下新建handle_logs.py
    基类中封装显示等待的时间需要截图、并抓取log
"""
日志相关内容
    1、日志的输出渠道:文件xxx.logs        控制台输出
    2、日志级别:CRITICAL-ERROR-WARNING-INFO-DEBUG
    3、日志内容:2023-03-30 18:42:12, 234 INFO XXXXXXXX
        年月日.时分秒.级别-哪行代码出错-具体内容
"""
import logging
import datetime
from utils.handle_path import logs_path


def logger(file_Log=True, name=__name__):
    logDir = f'{logs_path}-{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.txt'
    # 1/创建日志收集器对象
    logObject = logging.getLogger()
    # 2/ 设置日志的级别
    logObject.setLevel(logging.DEBUG)
    # 3/ 日志内容格式
    fmt = '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
    formater = logging.Formatter(fmt)

    if file_Log:  # log文件模式
        # 设置日志渠道 -文件输出
        handle = logging.FileHandler(logDir, encoding='utf-8')
        # 日志内容与渠道绑定
        handle.setFormatter(formater)
        # 把日志对象与渠道绑定
        logObject.addHandler(handle)
    else:
        # 设置日志渠道控制台 控制台输出
        handle2 = logging.StreamHandler()
        # 日志内容与渠道绑定
        handle2.setFormatter(formater)
        # 把日志对象与渠道绑定
        logObject.addHandler(handle2)
    return logObject


log = logger()  # 控台输出日志

if __name__ == '__main__':
    log = logger()  # 控制台输出日志
    log.debug('日志信息:')
  • 3、
    locator的定义:
    selenium4 升级后定位方式变化为:driver.find_element(By.定位方法, '定位方式')格式
    例如:ID定位某个元素:

    login = driver.find_element(By.ID, 'btnLogin')
    

    locator即是By.定位方法, '定位方式'这个包

  • 4、封装页面操作:后续需要自行添加函数封装即可

    • 显示等待如上封装,可见元素定位的封装方法如下
 def element_is_presence(
            self,
            locator,
            action=None,
            timeout=5,
            poll_frequency=0.5):
        '''
   def element_is_visibility(
            self,
            locator,
            action=None,
            timeout=5,
            poll_frequency=0.5):
        # 1/设置显式等待可见元素定位时间
        try:
            ele = WebDriverWait(
                self._driver,
                timeout=timeout,
                poll_frequency=poll_frequency).until(
                #判断元素是否可见再执行定位
                EC.visibility_of_element_located(locator))
        except BaseException:
            # 1/打印log信息
            log.error(action)
            # 2/截图
            self.get_page_screenshot(f'{action}-元素定位不到')
            # 3/元素不存在直接抛出异常信息
            raise
        # 如果存在返回元素本身
        return ele 

而封装的页面操作使用可见元素定位的方法修改为:

 # 2、输入操作
    def click(self, locator, action=None):
        # 1、元素定位
        # element_is_visibility(定位方式,定位表达式)。元素是可见的再进行定位
        element = self.element_is_visibility(locator)
        # 2、点击
        element.click()
  • 封装定位元素是可点击的方法
    # ----------------------可见元素且是可点击的-----------------
    def element_is_clickable(
            self,
            locator,
            action=None,
            timeout=5,
            poll_frequency=0.5):
        # 1/设置显式等待可见元素定位时间
        try:
            ele = WebDriverWait(
                self._driver,
                timeout=timeout,
                poll_frequency=poll_frequency).until(
                # 判断元素是否可点击执行定位
                EC.element_to_be_clickable(locator))
        except BaseException:
            # 1/打印log信息
            log.error(action)
            # 2/截图
            self.get_page_screenshot(f'{action}-元素定位不到')
            # 3/元素不可点击直接抛出异常信息
            raise
        # 如果存在返回元素本身
        return ele

相应页面的操作封装方法:

 # 2、输入操作
    def click(self, locator, action=None):
        # 1、元素定位
        # element_is_visibility(定位方式,定位表达式)。元素是可见的再进行定位
        element = self.element_is_clickable(locator)
        # 2、点击
        element.click()
  • 5、wait_timeout, wait_poll_frequency在config中提前配置号
    在这里插入图片描述
    ---------------------------------.----------------------------------------

3、设置yaml定位元素(固定写法

config包下新建一个文件locator.yaml 写定位配置数据

  • 【”定位方法“,”定位方式“】的写法即:.find_element_by_id('username '),取id和对应的属性值,八大元素定位法之一。
  • 若采用css或者xpath写法:message_text: [‘css selector’,‘.el-message--error’] #登录错误消息文本:css selector即定位方法,.el-message--error定位方式。构成了字典格式{xxx:{ss:['v','v'],ss:['v','v'],ss:['v','v']},{[],[]},{[],[]}},后续通过字典的键获取值执行操作
LoginPage:
  username_input: ['id','username']#登录账户
  pwd_input: ['id','pwd']#登录密码
  login_btn: ['id','btnLogin']#登录按钮
  message_text: ['css selector','.el-message--error'] #登录错误消息文本
OtherPage:
  home_button: ['xpath','//*[text()="首页"]'] #首页按钮
  logout_button: ['xpath',"//span[text()='退出']"] #退出按钮
  personal_button: ['xpath','//img']  #个人中心按钮
  

4、读取yaml定位数据(固定写法

utils包下新建一个文件,handle_yanl.py 处理获取yaml数据

import yaml
from  utils.handle_path import config_path

def get_yaml_data(fileDir):
    #方法一
    # # 1、打开文件
    # fo = open(fileDir, 'r', encoding='utf-8')
    # # 2、读取文件
    # yaml_data = yaml.load(fo, Loader=yaml.FullLoader)
    # return yamldata
    #方法二
    with open(fileDir, encoding='utf-8') as fo:
        return yaml.safe_load(fo.read())


if __name__ == '__main__':
    print(get_yaml_data(f'{config_path}\locator.yaml'))

5、写PO页面-继承基类

先写个基础常规的页面:后续还有优化的方法

在pageObject新建操作模块页面:比如:login_page.py,继承basePage
登录页面:

from  utils.handle_path import config_path
from common.basePage import BasePage
from configs.config import url
from time import sleep
from utils.handle_yaml import get_yaml_data


class LoginPage(BasePage): # 继承基类
    def to_login_page(self):
        self._driver.get(url)  # 打开登录页面,url在config包中配置
        return self		#登录网址返回self本身,方便后续调用

    # 1、登录
    def login(self, username, pwd):
    	#读取yaml定位参数locator['LoginPage']提取键值数据
        locator = get_yaml_data(f'{config_path}\locator.yaml')['LoginPage']
        # 输入账号,locator['username_input']获取定位方法和定位方式,xxx填写容
        self.input_text(locator['username_input'], 'username', action='输入账号')
        # 输入密码
        self.input_text(locator['pwd_input'], 'pwd', action="输入密码")
        # 点击登录按钮
        self.click(locator['login_btn'], action="点击登录按钮")
        sleep(5)


if __name__ == '__main__':
    # 登录页面
    LoginPage().to_login_page().login('uername','pwd' )

------------------------**注**---------------------------------

  • 1、urlconfigs中配置后再调用
    在这里插入图片描述
    定位获取优化方法:

locator = get_yaml_data(f’{config_path}\locator.yaml’)[‘LoginPage’]
self.input_text(locator[‘username_input’], ‘username’, action=‘输入账号’)
一个项目上百个定位元素,每次都通过get_yaml_data文件处理数据,然后再通locator
一个获取定位方法和定位方式比较繁琐。
so:设计了巧妙的定位调用方法,脚本中对于定位不可见,但是却省事省力的方法:

在设计locator.yaml文件是,把字典的键设计成语po页面的类同名即可。
在这里插入图片描述
在这里插入图片描述
在基类中封装一下:

import time
import os.path
from utils.handle_logs import logger
from utils.handle_path import screenshot_path, config_path
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from utils.handle_yaml import get_yaml_data
from common.myDriver import Driver

class BasePage:
    def __init__(self):  # 调用basepage 需要的浏览器对象,默认谷歌浏览器
        self._driver = Driver().get_driver()
        # --------------获取所有需要的定位的元素参数,且实现对号入座
        # loginpage-----只获取loginPage
        # [self.__class__.__name__]获取到对应的类的名字,yaml定位元素时命名与后续页面操作类的名字保持一样
        locator = get_yaml_data(
            config_path + r'\locator.yaml')[self.__class__.__name__]  # 字典
        # print(locator)
        for k, v in locator.items():  # k自定义键名,v(定位方法,定位表达式)
            setattr(self, k, v)
            """
            setattr(self, k, v)
                1、self是页面的实例
                2、使用set attrbute--setattr
                3、创建self这个实例的新的属性,将v赋值给她,self.k = v
                例子:
                class Test:
                    pass
                test = Test()  创建实例
                #对实例再创建属性,并将对应属性的值赋给它
                setattr(test, 'name', '123')
                print(test.name)
                结果就是test.name = 123
            """

------------------------**注**-----------------------------
上述locator的封装方式就是通过[self.__class__.__name__]获取到对应po页面的本身类的名字

 locator = get_yaml_data(
            config_path + r'\locator.yaml')[self.__class__.__name__]  # 字典格式

例如登录的定位元素打印出来是:
在这里插入图片描述
所以对字典处理:setattr方法将键值和self绑定

for k, v in locator.items():  # k自定义键名,v(定位方法,定位表达式)
            setattr(self, k, v)

则,self.k = v,则最终可以获取[‘id’, ‘username’],也就是我们需要的,定位方法和定位方式
基类封装中可以直接使用self.自定义键名

find_element(定位方式,定位表达式) ==find_element(self.自定义键名)

则优化后登录页面可以写成:

from pageObjcets.homePage import HomePage
from common.basePage import BasePage
from configs.config import url
from time import sleep


# 登录类,继承基类
class LoginPage(BasePage):
    def to_loginPage(self):
        self._driver.get(url)  # 打开登录页面
        return self

    # 1、登录
    def login(self, username, pwd):
        # 输入账号self.username_input:优化后的方法
        self.input_text(self.username_input, username, action='输入账号')
        # 输入密码
        self.input_text(self.pwd_input, pwd, action="输入密码")
        # 点击登录按钮"self.login_btn"就是优化后的使用方法
        self.click(self.login_btn, action="点击登录按钮")
        sleep(2)


if __name__ == '__main__':
    # # 登录页面
    homepage = LoginPage().to_loginPage().login('xxx', 'xxx')
    # 首页展示
    # # print('这个类的名字是', LoginPage().__class__.__name__)

这样的话,整个测试框架中看不到定位:只需要配置locator.yaml即可。后续保持键与各个page的类名一样即可。
-------------------------------.----------------------------------

6、测试用例yaml

data文件夹下新建一个文件loginData.yaml
登录的的测试用例编写格式:

# 登录成功用例
- ['usernamexxx','pswxxx'] # 账号密码正确
---   #分隔符
# 登录失败用例
- [['xpath',"//*[contains(text(),'该用户不存在!')]"],'该用户不存在!','usernamexx','pswxxx']
- [['xpath',"//*[contains(text(),'输入的密码错误!')]"],'输入的密码错误!','usernamexx','pswxxx']
- [['xpath',"//*[contains(text(),'该用户不存在!')]"],'该用户不存在!','','111111']
- [['css selector','.el-form-item__error'],'The password can not be less than 5 digits','xxxx','']
- [['css selector','.el-form-item__error'],'The password can not be less than 5 digits','','']

用例编写完,需要进一步处理数据成为自己想要的:[(‘username’,‘pwd’),(‘us’,‘pwd’),(‘usme’,‘wd’)]这种格式的,就可以使用pytest.mark.parametrize()参数化了。
在utile包新建一个handle_yaml.py文件,因为之前处理定位yaml有了,就在当前文件下封装个读取用例的get_case_yaml函数:因为我们把正向用例和反向用例写在一个yaml文件里,分隔成两个yaml文件数据了,需要单独处理

def get_case_yaml(filedir):
    caselist = []
    # 1、打开文件
    fo = open(filedir, 'r', encoding='utf-8')
    # 2、读取文件
    testcases = yaml.load(fo, Loader=yaml.FullLoader)
    for one in testcases:
        caselist.append(one)
    return caselist
    # print(testcases)

test_login.py提取数据时需要使用角标提取就行了:
get_case_yaml(filedir[0]),提取的就是正确的密码和账号
get_case_yaml(filedir[1]),提取反向用例的数据

7、pytest测试模块+allure测试报告输出

testCase包下新建一个文件test_login.py
1、参数化测试用例:@pytest.mark.parametrize('username, pwd', get_yamlData(testcase_path+r'\loginData.yaml'))
2、优化测试报告:
标题level: ---------------------测试步骤:
@allure.epic() -----------------with allure.step(’ ‘):
@allure.feature()--------------
@allure.story()
@allure.title()
测试报告输出:标红的部分
pytest.main([‘test_login.py’, '--alluredir', reports_path, '--clean-alluredir'])
os.system(f'allure serve {reports_path}')
3、测试报告版本展示:
在reports/tmp下新建一个文件:environment.properties文件,也就是在你生成报告的路径下新建这个文件
里面填写你测试软件平台的版本信息,丰富allure测试报告的展示

Browser=Chromium
Browser.Version=21.1.18
Stand=test
python.Version=3.9.2
allure.version=2.13.3

pytest测试代码如下:

import os, pytest, allure
from utils.handle_yaml import get_yamlData
from pageObjcets.loginPage import LoginPage
from utils.handle_path import testcase_path, reports_path
loginData = f"{testcase_path}/login_cases.yaml"

@allure.epic('登录模块test')
@allure.story('登录模块test')
class TestLogin:  # 测试类
    @pytest.mark.parametrize('username, pwd',
                             get_case_yaml(loginData)[0])
    @allure.title('登录成功case')
    def test_login(self, username, pwd):
    	#第一步:输入账户密码登录
        with allure.step('输入账户密码执行登录'):
            # 1/登录
            homepage = LoginPage().to_loginPage().login(username, pwd)
            # 2/登录是否成功,断言四否具有首页元素。assert断言失败后就无法继续执行。
            # assert homepage.element_is_presence(homepage.home_button)
            # 2/解决方案:assume断言失败后可以继续执行下一步,有时间也需要反向的用例测试失败可以继续下一步执行
        # 第2步:验证登录成功
        with allure.step('验证登录是否成功'):
            pytest.assume(
                homepage.element_is_presence(
                    homepage.home_button,
                    action='登录后验证首页'))
        # 第3步:退出返回到登录页面,方便后续的反向用例执行
        with allure.step('退出返回到登录页面'):
            # 3/做完登录退回到登录页面
            homepage.to_login_page(action='退出登录')


if __name__ == '__main__':
    pytest.main(['-sq', 'test_login.py', '--alluredir', reports_path, '--clean-alluredir'])
    os.system(f'allure serve  {reports_path}')

验证首页元素,需要先在projectPage页新建一个homePage.py文件,因为首页不属于登录页,需要写在首页中:

 # 点击回到首页
def homepage(self):
    self.click(self.home_button, '点击首页')    

homepage.element_is_presence(homepage.home_button,action='登录后验证首页'))中的home_button即是首页元素。

反向用例处理:
测试反向用例若使用assert,断言失败就无法继续执行:
引入新的插件:assume
需要安装pytest-assume库,在pycharm终端使用命令安装:

pip3 install pytest-assume

在这里插入图片描述
引自:https://blog.csdn.net/weixin_50829653/article/details/113179401
反向用例准备好了,在test_login.py再封装一个函数处理反向测试场景:
1、在locator.yaml准备好相应的登录失败的提示文本的元素定位:
在这里插入图片描述
2、编写处理反向用例代码

    # 执行反向用例,登录失败的场景
    @pytest.mark.parametrize('locator, expected, username, password',
                             get_case_yaml(loginData)[1])
    # @pytest.mark.skip(reason='先不跑')
    @allure.title('登录失败用例')
    def test_loginerr(self, locator, expected, username, password):
        with allure.step('执行登录'):
            homepage = LoginPage().login_page()
            homepage.login(username, password)

        with allure.step('断言登录失败提示'):
            pytest.assume(homepage.get_text(locator) == expected)
            # homepage.get_screenshot('报错截图')

四个参数分别对应用例中的定位元素(locator),期望结果(expected),账号和密码
通过BasePage的get_text方法获取登录失败提示文本信息,与期望对比,这里使用assert也行,建议使用assume,不然失败了就停止测试了。
通过装饰器mark.skip()选择是否跑反向用例

最后合起来的pytest执行脚本:

import os, pytest, allure
from utils.handle_yaml import get_yamlData
from pageObjcets.loginPage import LoginPage
from utils.handle_path import testcase_path, reports_path
loginData = f"{testcase_path}/login_cases.yaml"

@allure.epic('登录模块test')
@allure.story('登录模块test')
class TestLogin:  # 测试类
    @pytest.mark.parametrize('username, pwd',
                             get_case_yaml(loginData)[0])
    @allure.title('登录成功case')
    def test_login(self, username, pwd):
    	#第一步:输入账户密码登录
        with allure.step(' 1、输入账户密码执行登录'):
            # 1/登录
            homepage = LoginPage().to_loginPage().login(username, pwd)
            # 2/登录是否成功,断言四否具有首页元素。assert断言失败后就无法继续执行。
            # assert homepage.element_is_presence(homepage.home_button)
            # 2/解决方案:assume断言失败后可以继续执行下一步,有时间也需要反向的用例测试失败可以继续下一步执行
        # 第2步:验证登录成功
        with allure.step('2 、验证登录是否成功'):
            pytest.assume(
                homepage.element_is_presence(
                    homepage.home_button,
                    action='登录后验证首页'))
        # 第3步:退出返回到登录页面,方便后续的反向用例执行
        with allure.step(' 3、退出返回到登录页面'):
            # 3/做完登录退回到登录页面
            homepage.to_login_page(action='退出登录')

    # 执行反向用例,登录失败的场景
    @pytest.mark.parametrize('locator, expected, username, password',
                             get_case_yaml(loginData)[1])
    # @pytest.mark.skip(reason='先不跑')
    @allure.title('登录失败用例')
    def test_loginerr(self, locator, expected, username, password):
        with allure.step('1、执行登录'):
            homepage = LoginPage().login_page()
            homepage.login(username, password)
        with allure.step('2、断言登录失败提示'):
            pytest.assume(homepage.get_text(locator) == expected)
            # homepage.get_screenshot('报错截图')


if __name__ == '__main__':
    pytest.main(['-sq', 'test_login.py', '--alluredir', reports_path, '--clean-alluredir'])
    os.system(f'allure serve  {reports_path}')

8、PO模式结合fixture的数据清除与格式化

testCase包下新建一个conftest.py注:命名必须为conftest.py, pytest的规则命名。只有conftest才能识别


"""
scope指的是fixture的作用域:
      4种:
          function(函数)<class(类)<module(模块级别)<session(包)
auto
    1、手动调用执行
    2、自动执行
"""
import pytest
from time import sleep
from common.myDriver import Driver

@pytest.fixture(scope='session', autouse=True)
def start_running():
    print("------开始运行UI自动化测试-----")
    yield
    # 自动化完成之后,关闭浏览器进程
    sleep(3)    #避免浏览器关闭太快,最后一部操作了但响应时间时浏览器关闭太快导致问题。
    Driver().get_driver().quit()
    print("-----结束-UI自动化测试-----")

--------------------**注**--------------------------
1、sleep(3)是为了保证关闭退出浏览器过快而报错。有时候最后一步执行操作未完成就关闭了浏览器。
2、关于数据清除:例如在增加设置或者新增数据时,保证不改变别人的数据,我们只删除自己的数据,一般处理代码放在执行完成测试后:以下非登录模块的数据清除代码。删除代码编写在page模块中调用。

 # 1、测试完毕删除测试数据
    homepage.to_product_list().delete_first_product()
    # 2、回到首页方便后续执行测试
    homepage.to_home_page()

放置位置如下:
在这里插入图片描述
本地一键执行所有测试模块
run.bat的创建:
本地项目的路径下创建一个run.bat文件
在这里插入图片描述
在文件内使用如下:
cd到测试路径->pytest执行所有文件,->输出allure报告

cd ./testCase_polly
pytest -sv --alluredir ./outFiles/reports/tmp --clean-alluredir
allure serve ./outFiles/reports/tmp

操作汇总

1:需要主要的是操作导致刷新页面需要重新获取定位元素,否则会失效
2:若操作会导致切换页面,需要加一定强制等待sleep(3),不然很容易导致定位报错或者定位不到
3:conftest 的fixture配置退出浏览器或者结束关闭浏览器需要强制等待sleep(5),不然最后一步的执行可能会报错

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
基于JavaSelenium的Web UI自动化测试框架是一种自动化测试工具,可以用于测试Web界面的功能和用户交互。以下是该框架的一些关键特性和优势。 1. 支持多浏览器和平台:这个框架可以在各种主流浏览器(如Chrome、Firefox、Safari等)和操作系统(如Windows、Mac、Linux等)上运行,确保网站在不同环境下的兼容性。 2. 灵活的对象识别:通过使用Selenium的定位策略(如ID、CSS选择器、XPath等),该框架可以准确地识别页面上的元素,从而使测试用例的编写更加简单和可靠。 3. 数据驱动测试:该框架支持数据驱动测试,可以从外部源(例如Excel、CSV等)中加载测试数据,并对每个数据组合执行相同的测试用例,从而提高测试效率和可重复性。 4. 测试报告和日志:这个框架提供了详细的测试报告和日志记录功能。测试报告会提供关键的测试执行结果,如成功率、失败率和跳过率,并展示每个用例的执行状态。日志记录则可以帮助开发人员在调试期间查找问题和进行错误分析。 5. 高可扩展性:该框架可以根据项目需要进行定制和扩展。开发人员可以编写自定义函数和库,以便进行特定的测试操作和验证。 总之,基于JavaSelenium的Web UI自动化测试框架提供了一种可靠、高效和可扩展的方式来测试Web界面。它可以帮助团队快速检测和修复潜在的缺陷,提高软件质量和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值