basepage封装

本文介绍了如何对自动化测试框架进行完善,包括BasePage的二次封装,实现日志记录、错误处理和失败截图功能。通过对selenium webdriver API的封装,实现了等待、查找、点击、输入等常见操作,简化了页面对象的编写,提高了测试用例的可读性和维护性。此外,还介绍了配置文件的管理和日志文件的生成。
摘要由CSDN通过智能技术生成

接着上一篇 分层设计思想,进行了自动化测试框架 PO页面对象层、测试用例层、元素定位层 和 测试数据层 4层封装

然而,在真实项目中,仅有这4层封装,这个自动化测试框架还是不完善的。

自动化测试框架的常用功能:

  • 1、补充用例、执行用例或筛选用例进行执行
  • 2、对执行失败的用例进行重运行功能(用例重运行机制)
  • 3、生成测试报告
  • 4、生成日志、运行失败进行截图

一个好的自动化测试框架,需要做到能减少测试人员的一些不必要的工作,关键步骤在于测试人员的拿到这个框架,能将主要工作在补充测试用例上,通过补充用例生成对应的日志、测试报告的这些内容。
所以,当前的这4层封装,还没有达到这个效果。

basepage封装的目的

为了实现 生成日志、运行失败进行截图 和 优化 PageObjects的行为封装 的功能,需要对 basepage进行二次封装,而这个封装的本质,其实就是对 selenium webdriver 进行封装。

首先,回顾前面的内容,我们自动化测试框架中的测试用例的实现,测试用例中的每一个步骤 = 调用页面对象的行为 + 测试数据

test_login.py

    def test_login_success(self):
    # 步骤:
        # 1、登录页面-登录操作
        LoginPage(self.driver).login(*lds.success_case) # 输入账号密码
    # 断言:
        # 1、首页-获取元素是否存在 (进行断言操作,元素可见返回True)
        self.assertTrue(HomePage(self.driver).get_element_exists())



    @ddt.data(*lds.fail_cases)
    def test_login_failed(self,case):
    # 步骤:
        # 1、登录页面-登录操作
        lp = LoginPage(self.driver)
        lp.login(case["user"], case["psd"])  # 输入 cases 数据中的 账号和密码
        # print(case["user"], case["psd"], case["check"])
    # 断言:
        # 1、登录页面-获取错误提示信息 进行对比
        # print(lp.get_error_msg())
        self.assertEqual(lp.get_error_msg(),case["check"])

就拿登录的测试用例来说,其实是调用了 login函数中的登录操作,再加上登录账号和密码数据。

由于登录的测试用例只涉及到登录操作,只调用了登录函数,所以这里相对没有那么多其他函数调用。假设一个测试用例比较复杂,比如我们发邮件,需要有登录操作、填写收件人和文本内容操作、添加文件操作、发送操作等等。

所以,错误的抛出和异常处理,我们就对测试用例中调用的函数的操作进行错误处理,比如这里就对
登录操作进行异常处理
在这里插入图片描述
回到 login_page.py 页面,查看登录函数详情
在这里插入图片描述
这些函数操作,都涉及 等待、元素定位、输入文本、点击 这些操作,所以,假设我们对这些操作进行封装,把输入日志错误处理的功能放在这些操作的身上,这样,不管页面对象上有多少个函数,只要这些函数涉及 等待、元素定位、输入文本、点击 这些操作,那么,这些函数都具有输入日志和错误处理的功能

所以说,basepage二次封装的本质,其实就是对 selenium webdriver API 进行封装。

我们常用的操作有:

  • 1、等待元素可见
  • 2、查找元素
  • 3、点击
  • 4、输入
  • 5、获取文本
  • 6、获取属性

所以,就对这些操作进行二次封装
而这里3~6对应的操作:点击、输入、获取文本和获取属性,都有两个前提,就是等待元素可见和查找元素,所以,封装好 等待元素可见和查找元素 后,可以直接调用 1和2 等待元素可见和查找元素

basepage封装操作

这些封装操作,我们可以将其独立出来,不属于 PO 四层中的内容,作为公共模块进行使用

新建一个 Outputs 文件夹,放置失败截图、日志文件和测试报告

新建一个 Common Python包,在Common文件夹下新建 basepage.py 文件
在这里插入图片描述
接下来就是在 basepage.py 文件下进行封装,先把大致的内容写好,然后往里面填东西

basepage.py

from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

import logging


