WebUI之数据驱动与Unittest——excel(三)

目录:

一、xlrd与xlwt读写excel文件
    (一)xlrd涉及到的属性与方法
    (二)xlrd使用实例
    (三)xlutils模块写入excel
    (四)综合实例(一)
    (五)综合实例二(解决实例一的缺陷)
二、pandas读写excel
    (一)pandas读取excel
    (二)pandas写入excel
二、openpyxl读写excel
    (一)openpyxl读取excel
            1.openpyxl读取单元格内容
            2.openpyxl读取整列内容
    (二)openpyxl写入excel

前言:

    前面分享了使用数据驱动结合CSV文件的相关文章,现在本篇分享结合excel的数据驱动。
    本篇将使用两种方法来读写excel,需要安装相应的模块,其他与示例相关的模块,可以看示例导入什么模块,没有就安装。
    excel读写方法一安装模块:xlrd,xlutils
    excel读写方法二安装模块:pandas
    excel读写方法三安装模块:openpyxl

一、xlrd与xlwt读写excel文件

    说明:此方法只能读写以“.xls”结尾的excel文件,也就是相对老版本的文件,所以准备数据时需要注意文件的尾缀。

(一)xlrd涉及到的属性与方法

    第一级:打开文件

  • open_workbook()    打开文件,所有后续操作的前提,需要参数:文件名

    第二级:打开表单的方法:
  • sheet_by_index()    通过索引数字打开表单,需要参数:索引号。
  • sheets()     获取所有表单,返回的是一个表单列表,需要通过列表索引访问。
  • sheet_by_name()    通过表单名访问表单,需要参数:表单名



    第三级:读取行列的方法:

  • row(行号)    读取行数据,数据类型为列表,样式为,单元格类型:值
  • col_values(colx,start_rowx=0, end_rowx=None)    
    若读取整列,只需要行号 colx,直到读取完所有数据(模块自动识别);
    若需要从指定行开始读取,需要设置参数start_rowx=行号
    若需要到指定行结束读取,需要设置参数end_rowx=行号

    第三级:行列的属性

  • ncols    列数
  • nrows    行数

(二)xlrd使用实例

1.简单用法

image.png

image.png
import xlrd,xlwt
import pandas

#打印第一张表的第一
text1=xlrd.open_workbook('CVS数据表.xls').sheet_by_index(1).row(0)
#返回单元格类型+内容
print("序号:1")
print(text1)
#通过表名的索引,打印第一张表的第一列
text=xlrd.open_workbook('CVS数据表.xls').sheet_by_index(0).col_values(0)
print("序号:2")
print(text)
#打印第一张表的第一列,通过sheets()加索引方式,为前面一种方法的原始版,前面的相当于封装过的,推介使用前面一种
text=xlrd.open_workbook('CVS数据表.xls').sheets()[0].col_values(0)
print("序号:3")
print(text)
#通过表名访问表单
text=xlrd.open_workbook('CVS数据表.xls').sheet_by_name('Sheet1').col_values(0)
print("序号:4")
print(text)

结果:


image.png

2.xlrd实际使用中常用方法

    接续前面的内容,在读取内容时,最重要的两点是跳过首行读取数据,和获取行数

import xlrd,xlwt

#读取第二张表的第一列,从第二行开始
text=xlrd.open_workbook('CVS数据表.xls').sheet_by_name('Sheet2').col_values(0,start_rowx=1)
print(text)
#一般用在循环中
#获取列数
cols_account=xlrd.open_workbook('CVS数据表.xls').sheet_by_name('Sheet2').ncols
print(cols_account)
#获取行数
rows_account=xlrd.open_workbook('CVS数据表.xls').sheet_by_name('Sheet2').nrows
print(rows_account)

3.封装xlrd读取excel

def read_excel(filename,sheet_index,col_num):
    import xlrd, xlwt
    #跳过首行
    text=xlrd.open_workbook(filename).sheet_by_index(sheet_index).col_values(col_num,start_rowx=1)
    #必须要有返回值,没有返回值,调用函数得到的是空值
    return text

