Python+seleunim+unittest+ddt自动化数据驱动测试心得二

Python+seleunim+unittest+ddt自动化数据驱动测试心得二

前一篇文章写了安装的支持插件现在我们来写入具体的实现代码(中有很多代码与网上大同,或者我做的时候就是使用的人家的原创,谢谢晚上其他大佬的分享)

三、代码

1、在config包下创建config.ini配置文件,存储配置的浏览器与访问网站,代码如下:

[browserType]
;browserName = Firefox
browserName = Chrome
;drowserName = IE

[testServer]
URL = https://www.baidu.com
;URl = www.goole.com

2、dataSase包下放execl文件,execl文件如下
在这里插入图片描述

Id:编号 title:标题 module:当前测试页面(影响pageobjects中的选择脚本)
data:一个用例中需要填充的数据,使用英文”,”号隔开 result:想要的结果
3、重点的核心文件framework包
1)browerengine用于打开指定浏览器,代码如下:

#coding=utf-8
import configparser
from selenium import webdriver
from framework import loggers
import os

"""
浏览器引擎类
"""
logger = loggers.Logger(logger="browerengine_BrowserEngine").getlog()

class BrowserEngine(object):

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

    #浏览器操作
    def open_browser(self):
        #读取配置文件中的浏览器设置与访问网址
        config = configparser.ConfigParser()
        file_path = os.path.abspath('.')+"/config/config.ini"
        # 但是对于有BOM(如Windows下用记事本指定为utf-8)的文件,需要使用 utf-8-sig, 使用utf-8没办法。
        # 【我没试过,http://blog.csdn.net/liujingqiu/article/details/77677256】
        # 对于我写的代码 此处是utf-8-sig 或者 utf-8 都可以
        config.read(file_path,encoding="utf-8")
        browser = config.get("browserType", "browserName")
        logger.info("You had select %s browser." % browser)
        url = config.get("testServer", "URL")
        logger.info("The test server url is: %s" % url)

        #根据配置文件加载驱动
        if browser == "Firefox":
            self.driver = webdriver.Firefox()
            logger.info("firefox.")
        elif browser == "Chrome":
            self.driver = webdriver.Chrome()
            logger.info("Chrome.")
        elif browser == "IE":
            self.driver = webdriver.Ie()
            logger.info("Ie.")


        self.driver.get(url)
        logger.info("Open url: %s" % url)
        self.driver.maximize_window()
        logger.info("Maximize the current window.")
        self.driver.implicitly_wait(10)
        logger.info("Set implicitly wait 10 seconds.")
        # self.driver = driver
        return self.driver

    #退出浏览器
    def quit_browser(self):
        self.driver.quit()
        logger.info("Now, Close and quit the browser.")

2)elementUtils对seleunim中的方法封装作为基类使用,代码如下:

#coding=utf-8
import time
from selenium.common.exceptions import NoSuchElementException
from framework import loggers
import os


#创建logger实例
logger = loggers.Logger(logger="BasePage").getlog()

