• Pandas、Excel实现 Pytest 数据驱动


    引言

    前面分享的推文 自动化测试必会—数据驱动DDT  介绍过 unittest 框架中操作 JSON 和 YAML 文件实现数据驱动。那么在 pytest 中,又该如何实现呢?

    Pytest 操作 JSON/YAML 文件实现数据驱动

    首先按照使用 pytest 进行数据驱动的基本思路创建一个读取 JSON 文件和 YAML 文件的方法:

    def test_read_data_from_json_yaml(data_file):
     
        return_value = []
     
        data_file_path = os.path.abspath(data_file)
     
        print(data_file_path)
     
        _is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
     
        with codecs.open(data_file_path, 'r', 'utf-8') as f:
     
            #从YAML或JSON文件中加载数据
     
            if _is_yaml_file:
     
                data = yaml.safe_load(f)
     
            else:
     
                data = json.load(f)
     
        for i, elem in enumerate(data):
     
            if isinstance(data, dict):
     
                key, value = elem, data[elem]
     
                if isinstance(value, dict):
     
                    case_data = []
     
                    for v in value.values():
     
                        case_data.append(v)
     
                    return_value.append(tuple(case_data))
     
                else:
     
                    return_value.append((value,))
     
        return return_value

    test_read_data_from_json_yaml 这个函数方法,实现了自动读取 JSON 文件和 YAML 文件,并且把 JSON 文件和 YAML 文件中的数据提取出来,并按照 pytest.mark.parametrize 可接收的方式返回。

    有了这个函数方法,JSON 或者 YAML 文件的数据通过此方法就可以转换成 pytest.mark.parametrize 认可的格式直接传入了。

    下面实践一下,在 APITest 项目根目录下创建如下文件目录:

    |--APITest
     
        |--tests_pytest_ddt
     
            |--test_baidu_ddt.py
     
            |--test_baidu_ddt.json
     
            |--test_baidu_ddt.yaml
     
            |--test_baidu_ddt.xlsx
     
            |--__init__.py
     
            |--conftest.py

    其中,test_baidu_ddt.json 文件的内容如下:

    { 
     
      "case1": {
     
      "search_string": "testing",
     
      "expect_string": "Testing"
     
      },
     
      "case2": {
     
      "search_string": "hello_world.com",
     
      "expect_string": "Testing"
     
      }
     
    }

    test_baidu_ddt.yaml 文件的内容如下:

    "case1":
     
      "search_string": "testing"
     
      "expect_string": "Testing"
     
    "case2": 
     
      "search_string": "hello_world.com"
     
      "expect_string": "Testing"

    test_baidu_ddt.py 文件的代码如下:

    import codecs
    import json
    import os
    import time
    import pytest
    import yaml
     
     
    def test_read_data_from_json_yaml(data_file):
     
        return_value = []
     
        data_file_path = os.path.abspath(data_file)
     
        print(data_file_path)
     
        _is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
     
        with codecs.open(data_file_path, 'r', 'utf-8') as f:
     
            #从YAML或JSON文件中加载数据
     
            if _is_yaml_file:
     
                data = yaml.safe_load(f)
     
            else:
     
                data = json.load(f)
     
        for i, elem in enumerate(data):
     
            if isinstance(data, dict):
     
                key, value = elem, data[elem]
     
                if isinstance(value, dict):
     
                    case_data = []
     
                    for v in value.values():
     
                        case_data.append(v)
     
                    return_value.append(tuple(case_data))
     
                else:
     
                    return_value.append((value,))
     
        return return_value
     
     
    @pytest.mark.baidu
    class TestBaidu:
     
        @pytest.mark.parametrize('search_string, expect_string',  test_read_data_from_json_yaml('tests_pytest_ddt/test_baidu_ddt.yaml'))
     
        def test_baidu_search(self, login, search_string, expect_string):
     
            driver, s, base_url = login
     
            driver.get(base_url + "/")
     
            driver.find_element_by_id("kw").send_keys(search_string)
     
            driver.find_element_by_id("su").click()
     
            time.sleep(2)
     
            search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
     
            print(search_results)
     
            assert (expect_string in search_results) is True
     
     
     
    if __name__ == "__main__":
     
        pytest.main(['-s', '-v'])

    此文件中的代码与 Pytest 测试框架——数据驱动 中讲解的代码几乎相同,唯一的改变在于增加了一个新方法 test_read_data_from_json_yaml,另外 @pytest.mark.parametrize 的参数,从直接提供参数变成了从文件提供参数。

    (test_read_data_from_json_yaml('tests_pytest_ddt/test_baidu_ddt.yaml'))

    在命令行中通过如下方式运行:

    D:Python_TestAPITest>pytest tests_pytest_ddt -s -v

    运行结束后查看结果如下:
    图片
    可以看到,两个测试用例都执行了,并且 YAML 文件中的数据被正确读取。

    那么如果我们现在要执行 JSON 文件中的数据该如何操作呢?把上述代码中传入的 yaml 文件后缀换成 json 文件后缀,再次执行即可。

    Pytest 操作 Excel 文件实现数据驱动

    在实际应用中,也有很多公司使用 Excel 来做数据驱动。在 python 中,读写 Excel 的 library 很多,常见的有 xlrd、xlwt,以及openpyxl。由于 xlrd 和 xlwt 只能分别用作读和写,实现同样的读写操作,它的代码行数较多,故逐渐变得不再流行。所以下面将重点介绍 openpyxl 的使用。

    openpyxl 安装

    pip install openpyxl

    openpyxl 使用

    from openpyxl import load_workbook, Workbook
     
    if __name__ == "__main__":
        # 创建一个workbook
        file_name = r'c:	est.xlsx'
        wb = Workbook()
     
        # 创建一个sheet,名为Testing,把它插入到最前的位置
        wb.create_sheet('Testing',0)
     
        # 创建一个sheet,名为TEST,把它插入index为1的位置
        wb.create_sheet('TEST',1)
     
        # 保存表格
        wb.save(file_name)
        
        # 读和写
        # 初始化表格
        wb2 = load_workbook(file_name)
     
        # 读,获取所有的sheet名称
        print(wb2.sheetnames)
     
     
        # 获取sheet名为Testing的表格
        s = wb2['Testing']
     
        # 将A1行的值设置为Testing
        s['A1'] = 'Testing'
     
        # 将第2行,第一列的值设置为1
        s.cell(row=2, column=1).value = 1
     
        # 打印第2行第一列单元格的值 --方法1
        print(s.cell(row=2,column=1).value)
     
        # 打印第2行第一列单元格的值 --方法2
        print(s['A2'].value)
     
        # 保存表格
        wb.save(file_name)

    如上述代码块所示,简单介绍了 openpyxl 的用法,涉及创建表格、创建 sheet 名、读取单元格的值、设置单元格的值等部分。你可以看出使用 openpyxl 操作 excel 是相对比较简单的。

    openpyxl 结合 pytest 实现数据驱动

    文件 test_baidu_ddt.xlsx 的内容如下(sheet 名 Testing):
    图片
    来写个读 Excel 的方法,代码如下:

    def test_read_data_from_excel(excel_file, sheet_name):
     
        return_value = []
     
        # 判断文件是否存在
        if not os.path.exists(excel_file):
            raise ValueError("File not exists")
     
        # 打开指定的sheet
        wb = load_workbook(excel_file)
     
        # 按照pytest接受的格式输出数据
        for s in wb.sheetnames:
            if s == sheet_name:
                sheet = wb[sheet_name]
                for row in sheet.rows:
                    return_value.append([col.value for col in row])
     
        # 第一行数据是标题,故skip掉
        return return_value[1:]

    更新 test_baidu_ddt.py 文件,把 test_read_data_from_excel 的方法加进去,更新后的代码如下:

    import codecs
    import json
    import os
    import time
    import pytest
    import yaml
    from openpyxl import load_workbook
     
     
    def test_read_data_from_json_yaml(data_file):
        return_value = []
        data_file_path = os.path.abspath(data_file)
        print(data_file_path)
     
        _is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
        with codecs.open(data_file_path, 'r', 'utf-8') as f:
            # 从YAML或JSON文件中加载数据
            if _is_yaml_file:
                data = yaml.safe_load(f)
            else:
                data = json.load(f)
     
        for i, elem in enumerate(data):
            if isinstance(data, dict):
                key, value = elem, data[elem]
                if isinstance(value, dict):
                    case_data = []
                    for v in value.values():
                        case_data.append(v)
                    return_value.append(tuple(case_data))
                else:
                    return_value.append((value,))
        return return_value
     
     
    def test_read_data_from_excel(excel_file, sheet_name):
     
        return_value = []
        if not os.path.exists(excel_file):
            raise ValueError("File not exists")
     
        wb = load_workbook(excel_file)
        for s in wb.sheetnames:
            if s == sheet_name:
                sheet = wb[sheet_name]
                for row in sheet.rows:
                    return_value.append([col.value for col in row])
        print(return_value)
        return return_value[1:]
     
     
    @pytest.mark.baidu
    class TestBaidu:
        # 注意,此处调用我换成了读Excel的方法
     
        @pytest.mark.parametrize('search_string, expect_string',  test_read_data_from_excel(r'DPython_TestAPITest	ests_pytest_ddt	est_baidu_ddt.xlsx', 'Testing'))
     
        def test_baidu_search(self, login, search_string, expect_string):
            driver, s, base_url = login
            driver.get(base_url + "/")
            driver.find_element_by_id("kw").send_keys(search_string)
            driver.find_element_by_id("su").click()
            time.sleep(2)
     
            search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
            print(search_results)
            assert (expect_string in search_results) is True
     
     
    if __name__ == "__main__":
        pytest.main(['-s''-v''tests_pytest_ddt'])

    在命令行中通过如下方式再次运行即可:

    D:Python_TestAPITest>pytest tests_pytest_ddt -s -v

    运行后查看结果,会发现测试被正确执行,测试数据是从 Excel 指定的 sheet 名中获取的。

    Pandas 实现数据驱动

    openpyxl 操作 Excel 非常简洁,但是相对于 Pandas 来说,还不够简洁,而且 openpyxl 运算效率不如 Pandas,特别是当表格行项目过多时,openpyxl 运算较慢。

    Pandas 是一个强大的分析结构化数据的工具集,它的使用基础是 Numpy(提供高性能的矩阵运算);Pandas 用于数据挖掘和数据分析,同时也提供数据清洗功能。使用 Pandas 操作 Excel 数据,就会变得十分简单。

    Pandas 安装

    # pandas默认依赖xlrd库,故先安装xlrd
    pip install xlrd
     
    # 安装Pandas
    pip install Pandas

    Pandas 语法

    import Pandas as pd
     
    # 首先初始化,engine默认是xlrd
    s = pd.ExcelFile(path_or_buffer, engine=None)
     
     
     
    # 接着parse
    s.parse(sheet_name=0,header=0,names=None,index_col=None,usecols=None,
     
    squeeze=False,converters=None,true_values=None,false_values=None,
     
    skiprows=None,nrows=None,na_values=None,parse_dates=False,
     
    date_parser=None,thousands=None,comment=None,skipfooter=0,
     
    convert_float=True,mangle_dupe_cols=True,**kwds,)

    Pandas 读取 Excel 文件非常简单,首先使用 Pandas 初始化 ExcelFile。其两个参数path_or_buffer 是我们要读取的文件路径。

    Excel 文件名称建议使用英文路径及英文命名方式,不要使用中文。

    import pandas as pd
     
    path_or_buffer = r'D:Python_TestAPITest	ests_pytest_ddt	est_baidu_ddt.xlsx'

    engine 是供 Pandas 使用的 engine,可选项为“xlrd”、“openpyxl”、“odf”、“pyxlsb”,如果不提供,默认使用 xlrd。

    parse 函数的参数
    初始化后,可以使用 s.parse() 函数。parse 函数有非常多的参数,在此只列出常用的几个。

    sheet_name: Excel 的 sheet 名
    sheet_name 可以是整型数字、列表名,或者上述两者组合。

    # 通过整型数字读取。读取第一个sheet。Pandas sheet名下标以0开始
    s = pd.ExcelFile(path_or_buffer, sheet_name = 0)
     
    # 通过列表名读取
    data = s.parse(sheet_name = 'Testing')
     
    # 通过index读取。读取第一个sheet
    data = s.parse(sheet_name = 0)
     
    #组合读取。读取第4个sheet,名为Testing的sheet以及第7个sheet
    data = s.parse(sheet_name = [3, 'Testing', 'Sheet6'])

    header:使用哪一行作为列名
    header 默认值为 0,即第一行,也可以设置为 [0, x]。
    (例如 [0,1] 意味着将前两行作为多重索引)

    data = s.parse(sheet_name = 'Testing', header = 0)

    需要注意:Pandas 默认使用第一行为 header,所以在 Excel 里,第一行必须是 title,如果第一行是数据,将会导致第一行数据被遗漏。如果不想要 header,可以参数传递 header=None。

    usecols:待读取的列
    usecols 接收整型,从 0 开始,例如 [0, 1, 2],也可以使用列名例如 “A:D, F”,表示读取 A 到 D 列,以及 F 列。

    data = s.parse(sheet_name = 'Testing', usecols='A:D')

    skiprows:读取时,跳过特定行
    skiprows=n,跳过前 n 行;skiprows = [a, b, c],跳过第 a+1、b+1、c+1 行(索引从0开始)

    data = s.parse(sheet_name = 'iTesting', skiprows = [1,2,3])

    nrows:需要读取的行数
    仅仅列出要读取的行数

    data = s.parse(sheet_name = 'Testing', nrows = 3)

    Pandas 结合 Pytest 实现数据驱动

    了解了 Pandas 语法后,来看下如何使用 Pandas 读取 Excel 数据:

    def test_read_data_from_pandas(excel_file, sheet_name):
     
        if not os.path.exists(excel_file):
            raise ValueError("File not exists")
     
        # 初始化
        s = pd.ExcelFile(excel_file)
     
        # 解析Excel Sheet
        df = s.parse(sheet_name)
     
        # 以list格式返回数据
        return df.values.tolist()

    可以看到,使用 pandas 读取 Excel 数据更加简洁方便。
    最后,来更新下 test_baidu_ddt.py 文件,更新后的代码如下:

    import codecs
    import json
    import os
    import time
    import pytest
    import yaml
    from openpyxl import load_workbook
    import pandas as pd
     
    # 读取Yaml文件和Json文件
    def test_read_data_from_json_yaml(data_file):
        return_value = []
        data_file_path = os.path.abspath(data_file)
        print(data_file_path)
     
        _is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
        with codecs.open(data_file_path, 'r', 'utf-8') as f:
            #从YAML或JSON文件中加载数据
            if _is_yaml_file:
                data = yaml.safe_load(f)
            else:
                data = json.load(f)
     
        for i, elem in enumerate(data):
            if isinstance(data, dict):
                key, value = elem, data[elem]
                if isinstance(value, dict):
                    case_data = []
                    for v in value.values():
                        case_data.append(v)
                    return_value.append(tuple(case_data))
                else:
                    return_value.append((value,))
        return return_value
     
    # 读取Excel 文件 -- openpyxl
    def test_read_data_from_excel(excel_file, sheet_name):
        return_value = []
     
        if not os.path.exists(excel_file):
            raise ValueError("File not exists")
     
        wb = load_workbook(excel_file)
        for s in wb.sheetnames:
            if s == sheet_name:
                sheet = wb[sheet_name]
                for row in sheet.rows:
                    return_value.append([col.value for col in row])
        print(return_value)
        return return_value[1:]
     
    # 读取Excel文件 -- Pandas
    def test_read_data_from_pandas(excel_file, sheet_name):
        if not os.path.exists(excel_file):
            raise ValueError("File not exists")
        s = pd.ExcelFile(excel_file)
        df = s.parse(sheet_name)
        return df.values.tolist()
     
    @pytest.mark.baidu
    class TestBaidu:
     
        @pytest.mark.parametrize('search_string, expect_string',  test_read_data_from_pandas(r'D:Python_TestAPITest	ests_pytest_ddt	est_baidu_ddt.xlsx', 'Testing'))
        def test_baidu_search(self, login, search_string, expect_string):
            driver, s, base_url = login
            driver.get(base_url + "/")
            driver.find_element_by_id("kw").send_keys(search_string)
            driver.find_element_by_id("su").click()
            time.sleep(2)
     
            search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
            print(search_results)
            assert (expect_string in search_results) is True
     
     
    if __name__ == "__main__":
        pytest.main(['-s', '-v', 'tests_pytest_ddt'])

    在命令行中通过如下方式再次运行即可:

    D:Python_TestAPITest>pytest tests_pytest_ddt -s -v

    运行后查看结果,可以发现测试被正确执行,测试数据是通过 Pandas 从 Excel 指定的 sheet 名中获取的。


    事实上,Pandas 不仅仅能读取 Excel 文件,还可以读取 HTML 文件、TXT 文件、JSON 文件、数据库文件 (.sql) 等。在数据分析领域,Pandas 使用非常广泛,更多具体的 Pandas 使用,请自行查阅。

    欢迎关注【无量测试之道】公众号,回复【领取资源】
    Python编程学习资源干货、
    Python+Appium框架APP的UI自动化、
    Python+Selenium框架Web的UI自动化、
    Python+Unittest框架API自动化、

    资源和代码 免费送啦~
    文章下方有公众号二维码,可直接微信扫一扫关注即可。

    备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:

     添加关注,让我们一起共同成长!

  • 相关阅读:
    江西师范大学瑶湖校区图片
    什么是sp?
    教育技术学硕士点学校排名
    西南师大教育技术学专业2005年入学考试信息
    什么是"工包"?
    买书网址
    2006年全国教育技术学专业新增硕士点
    今天终于拿到书了
    2007年教育学专业基础综合考试大纲(重要部分) ——下载地址
    电脑DIY推荐
  • 原文地址:https://www.cnblogs.com/Wu13241454771/p/14374643.html
Copyright © 2020-2023  润新知