write_excel('CVS数据表.xls',1,0)


(三)xlutils模块写入excel

import xlrd
from xlutils.copy import copy

#xlrd模块打开表格
book=xlrd.open_workbook('CVS数据表.xls')
#xlutils的copy文件的copy方法复制一个表,用于实际操作
book_copy=copy(book)
#copy到的对象获取表单
sheet=book_copy.get_sheet(1)
#写入指定表单单元格:write(行号,列号,要写入的内容)
sheet.write(1,1,'pass')
#保存写入的内容,这不能漏掉,不然白写了
book_copy.save('CVS数据表.xls')

封装方法,方便后续调用:

def write_excel(filename,sheet_index,row_num,col_num,content):
    from xlutils.copy import copy
    import xlrd
    book=xlrd.open_workbook(filename)
    book_copy=copy(book)
    sheet=book_copy.get_sheet(sheet_index)
    sheet.write(row_num,col_num,content)
    book_copy.save(filename)
#下面只是为了看的更直观,实际可以直接按顺序写入数据
write_excel(filename='CVS数据表.xls',sheet_index=1,row_num=2,col_num=1,content='pass')

(四)综合实例(一)

原创内容:
    由于使用Unittest结合数据驱动会自动读取数据,并重复执行,所以想要得到一个和取得数据相同的索引比较困难,所以需要改进代码。


思路:由于我们读取到的数据是一个列表,那么我们可以获取列表内数据的索引,当取一个数据时得到一个索引。但我们写的读取数据去掉了首行,所以最后在使用索引时需要+1才能匹配上。
缺陷:当列表内存在重复的数据时,会返回第一个出现的索引,所以不能用于列表数据有重复值的场合,比如注册测试。改进方法可以看(五)

from selenium import webdriver
import unittest,time
from ddt import ddt,data,unpack

def write_excel(filename,sheet_index,row_num,col_num,content):
    from xlutils.copy import copy
    import xlrd
    book=xlrd.open_workbook(filename)
    book_copy=copy(book)
    sheet=book_copy.get_sheet(sheet_index)
    sheet.write(row_num,col_num,content)
    book_copy.save(filename)

def read_excel(filename,sheet_index,col_num):
    import xlrd, xlwt
    #跳过首行
    text=xlrd.open_workbook(filename).sheet_by_index(sheet_index).col_values(col_num,start_rowx=1)
    return text


search_data=read_excel('CVS数据表.xls',1,0)
print(search_data)



#使用装饰器,表明该测试集采用ddt驱动
@ddt
#使用unittest必须先使用class定义一个类,且以Test开头,并继承unittest.TestCase
class Testbaidu_search(unittest.TestCase):
    # 必须使用@classmethod装饰器,这是使用tearDownClass与setUpClass的基本要求
    #setUpClass表示下面的代码Testbaidu_search测试集只会在开始时运行一次
    @classmethod
    def setUpClass(cls) -> None:
        global driver
        driver=webdriver.Chrome()
        driver.get('https://www.baidu.com/')
    #data取数据,如果传入的是一个变量接收的数据集,那么需要在变量前面加*
    @data(*search_data)
    #unpack解压数据包
    # @unpack
    #由于只有一个变量,在测试用例用中,加入一个变量名,用于接收解压到的数据,针对多个的情况后面再详细说明


    def test_search(self,value):
        """
        非常重要的核心:
        由于使用Unittest结合数据驱动会自动读取数据,并重复执行,所以想要得到一个和取得数据相同的索引比较困难.
        由于我们读取到的数据是一个列表,那么我们可以获取列表内数据的索引,当取一个数据时得到一个索引。
        但我们写的读取数据去掉了首行,所以最后在使用索引时需要+1才能匹配上
        :param value:
        :return:
        """
        i=search_data.index(value)
        print(i)
        driver.find_element_by_id('kw').send_keys(value)
        text=driver.find_element_by_id('kw').get_attribute('value')
        driver.find_element_by_id('su').click()
        time.sleep(3)
        driver.find_element_by_id('kw').clear()
        if text in ['孔子','孟子']:
            write_excel(filename='CVS数据表.xls', sheet_index=1, row_num=i+1, col_num=1, content='pass')
        else:
            write_excel(filename='CVS数据表.xls',sheet_index=1,row_num=i+1,col_num=1,content='unpass')
        #原创内容,转载请注明出处,有用请点赞


    @classmethod
    # tearDownClass表示下面的代码Testbaidu_search测试集只会在结束时运行一次
    def tearDownClass(cls) -> None:
        driver.quit()

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