'''
seleunim操作基类
'''
class BasePage(object):
    """
    页面基类
    """
    def __init__(self, driver):
        self.driver = driver

    # 退出浏览器 结束操作
    def quit_browser(self):
        self.driver.quit()

    # 浏览器前进
    def forward(self):
        self.driver.forward()
        logger.info("在页面上点击前进.")

    # 浏览器后退
    def back(self):
        self.driver.back()
        logger.info("在页面上点击后退.")

    # 隐式等待
    def wait(self, seconds):
        self.driver.implicitly_wait(seconds)
        logger.info("等待 %d s."%seconds)

    # 点击关闭当前窗口
    def close(self):
        try:
            self.driver.close()
            logger.info("关闭并退出浏览器.")
        except NameError as e:
            logger.error("退出浏览器失败: %s."%e)

    # 保存图片
    def get_windows_img(self):
        """
        将图片直接保存到项目下.\screenshots下
        :return:
        """
        file_path = os.path.abspath(".")+ '/ErrorImg/' \
                    + time.strftime('%Y%m%d%H%M', time.localtime()) + '.png'
        try:
            self.driver.get_screenshot_as_file(file_path)
            logger.info("已经截图并保存到文件: %s"%file_path)
        except NameError as e:
            logger.error("截图失败: %s."%e)
            # 截图失败重新调用此方法
            self.Get_windows_img()

    # 定位元素
    def find_element(self, selector):
        """
        根据 => 切割字符串
        页面里定位元素的方法:
        submit_btn = "id=>su"
        login_lnk = "xpath=>//*[@id='u1']/a[7]"
        :param selector:
        :return:
        """

        element = ''
        if '=>' not in selector:
            return self.driver.find_element_by_id(selector)
        selector_by = selector.split('=>')[0]
        selector_value = selector.split('=>')[1]

        try:
            if selector_by == 'i' or selector_by == 'id':
                element = self.driver.find_element_by_id(selector_value)
            elif selector_by == 'n' or selector_by == 'name':
                element = self.driver.find_element_by_name(selector_value)
            elif selector_by == 'l' or selector_by == 'link_text':
                element = self.driver.find_element_by_link_text(selector_value)
            elif selector_by == 'p' or selector_by == 'partial_link_text':
                element = self.driver.find_element_by_partial_link_text(selector_value)
            elif selector_by == 't' or selector_by == 'tag_name':
                element = self.driver.find_element_by_tag_name(selector_by)
            elif selector_by == 'x' or selector_by == 'xpath':
                element = self.driver.find_element_by_xpath(selector_value)
            elif selector_by == "s" or selector_by == 'selector_selector':
                element = self.driver.find_element_by_css_selector(selector_value)
            else:
                raise NameError("请输入一个有效类型的目标元素.")
            logger.info("已经通过 %s 成功找到元素: %s ."%(selector_by,selector_value))
        except NoSuchElementException as e:
            self.get_windows_img()
            logger.error("没有找到这样的元素 : %s"%e)
        return element

    # 输入
    def type(self, selector, text):

        el = self.find_element(selector)
        el.clear()
        try:
            el.send_keys(text)
            logger.info("在输入框已经输入: %s ." % text)
        except NameError as e:
            logger.error("输入失败: %s" %e)
            self.get_windows_img()

    # 点击
    def click(self, selector):
        el = self.find_element(selector)
        try:
            # 输出el.text不能写在el.click()后,若写其后,当点击链接跳转页面时会报错,显示元素丢失。
            logger.info("%s 元素被点击." %el.text)
            el.click()
            logger.info("点击成功.")
        except NameError as e:
            logger.error("点击失败: %s"%e)
	
	#获取标题
    def get_page_title(self):
        logger.info("当前页标题: %s" % self.driver.title)
        return self.driver.title
	
	#休眠等待
    @staticmethod
    def sleep(seconds):
        time.sleep(seconds)
        logger.info("休眠 %d s" % seconds)

    # 获取窗口句柄
    def get_now_window_handle(self):
        handle = self.driver.current_window_handle
        logger.info("获取当前窗口: %s"%handle)
        return handle

    #切换页面
    def set_new_handle(self,oldhandle):
        #获取当前浏览器所有句柄
        handles=self.driver.window_handles
        logger.info("获取当前浏览器所有窗口: %s"%handles)
        #将所有句柄循环读取出来,获取到一个没有使用的句柄
        for hd in handles:
            if hd != oldhandle:
                newHandle=hd
        self.driver.close()
        logger.info("关闭原来的窗口: %s"%oldhandle)
        #切换到一个新的句柄
        self.driver.switch_to.window(newHandle)
        logger.info("切换新的窗口: %s"%newHandle)

    #通过","符号对字符进行分割
    def str_split(self,str):
        list=str.split(",")
        return list

3)loggers包主要用于书写日志文件,代码如下:

#coding=utf-8
import logging
import time
import os