class Basepage:

    def __init__(self,driver:WebDriver):

        # 初始化 dirver
        self.driver = driver

   
    # 等待元素可见 
    def wait_ele_visible(self):

        pass

    # 查找元素
    def get_ele(self):

        pass

    # 点击元素
    def click_ele(self):
        pass

    # 输入内容
    def input_text(self):
        pass

    # 获取文本属性
    def get_ele_attribute(self):

        pass
        
    # 获取文本内容
    def get_ele_text(self):

        pass

配置文件

填完之后,把这块内容先放一放,在 Common 文件夹下新建 dir_config.py 文件,用于放置文件的配置路径

dir_config.py

import os

#框架项目顶层目录的绝对路径
base_dir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0]
# print(base_dir)
# D:\Python\pythonStudy\PO


# 测试数据路径
testdatas_dir =  os.path.join(base_dir,"TestDatas")
# print(testdatas_dir)
# D:\Python\pythonStudy\PO\TestDatas

# 测试用例路径
testcases_dir =  os.path.join(base_dir,"TestCases")

# 测试报告路径
htmlreport_dir =  os.path.join(base_dir,"Outputs/reports")


# 日志文件路径
logs_dir =  os.path.join(base_dir,"Outputs/logs")

# 文件配置路径
# config_dir =  os.path.join(base_dir,"Config")

# 截图文件路径
screenshot_dir = os.path.join(base_dir,"Outputs/screenshots")

在 Common 文件夹下新建 logger.py 文件,用于自定义日志文件的输出内容

logger.py

import logging
from logging.handlers import RotatingFileHandler
import os
import time
from PO.Common import dir_config

# format格式化的内容
#         时间格式       级别名称        文件名称                       行号            日志信息
fmt = " %(asctime)s  %(levelname)s %(filename)s %(funcName)s [ line:%(lineno)d ] %(message)s"

# 时间格式
datefmt = '%a, %d %b %Y %H:%M:%S'

# 输出到控制台
handler_1 = logging.StreamHandler()

curTime = time.strftime("%Y-%m-%d %H%M", time.localtime())
# print(curTime)
# 输出内容  2020-11-02 2140

# 输出到文件
handler_2 = RotatingFileHandler(dir_config.logs_dir+"/Web_Autotest_{0}.log".format(curTime),backupCount=20,encoding='utf-8')

#设置rootlogger 的输出内容形式,输出渠道
logging.basicConfig(format=fmt,datefmt=datefmt,level=logging.INFO,handlers=[handler_1,handler_2])


# 举例
# logging.info("hehehe")
# 运行该py文件,执行该语句,控制台输入的日志为
# Mon, 02 Nov 2020 21:38:13  INFO logger.py <module> [ line:23 ] hehehe
#该种方法让日志的输出内容按照我们想要的形式输出
封装函数操作

日志的处理已经完成了,回到 basepage.py 文件下,现在对 basepage进行封装

现在还需要有失败截图的功能, 等待、元素定位、输入文本、点击 等这些操作都有可能会失败,所以,先在最前面定义一个截图功能的函数,如果这些操作都有失败的操作,调用这个截图的函数进行截图操作

在操作的同时,引入日志
basepage.py

from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from PO.Common.dir_config import screenshot_dir


import time
import logging


class Basepage:

    def __init__(self,driver:WebDriver):

        # 初始化 dirver
        self.driver = driver

    def save_screenshot(self,img_name):
        # 将图片存储在Outputs文件夹下的screenshots文件夹下
        # 失败截图命名规范 页面名称_页面行为_时间.png,这里先传一个参数 img_name 代表业务操作,对应的是 页面名称_页面行为

        # 获取当前时间并转化为指定格式
        now = time.strftime("%Y-%m-%d %H_%M_%S")
        # 文件完整名称
        screenshot_path = screenshot_dir + "/{}_{}.png".format(img_name,now)
        # 调用driver中的截图操作
        self.driver.save_screenshot(screenshot_path)
        # 同时输出日志信息
        logging.info("截图当前网页成功并存储在:{}".format(screenshot_path))


    # 等待元素可见 默认时间为20秒,轮询为0.5(可修改)
    def wait_ele_visible(self,loc,img_name,timeout=20,poll_fre=0.5):
    
    	# 引入日志功能,输出日志内容
        logging.info("等待 {} 元素可见".format(loc))

        # 错误处理
        try:
            WebDriverWait(self.driver,timeout,poll_frequency=poll_fre).until(EC.visibility_of_element_located(loc))
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("等待元素可见失败:")
        
        raise 


    # 查找元素
    def get_ele(self):

        pass

    # 点击元素
    def click_ele(self):
        pass

    # 输入内容
    def input_text(self):
        pass

    # 获取文本属性
    def get_ele_attribute(self):

        pass
    # 获取文本内容
    def get_ele_text(self):

        pass