(五)综合实例二(解决实例一的缺陷)

原创内容:

思路:
观察测试运行结果,每次运行之后,产生的用例会有一个名字,并且里面有一个不重复的序号,那么我们可以想办法获取这个数字。
查看unittest.TestCase的方法中有一个id方法,返回运行用例的名称,也就是Test Results下的全部完整路径。但是这个方法只能在用例里面调用才能得到当前运行用例的名称,结果类型为字符串。然后进行字符串提取,并进行类型转换(int型)。
图片是因为selenium访问百度太频繁了,必须要验证才能继续访问导致的。

image.png
定义成方法,只是好看一点:
        def get_case_num():
            import re
            case_name = unittest.TestCase.id(self)
            pattern = '.*_+(\d{1,3})_+'
            case_num = int(re.findall(pattern, case_name)[0])
            return case_num
        i=get_case_num()
#后面引用i,就不用+1了,因为用例就是以1开头的。

完整代码:

from selenium import webdriver
import unittest,time
from ddt import ddt,data,unpack

def write_excel(filename,sheet_index,row_num,col_num,content):
    from xlutils.copy import copy
    import xlrd
    book=xlrd.open_workbook(filename)
    book_copy=copy(book)
    sheet=book_copy.get_sheet(sheet_index)
    sheet.write(row_num,col_num,content)
    book_copy.save(filename)

def read_excel(filename,sheet_index,col_num):
    import xlrd, xlwt
    #跳过首行
    text=xlrd.open_workbook(filename).sheet_by_index(sheet_index).col_values(col_num,start_rowx=1)
    return text


search_data=read_excel('CVS数据表.xls',1,0)
print(search_data)



#使用装饰器,表明该测试集采用ddt驱动
@ddt
#使用unittest必须先使用class定义一个类,且以Test开头,并继承unittest.TestCase
class Testbaidu_search(unittest.TestCase):
    # 必须使用@classmethod装饰器,这是使用tearDownClass与setUpClass的基本要求
    #setUpClass表示下面的代码Testbaidu_search测试集只会在开始时运行一次
    @classmethod
    def setUpClass(cls) -> None:
        global driver
        driver=webdriver.Chrome()
        driver.get('https://www.baidu.com/')
    #data取数据,如果传入的是一个变量接收的数据集,那么需要在变量前面加*
    @data(*search_data)
    #unpack解压数据包
    # @unpack
    #由于只有一个变量,在测试用例用中,加入一个变量名,用于接收解压到的数据,针对多个的情况后面再详细说明


    def test_search(self,value):
        print(search_data)
        """
        非常重要的核心:
        由于使用Unittest结合数据驱动会自动读取数据,并重复执行,所以想要得到一个和取得数据相同的索引比较困难.
        由于我们读取到的数据是一个列表,那么我们可以获取列表内数据的索引,当取一个数据时得到一个索引。
        但我们写的读取数据去掉了首行,所以最后在使用索引时需要+1才能匹配上
        :param value:
        :return:
        """

        def get_case_num():
            import re
            case_name = unittest.TestCase.id(self)
            pattern = '.*_+(\d{1,3})_+'
            case_num = int(re.findall(pattern, case_name)[0])
            return case_num
        i=get_case_num()

        driver.find_element_by_id('kw').send_keys(value)
        text=driver.find_element_by_id('kw').get_attribute('value')
        driver.find_element_by_id('su').click()
        time.sleep(3)
        driver.find_element_by_id('kw').clear()
        if text in ['孔子','孟子']:
            write_excel(filename='CVS数据表.xls', sheet_index=1, row_num=i, col_num=1, content='pass')
        else:
            write_excel(filename='CVS数据表.xls',sheet_index=1,row_num=i,col_num=1,content='unpass')
        #原创内容,转载请注明出处,有用请点赞


    @classmethod
    # tearDownClass表示下面的代码Testbaidu_search测试集只会在结束时运行一次
    def tearDownClass(cls) -> None:
        driver.quit()

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

