• Python接口自动化之数据驱动


    VOL 118

    01

    2020-05

    今天距2021年244天

    这是ITester软件测试小栈第118次推文

    点击上方蓝字“ITester软件测试小栈“关注我,每周一五早上 07:30准时推送。

    微信公众号后台回复“资源测试工具包”领取测试资源,回复“21天打卡”一起学习成长,打怪升级。

    本文6421字,阅读约需17分钟

    在上一篇Python接口自动化测试系列文章:Python接口自动化之登录接口测试,主要介绍接口概念、接口用例设计及登录接口测试实战。

    以下主要介绍使用openpyxl模块操作excel及结合ddt实现数据驱动。

    如果感觉文章很长,那就是文章很长。

                                

    在此之前,我们已经实现了用unittest框架编写测试用例,实现了请求接口的封装,这样虽然已经可以完成接口的自动化测试,但是其复用性并不高。

    我们看到每个方法(测试用例)的代码几乎是一模一样的,试想一下,在我们的测试场景中,一个登录接口有可能会有十几条到几十条测试用例,如果每组数据都编写一个方法,这样将会有更多的重复项代码,不仅执行效率不高,也不好维护。

    接下来将会对框架进行优化,采用数据驱动方式:

    1.把测试数据用excel表格管理起来,代码做封装;

    2.用ddt来驱动测试,两部分相互独立。

    openpyxl模块

    openpyxl模块介绍

    openpyxl是python第三方模块,运用openpyxl库可以进行excel的读和写。

    在了解openpyxl模块之前,我们需要先熟悉excel的结构,才能更好理解openpyxl是如何操作excel。

    从外到内,首先是一个excel文件(名),打开excel之后,会看到底部有一个或多个sheet(工作簿),每个sheet里有很多单元格,总体来说,主要分为三个层级。

    在opnepyxl里面,一个Excel文件对应着一个Workbook对象, 一个Sheet对应着一个Worksheet对象,而一个单元格对应着一个Cell对象。了解这些之后,对openpyxl是如何操作excel就比较清楚了。

    openpyxl安装

    pip install openpyxl

    openpyxl简单使用

    import openpyxl
    
    if __name__ == '__main__':
        path = 'F:/case/test_case.xlsx'
        # 读取excel文件
        workbook = openpyxl.load_workbook(path)
        # 读取所有sheet
        sheet = workbook.get_sheet_names()
        # 获取某个sheet
        sheet = workbook[sheet[0]]
        # 获取某个cell的值
        cell_val = sheet.cell(row=2, column=2).value
        print(cell_val)
    

    以上仅介绍openpyxl常用的语法,有兴趣了解更多内容可自行百度扩展

    Excel用例管理

    在项目下,新建一个文件夹:data,文件夹下新建一个cases.xlsx文件,用来存放测试用例。

    以下,是一个简单的登录测试用例设计模板:

    可以根据该表格生成实际结果,并将测试结果写入(Pass、Fail)表格。公众号后台回复:接口测试用例模板,可以获取完整接口测试用例Excle模板。

    既然有了用例模板,我们就开始从用openpyxl模块对excel读写数据。如下,在common文件夹下,新建excel_handle.py,用于封装操作excel的类。

    excel_handle.py

    import openpyxl
    class ExcelHandler:
        def __init__(self, file):
            self.file = file
        def open_excel(self, sheet_name):
            """打开Excel、获取sheet"""
            wb = openpyxl.load_workbook(self.file)
            # 获取sheet_name
            sheet = wb[sheet_name]
            return sheet
        def get_header(self, sheet_name):
            """获取header(表头)"""
            wb = self.open_excel(sheet_name)
            header = []
            # 遍历第一行
            for i in wb[1]:
                # 将遍历出来的表头字段加入列表
                header.append(i.value)
            return header
        def read_excel(self, sheet_name):
            """读取所有数据"""
            sheet = self.open_excel(sheet_name)
            rows = list(sheet.rows)
            data = []
            # 遍历从第二行开始的每一行数据
            for row in rows[1:]:
                row_data = []
                # 遍历每一行的每个单元格
                for cell in row:
                    row_data.append(cell.value)
                    # 通过zip函数将两个列表合并成字典
                    data_dict = dict(zip(self.get_header(sheet_name),row_data))
                data.append(data_dict)
            return data
        @staticmethod
        def write_excel(file, sheet_name, row, cloumn,data):
            """Excel写入数据"""
            wb = openpyxl.load_workbook(file)
            sheet = wb[sheet_name]
            sheet.cell(row, cloumn).value = data
            wb.save(file)
            wb.close()
    if __name__ == "__main__":
        # 以下为测试代码
        excel = ExcelHandler('../data/cases.xlsx')
        data = excel.read_excel('login')

    接下来结合ddt实现数据驱动,先简单来介绍下ddt。


    ddt介绍及使用

    ddt介绍

    • 名称:Data-Driven Tests,数据驱动测试

    • 作用:由外部数据集合来驱动测试用例的执行

    • 核心的思想:数据和测试代码分离

    • 应用场景:一组外部数据来执行相同的操作

    • 优点:当测试数据发生大量变化的情况下,测试代码可以保持不变

    • 实际项目:excel存储测试数据,ddt读取测试数据到单元测试框架(测试用例中)

    补充:

    所谓数据驱动,就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。说的直白些,就是参数化的应用。

    ddt安装

    pip install ddt

    ddt使用

    要想知道ddt到底怎么使用,我们从ddt模块源码中提取出三个重要的函数ddt、unpack、data。

    def ddt(cls):
        """
        Class decorator for subclasses of ``unittest.TestCase``.
        Apply this decorator to the test case class, and then
        decorate test methods with ``@data``.
        For each method decorated with ``@data``, this will effectively create as
        many methods as data items are passed as parameters to ``@data``.
        The names of the test methods follow the pattern
        ``original_test_name_{ordinal}_{data}``. ``ordinal`` is the position of the
        data argument, starting with 1.
        For data we use a string representation of the data value converted into a
        valid python identifier.  If ``data.__name__`` exists, we use that instead.
        For each method decorated with ``@file_data('test_data.json')``, the
        decorator will try to load the test_data.json file located relative
        to the python file containing the method that is decorated. It will,
        for each ``test_name`` key create as many methods in the list of values
        from the ``data`` key.
        """
        for name, func in list(cls.__dict__.items()):
            if hasattr(func, DATA_ATTR):
                for i, v in enumerate(getattr(func, DATA_ATTR)):
                    test_name = mk_test_name(name, getattr(v, "__name__", v), i)
                    test_data_docstring = _get_test_data_docstring(func, v)
                    if hasattr(func, UNPACK_ATTR):
                        if isinstance(v, tuple) or isinstance(v, list):
                            add_test(
                                cls,
                                test_name,
                                test_data_docstring,
                                func,
                                *v
                            )
                        else:
                            # unpack dictionary
                            add_test(
                                cls,
                                test_name,
                                test_data_docstring,
                                func,
                                **v
                            )
                    else:
                        add_test(cls, test_name, test_data_docstring, func, v)
                delattr(cls, name)
            elif hasattr(func, FILE_ATTR):
                file_attr = getattr(func, FILE_ATTR)
                process_file_data(cls, name, func, file_attr)
                delattr(cls, name)
        return cls
    def unpack(func):
        """
        Method decorator to add unpack feature.
        """
        setattr(func, UNPACK_ATTR, True)
        return func
    def data(*values):
        """
        Method decorator to add to your test methods.
        Should be added to methods of instances of ``unittest.TestCase``.
        """
        global index_len
        index_len = len(str(len(values)))
        return idata(values)
    

    ddt:

    装饰类,也就是继承自TestCase的类。

    data:

    装饰测试方法。参数是一系列的值。

    unpack:

    传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上,字典也可以这样处理;当没有加unpack时,方法的参数只能填一个。

    知道了具体应用后,简单来个小例子加深理解。

    test_ddt.py

    import unittest
    import ddt
    # 装饰类
    @ddt.ddt
    class DdtDemo(unittest.TestCase):
        def setUp(self):
            pass
        def tearDown(self):
            pass
        
        # 装饰方法
        @ddt.data(("15312344578", "12345678"), ("15387654321", "12345678"))
        @ddt.unpack
        def test_ddt(self, username,password):
            print(username,password)
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    运行结果为:

    Ran 2 tests in 0.001s
    OK
    15312344578 12345678
    15387654321 12345678
    

    上面的例子是为了加深理解,接下来介绍excel结合ddt实现数据驱动,优化之前的test_login.py模块。

    test_login.py

    import unittest
    from common.requests_handler import RequestsHandler
    from common.excel_handler import ExcelHandler
    import ddt
    import json
    @ddt.ddt
    class TestLogin(unittest.TestCase):
        # 读取excel中的数据
        excel = ExcelHandler('../data/cases.xlsx')
        case_data = excel.read_excel('login')
        print(case_data)
        def setUp(self):
            # 请求类实例化
            self.req = RequestsHandler()
        def tearDown(self):
            # 关闭session管理器
            self.req.close_session()
        @ddt.data(*case_data)
        def test_login_success(self,items):
            # 请求接口
            res = self.req.visit(method=items['method'],url=items['url'],json=json.loads(items['payload']),
                                 headers=json.loads(items['headers']))
            try:
                # 断言:预期结果与实际结果对比
                self.assertEqual(res['code'], items['expected_result'])
                result = 'Pass'
            except AssertionError as e:
                result = 'Fail'
                raise e
            finally:
                # 将响应的状态码,写到excel的第9列,即写入返回的状态码
                TestLogin.excel.write_excel("../data/cases.xlsx", 'login', items['case_id'] + 1, 9, res['code'])
                # 如果断言成功,则在第10行(测试结果)写入Pass,否则,写入Fail
                TestLogin.excel.write_excel("../data/cases.xlsx", 'login', items['case_id'] + 1, 10, result)
    if __name__ == '__main__':
        unittest.main()
    

    整体流程如下图:

    总结:本文主要介绍openpyxl操作excel,结合ddt实现数据驱动。

    下一篇:Python接口自动化之配置文件

    以上


    That‘s all

    更多系列文章

    敬请期待

    ITester软件测试小栈

    往期内容宠幸

    1.Python接口自动化-接口基础(一)


    2.Python接口自动化-接口基础(二)


    3.Python接口自动化-requests模块之get请求


    4.Python接口自动化-requests模块之post请求


    5.Python接口自动化之cookie、session应用


    6.Python接口自动化之Token详解及应用


    7.Python接口自动化之requests请求封装


    8.Fiddler设置断点(一)


    9.Fiddler抓包详解


    10.Fiddler跨域调试及Django跨域处理

    快来星标 置顶 关注

    <<  滑动查看下一张图片  >>

    今日问题

    在接口自动化测试项目中,你是如何管理测试用例?以及如何做数据校验?

    (欢迎在下方留言区发表你的看法)


     后台 回复"资源"取干货

    回复"21天打卡"一起打怪升级

    测试交流Q群:727998947

    点亮一下在看,你更好看

  • 相关阅读:
    将表单赋予对对象
    sql server 锁
    设置SQL server服务器的dbo架构
    用ILSpy查看Session.SessionID的生成算法
    c#3.0新特性
    解决文件上传插件Uploadify在火狐浏览器下,Session丢失的问题
    VS2012 集成 IL DASM IL微软中间语言查看器
    认识
    操作符重载
    博客搬家了
  • 原文地址:https://www.cnblogs.com/ITester520/p/13203306.html
Copyright © 2020-2023  润新知