同样的方式,将所有需要进行二次封装的函数进行补齐,还可对函数加上时间统计功能,这样就知道这个操作耗时多少
basepage.py

from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from PO.Common.dir_config import screenshot_dir


import time
import logging


# 记录日志/失败截图+错误信息输出+抛出异常
class Basepage:

    def __init__(self,driver:WebDriver):

        # 初始化 dirver
        self.driver = driver

    def save_screenshot(self,img_name):
        # 将图片存储在Outputs文件夹下的screenshots文件夹下
        # 失败截图命名规范 页面名称_页面行为_时间.png

        # 获取当前时间并转化为指定格式
        now = time.strftime("%Y-%m-%d %H_%M_%S")
        # 文件完整名称
        screenshot_path = screenshot_dir + "/{}_{}.png".format(img_name,now)
        # 调用driver中的截图操作
        self.driver.save_screenshot(screenshot_path)
        # 同时输出日志信息
        logging.info("截图当前网页成功并存储在:{}".format(screenshot_path))



    # 等待元素可见 默认时间为20秒,轮询为0.5(可修改)
    def wait_ele_visible(self,loc,img_name,timeout=20,poll_fre=0.5):
    
    	# 引入日志功能,输出日志内容
        logging.info("等待 {} 元素可见".format(loc))

        # 错误处理
        try:
         	# 起始等待的时间 datetime
            start = datetime.datetime.now()
            
            WebDriverWait(self.driver,timeout,poll_frequency=poll_fre).until(EC.visibility_of_element_located(loc))
            
             # 结束等待的时间
            end = datetime.datetime.now()
            # 输出等待时间的日志
            logging.info("开始等待时间点:{},结束等待时间点:{},等待时长为:{}".format(start, end, end - start))
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            # 引入日志功能,输出失败日志
            logging.exception("等待元素可见失败:")
            raise
        



    # 查找元素
    def get_ele(self,loc,img_name):

        # 引入日志功能,输出日志内容
        logging.info("在{}中查找 {} 元素".format(img_name, loc))

        # 错误处理
        try:
        	# 起始等待的时间 datetime
            start = datetime.datetime.now()
            
            ele =self.driver.find_element(*loc)
            
             # 结束等待的时间
            end = datetime.datetime.now()
            # 输出等待时间的日志
            logging.info("开始等待时间点:{},结束等待时间点:{},等待时长为:{}".format(start, end, end - start))

        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("查找元素失败:")
            raise
        else:
            return ele



    # 点击元素
    def click_ele(self,loc,img_name,timeout=20,poll_fre=0.5):

       

        # 等待与查找是前提
        self.wait_ele_visible(loc,img_name,timeout,poll_fre)
        ele = self.get_ele(loc,img_name)

		 # 引入日志功能,输出日志内容
        logging.info("在{}中点击 {} 元素".format(img_name, loc))

        # 错误处理
        try:
            ele.click()
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("点击元素失败!")
            raise


    # 输入内容
    def input_text(self,loc,value,img_name,timeout=20,poll_fre=0.5):

        

        # 等待与查找是前提
        self.wait_ele_visible(loc,img_name,timeout,poll_fre)
        ele = self.get_ele(loc, img_name)
        
        # 引入日志功能,输出日志内容
        logging.info("在{}中往 {} 元素输入{}".format(img_name, loc,value))

        # 错误处理
        try:
            ele.send_keys(value)
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("元素输入文本失败!")
            raise

    # 获取文本属性
    def get_ele_attribute(self,loc,attr_name,img_name,timeout=20,poll_fre=0.5):

        
        # 等待与查找是前提
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_ele(loc, img_name)
        
        # 引入日志功能,输出日志内容
        logging.info("在{}中获取{}元素的{}属性。".format(img_name, loc, attr_name))


        # 错误处理
        try:
            value = ele.get_attribute(attr_name)
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("获取元素的属性失败!")
            raise
        else:
            logging.info("属性值为:{}".format(value))
            return value




    # 获取文本内容
    def get_ele_text(self,loc,img_name,timeout=20,poll_fre=0.5):

       
        # 等待与查找是前提
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_ele(loc, img_name)
        
         # 引入日志功能,输出日志内容
        logging.info("在{}中获取{}元素的文本内容。".format(img_name, loc))

        # 错误处理
        try:
            text = ele.text
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("获取元素的文本内容失败!")
            raise
        else:
            logging.info("文本内容为:{}".format(text))
            return text

继承 basepage

这时候,就可以在 login_page.py 文件中引入 basepage,通过继承 basepage 实现登录页面的登录操作

这是原有的登录函数的操作