二、pandas读写excel

(一)pandas读取excel

def read_excel_pandas(filename,sheet_name,col_name):
    import pandas as pd
    #pandas可以直接根据读取到的列名,得到到想要列的数据,为pandas数据类型,可以通过列表索引访问列的内容
    excel_data=pd.read_excel(filename,sheet_name)[col_name]
    #需要转换为list,才是我们需要的可迭代对象
    return list(excel_data)

(二)pandas写入excel

    查看源代码,发现to_excel方法没有读写模式控制,不能像csv一样在新表中进行追加,写一次数据,就覆盖了整个表,不能达到我们的要请求


image.png

    如下代码,若start_row不断进行迭代,最终文件中只有最后一行的数据,如果想要使用pandas达到想要的效果,需要结合其他openpyxl库,这方面,暂时还未进行学习总结。

def write_excel_pandas(filename,sheet_name,col_text,result,start_col,start_row):
    import pandas as pd
    #简单测试输入框,只有两个字段,一个是输入内容,一个接收结果
    data_dic = {
        'name': col_text,
        'result': result
    }
    #index=[0]必须写,不然会报错,我目前是这样的
    excel_data = pd.DataFrame(data=data_dic, index=[0])
    #mode='a'为追加模式,这种不停写入旧文件一般都用追加,不会覆盖原来的内容
    # header=False,不会在写入数据时添加首行标题,否则结果看起来会很乱
    excel_data.to_excel(filename,sheet_name=sheet_name, startcol=start_col,startrow=start_row,index=False,header=False,encoding='utf-8')

附源码:

image.png

from selenium import webdriver
import unittest,time
from ddt import ddt,data,unpack

def read_excel_pandas(filename,sheet_name,col_name):
    import pandas as pd
    excel_data=pd.read_excel(filename,sheet_name)[col_name]
    return list(excel_data)

def write_excel_pandas(filename,sheet_name,col_text,result,start_col,start_row):
    import pandas as pd
    #简单测试输入框,只有两个字段,一个是输入内容,一个接收结果
    data_dic = {
        'name': col_text,
        'result': result
    }
    #index=[0]必须写,不然会报错,我目前是这样的
    excel_data = pd.DataFrame(data=data_dic, index=[0])
    #mode='a'为追加模式,这种不停写入旧文件一般都用追加,不会覆盖原来的内容
    # header=False,不会在写入数据时添加首行标题,否则结果看起来会很乱
    excel_data.to_excel(filename,sheet_name=sheet_name, startcol=start_col,startrow=start_row,index=False,header=False,encoding='utf-8')

search_data=read_excel_pandas('CVS数据表.xls','Sheet1','name')
print(search_data)