class Logger(object):
    def __init__(self, logger):
        '''
        指定保存日志的文件路径,日志级别以及调用文件
        将日志存入到指定的文件中
        :param logger:
        '''

        # 创建一个logger
        self.logger = logging.getLogger(logger)
        self.logger.setLevel(logging.DEBUG)

        # 创建handler,写入日志文件
        rq = time.strftime('%Y%m%d%H%M',time.localtime(time.time()))
        log_path = os.path.abspath(".") + '/logs/' + rq + '.log'
        fh = logging.FileHandler(log_path)
        fh.level = logging.INFO

        # 创建handler, 用于输出控制台
        ch = logging.StreamHandler()
        ch.level = logging.INFO

        # 定义handler的输出格式
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        fh.formatter = formatter
        ch.formatter = formatter

        self.logger.addHandler(fh)
        self.logger.addHandler(ch)

    def getlog(self):
        return self.logger

4)readExcel包主要用于读取dataSase中Excel的数据,代码如下:

#coding=utf-8
import xlrd
from framework import loggers

'''
excel的读取,应为有专用的报告文件所以不需要写入操作
'''
#创建logger实例
logger = loggers.Logger(logger="readExcel_ExcelMethod").getlog()

class ExcelMethod(object):
    def __init__(self,sasepath,index):
        self.sasepath =sasepath
        self.index=index
    #打开文件
    def open_excel(self):
        excelfile=xlrd.open_workbook(self.sasepath)
        return excelfile

    #获取表单
    def get_sheet(self,):
        datasheet=self.open_excel().sheet_by_index(self.index)
        return datasheet

    #获取用例数据
    def get_case(self):
        logger.info("读取excel数据")
        datasheet = self.get_sheet()
        list=[]
        for row in range(1 , datasheet.nrows):
            list.append(datasheet.row_values(row))
        return list

5)saseSelect包,一个简单的工厂设计模式来返回pageobjects的脚本类,主要通过dataSase文件下execl中module中数据来确认脚本,代码如下:

from pageobjects.searchValueSase import SearchValue
from pageobjects.baiduSase import HomePage
from framework import loggers

'''
通过一个简单的工厂设计模式,来确定我们需要的是那个用例类
'''
#创建logger实例
logger = loggers.Logger(logger="saseSelect_SaseSelect").getlog()

class SaseSelect(object):
    def __init__(self,module,browser):
        self.module=module
        self.browser=browser
        
    #返回直接的脚本类
    def sase_Select(self):
        logger.info("执行用例模块:"+self.module)
        if self.module == "百度搜索":
            return SearchValue(self.browser)
        elif self.module == "查看NBA":
            return HomePage(self.browser)

4、pageobjects包,在一个页面会进行的操作脚本
1)baiduSase脚本,我这里做的一个在百度中页面跳转的用例,不存在多数据的更换,代码如下:

#coding=utf-8
from framework import elementUtils

"""
在新闻页面点击体育
"""
class HomePage(elementUtils.BasePage):
    # 点击新闻链接
    def news_click(self):
        self.click("xpath=>//*[@id='s-top-left']/a[1]")
        #获取原窗口句柄
        oldihandle=self.get_now_window_handle()
        #切换为新弹窗
        self.set_new_handle(oldihandle)
        self.click("link_text=>体育")
        self.click("xpath=>//*[@id='col_focus']/div[1]/div[2]/div/div[2]/div/ul/li[1]/a")
        oldihandle=self.get_now_window_handle()
        #切换为新弹窗
        self.set_new_handle(oldihandle)
        return self.get_page_title()

2)searchValueSase脚本,主要在百度中进行搜索一些数据,代码如下:

#coding=utf-8
'''
搜索数据
'''
from framework import elementUtils
class SearchValue(elementUtils.BasePage):
    def search_Value(self,data):
        self.type("id=>kw",data)
        self.click("id=>su")
        self.sleep(2)
        return self.get_page_title()