login_page.py

# 登录-元素操作
    def login(self,username,password):

        # 设置元素可见等待
        # 显性等待时间为20秒,等待元素为登录按钮
        WebDriverWait(self.driver,20).until(EC.visibility_of_element_located(locs.login_btn))

        # 找到用户名输入框输入用户名
        self.driver.find_element(*locs.user_input).send_keys(username)
        # 找到密码输入框输入密码
        self.driver.find_element(*locs.psd_input).send_keys(password)
        # 找到登录按钮进行点击
        self.driver.find_element(*locs.login_btn).click()

现在继承basepage,就可以简化为三步操作:输入用户名、输入密码、点击登录按钮,因为继承了basepage,输入操作就具有了等待元素的功能

修改之后,就变成下面这样

from PO.PageLocators.login_page_locs import LoginPageLocs as locs
from PO.Common.basepage import Basepage

class LoginPage(Basepage):

	# 登录-元素操作
    def login(self,username,password):
    	self.input_text(locs.user_input,username,"登录页面,输入用户名")
        self.input_text(locs.psd_input,password,"登录页面,输入密码")
        self.click_ele(locs.login_btn,"登录页面点击登录按钮")

只需要三步,也不需要再进行注释了,一眼看过去就很明了

而继承了basepage后,初始化操作也可以不用了,但是还有个步骤,登录需要切换到 iframe中进行元素定位,那么就在basepage多封装一个切换到 iframe 中的操作

# iframe 切换
    def switch_to_iframe(self,loc,img_name):
        # iframe定位的元素可以是下标,也可以通过iframe的属性进行定位

        # 引入日志功能,输出日志内容
        logging.info("等待iframe{}可见,并切换到iframe中。".format(loc))
        # 错误处理
        try:
            # 等待 iframe 可见,然后进行切换
            #                                            等待iframe有效并切换进去
            WebDriverWait(self.driver,20).until(EC.frame_to_be_available_and_switch_to_it(loc))
        except:
            # 失败截图-也需写入日志
            self.save_screenshot(img_name)
            logging.exception("iframe 切换失败!")
            raise

然后在登录操作中多加一个步骤,先切换到 iframe 再进行元素定位和操作

from PO.PageLocators.login_page_locs import LoginPageLocs as locs
from PO.Common.basepage import Basepage

class LoginPage(Basepage):
 	登录-元素操作
    def login(self,username,password):
    	self.switch_to_iframe(locs.login_iframe,"登录页面_切换到iframe")
        self.input_text(locs.user_input,username,"登录页面_输入用户名")
        self.input_text(locs.psd_input,password,"登录页面_输入密码")
        self.click_ele(locs.login_btn,"登录页面点击登录按钮")

执行一下 TestCase 目录下 test_login.py 文件中的 test_login_success 测试用例,可以发现执行成功,且打印的日志为

 Tue, 03 Nov 2020 22:14:50  INFO basepage.py switch_to_iframe [ line:202 ] 等待iframe('xpath', "//iframe[contains(@id,'x-URS')]")可见,并切换到iframe中。
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py wait_ele_visible [ line:38 ] 等待 ('xpath', "//div[@class='u-input box']/input[@name='email']") 元素可见
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py get_ele [ line:72 ] 在登录页面_输入用户名中查找('xpath', "//div[@class='u-input box']/input[@name='email']") 元素
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py input_text [ line:135 ] 在登录页面_输入用户名中往 ('xpath', "//div[@class='u-input box']/input[@name='email']") 元素输入z14737424527
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py wait_ele_visible [ line:38 ] 等待 ('xpath', "//div[@class='u-input box']/input[@name='password']") 元素可见
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py get_ele [ line:72 ] 在登录页面_输入密码中查找('xpath', "//div[@class='u-input box']/input[@name='password']") 元素
 Tue, 03 Nov 2020 22:14:50  INFO basepage.py input_text [ line:135 ] 在登录页面_输入密码中往 ('xpath', "//div[@class='u-input box']/input[@name='password']") 元素输入zb05270513
 Tue, 03 Nov 2020 22:14:51  INFO basepage.py wait_ele_visible [ line:38 ] 等待 ('xpath', "//a[@id='dologin']") 元素可见
 Tue, 03 Nov 2020 22:14:51  INFO basepage.py get_ele [ line:72 ] 在登录页面点击登录按钮中查找('xpath', "//a[@id='dologin']") 元素
 Tue, 03 Nov 2020 22:14:51  INFO basepage.py click_ele [ line:114 ] 在登录页面点击登录按钮中点击 ('xpath', "//a[@id='dologin']") 元素

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值