#使用装饰器,表明该测试集采用ddt驱动
@ddt
#使用unittest必须先使用class定义一个类,且以Test开头,并继承unittest.TestCase
class Testbaidu_search(unittest.TestCase):
    # 必须使用@classmethod装饰器,这是使用tearDownClass与setUpClass的基本要求
    #setUpClass表示下面的代码Testbaidu_search测试集只会在开始时运行一次
    @classmethod
    def setUpClass(cls) -> None:
        global driver
        driver=webdriver.Chrome()
        driver.get('https://www.baidu.com/')
    #data取数据,如果传入的是一个变量接收的数据集,那么需要在变量前面加*
    @data(*search_data)
    #unpack解压数据包
    # @unpack
    #由于只有一个变量,在测试用例用中,加入一个变量名,用于接收解压到的数据,针对多个的情况后面再详细说明


    def test_search(self,value):
        print(search_data)
        """
        非常重要的核心:
        由于使用Unittest结合数据驱动会自动读取数据,并重复执行,所以想要得到一个和取得数据相同的索引比较困难.
        由于我们读取到的数据是一个列表,那么我们可以获取列表内数据的索引,当取一个数据时得到一个索引。
        但我们写的读取数据去掉了首行,所以最后在使用索引时需要+1才能匹配上
        :param value:
        :return:
        """

        def get_case_num():
            import re
            case_name = unittest.TestCase.id(self)
            pattern = '.*_+(\d{1,3})_+'
            case_num = int(re.findall(pattern, case_name)[0])
            return case_num
        row_num=get_case_num()

        driver.find_element_by_id('kw').send_keys(value)
        text=driver.find_element_by_id('kw').get_attribute('value')
        driver.find_element_by_id('su').click()
        time.sleep(3)
        driver.find_element_by_id('kw').clear()
        if text in ['孔子','孟子']:
            write_excel_pandas('CVS数据表2.xls','Sheet2',value,'pass',start_col=0,start_row=row_num)
        else:
            write_excel_pandas('CVS数据表2.xls','Sheet2',value,'unpass',start_col=0,start_row=row_num)
        #原创内容,转载请注明出处,有用请点赞


    @classmethod
    # tearDownClass表示下面的代码Testbaidu_search测试集只会在结束时运行一次
    def tearDownClass(cls) -> None:
        driver.quit()

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


三、openpyxl读写excel

注意:
openpyxl只能读写“.xlsx”尾缀的excel文件,不能读写“.xls”

(一)openpyxl读取excel数据

1.读取指定单元格

def read_excel_openpyxl(filename,sheet_name,row_num,col_num):
    from openpyxl import load_workbook
    """
    注意,必须是.xlsx结尾的文件才能被操作,而且不能是被重命名未知的文件
    :param filename:
    :param sheet_name:
    :return:
    """
    book=load_workbook(filename)
    #由于默认激活的是第一张sheet,因此需要book.active=book[sheet_name]定义需要激活名称的sheet
    ws=book.active=book[sheet_name]
    data=ws.cell(row_num,col_num)
    book.save(filename)
    return data.value

2.读取整列

def read_excel_openpyxl_col(filename,sheet_name,col_name):
    """
    去除首行读取列,需要读取首行,就将row+1改为row
    :param filename:
    :param sheet_name: 如Sheet1
    :param col_name: 如A,B列
    :return:
    """

    from openpyxl import load_workbook
    wb = load_workbook(filename = filename)
    sheet_ranges = wb[sheet_name]
    col_content=sheet_ranges[col_name]
    content=[]
    for row in range(len(col_content)-1):
        content.append(col_content[row+1].value)
    return content

(二)openpyxl写入excel

def write_excel_openpyxl(filename,sheet_name,row_num,col_num,value):
    from openpyxl import load_workbook
    """
    注意,必须是.xlsx结尾的文件才能被操作,而且不能是被重命名未知的文件
    :param filename:
    :param sheet_name:
    :return:
    """
    book=load_workbook(filename)
    #由于默认激活的是第一张sheet,因此需要book.active=book[sheet_name]定义需要激活名称的sheet
    ws=book.active=book[sheet_name]
    data=ws.cell(row_num,col_num,value)
    book.save(filename)
    print(data.value)

(三)实例

实例就不写了,和前面一样,只是将读写的函数修改以下就行


原创内容,转载请注明出处,有用请点赞!!!!

你可能感兴趣的:(WebUI之数据驱动与Unittest——excel(三))