5、testuits包中,主要放通过unittest生成的每个脚本的单元测试
本包创建的单元测试文件名称必须以test开头,后面加载testuits包中的文件是以test*.py来读取的
1)test_baidu_search文件是pageobject中baiduSase文件的单元测试,代码如下

"""
测试脚本
"""
import unittest
from framework.browerengine import BrowserEngine
from framework.saseSelect import SaseSelect

class ViewNBANews(unittest.TestCase):
    def setUp(self):
        self.engin = BrowserEngine.__new__(BrowserEngine)
        self.driver = self.engin.open_browser()

    def tearDown(self):
        self.engin.quit_browser()

    def test_view_nba_news(self):
        # 初始化百度首页,点击“新闻”链接
        baiduhome = SaseSelect("查看NBA",self.driver).sase_Select()
        self.assertEqual(baiduhome.news_click(),"NBA赛程表_百度搜索")

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

2)test_BaiduSearchValue文件是pageobject中searchValueSase文件的单元测试,代码如下:

import unittest
from framework.browerengine import BrowserEngine
from framework.readExcel import ExcelMethod
from framework.saseSelect import SaseSelect
import os
from ddt import ddt,data,unpack
"""
百度搜索数据
"""
#获取用例的数据
excels=ExcelMethod(os.path.abspath(".")+"/dataSase/testsase.xls",0)
dataSase=excels.get_case()
@ddt
class SearchValue(unittest.TestCase):
    def setUp(self):
        #浏览器加载
        self.engin = BrowserEngine.__new__(BrowserEngine)
        self.driver = self.engin.open_browser()

    def tearDown(self):
        #浏览器退出
        self.engin.quit_browser()
    @data(*dataSase)
    @unpack
    def test_view_nba_news(self,id,title,module,data,result):
        #通过pageobject包的SaseSelect类来确认当前应执行的用例
        Search = SaseSelect(module,self.driver).sase_Select()
        #获取当前页面需要的excel中data数据,并处理转化成list
        strList=Search.str_split(data)
        self.assertEqual(Search.search_Value(strList[0]),result)
if __name__ == '__main__':
    unittest.main()

注:
@ddt注解表示当前类中采用了数据驱动方式进行。
@data(*dataSase)注解表示当前测试中数据源于dataSase的数据,当前数据是直接于execl读取生成的一个可迭代的list数据,每一次代表迭代exec中的一行数据
@unpack用于对当前一行数据进行详细的拆分成一列
在这里插入图片描述

当进行了unpack数据拆分之后在传入当前测试方法中,当前行拆分了多少数据,就需要在方法中传入多少数据。在这里插入图片描述

在①中,传入module数据来确定通过framework包中的saseSelect返回的执行脚本。
在②中,传入的data数据是通过execl中data的一个字符串(胡歌,numb2,numb3),需要对起拆分成list传入脚本中使用
在③中,主要用于与execl中的返回数据进行对比,判断是否正确

6、runn执行主文件
当前文件的主要方式是生成测试报告,代码如下:

#coding=utf-8
import HTMLTestRunner
import unittest
import time
import os

testPath=os.path.abspath(".")+'\\testsuits'
#加载testsuits文件下指定文件名类型是test*的,所有测试文件
suite = unittest.TestLoader().discover(testPath, "test*.py")

#设置报告文件保存路径
report_path = os.path.abspath(".") + '/test_report/' + time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time())) + "HTMLtemplate.html"

if __name__=='__main__':
    fp = open(report_path, 'wb')
    # 执行用例时生成测试报告
    runner =HTMLTestRunner.HTMLTestRunner(
        stream=fp,
        title=u"搜索功能测试报告",
        description=u"用例执行情况"
    )
    # runner=unittest.TextTestRunner()
    runner.run(suite)
    #记得关闭文件
    fp.close()

每句代码意思看上面的注释

最后会产生的结果如下(我的HTMLTestRunner是网上美化的了的,大概意思没变):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值