Python接口自动化浅析数据驱动原理

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

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

我们看到每个方法(测试用例)的代码几乎是一模一样的,试想一下,在我们的测试场景中,

一个登录接口有可能会有十几条到几十条测试用例,如果每组数据都编写一个方法,

这样将会有更多的重复项代码,不仅执行效率不高,也不好维护。

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

  • 把测试数据用excel表格管理起来,代码做封装;
  • 用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简单使用

1

2

3

4

5

6

7

8

9

10

11

12

13

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

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。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

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()

整体流程如下图:

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/265432.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于深氮化镓蚀刻的微米尺寸光子器件的研制

引言 GaN和相关合金由于其优异的特性以及大的带隙、高的击穿电场和高的电子饱和速度而成为有吸引力的材料之一,与优化工艺过程相关的成熟材料是有源/无源射频光电子器件近期发展的关键问题。专用于三元结构的干法蚀刻工艺特别重要,因为这种器件通常包括…

2023年【G1工业锅炉司炉】考试及G1工业锅炉司炉作业考试题库

题库来源:安全生产模拟考试一点通公众号小程序 G1工业锅炉司炉考试参考答案及G1工业锅炉司炉考试试题解析是安全生产模拟考试一点通题库老师及G1工业锅炉司炉操作证已考过的学员汇总,相对有效帮助G1工业锅炉司炉作业考试题库学员顺利通过考试。 1、【多…

源码泄露 [RoarCTF 2019]Easy Java1

打开题目 看见登录框一开始以为是sql注入,试了一下结果不是 我们点击help看看 于此同时url 我们在url上随便输入使其报错 在这里看到了Apache Tomcat/8.5.24 再看一下网站用的是什么服务器 百度搜索得知, openresty是一个基于Nginx的web平台 那我们bp抓…

六级高频词汇3

目录 单词 参考链接 单词 400. nonsense n. 胡说,冒失的行动 401. nuclear a. 核子的,核能的 402. nucleus n. 核 403. retail n. /v. /ad. 零售 404. retain vt. 保留,保持 405. restrict vt. 限制,约束 406. sponsor n. …

request.getSession().getAttribute 获取不到值,获取到的是null

我 | 在这里 🕵️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 🏠 工作 | 广州 ⭐ Java 全栈开发(软件工程师) ✈️公众号 | 热爱技术的小郑 。文章底部有个人公众号二维码。回复 Java全套视频教程 或 前端全套视频教程 即可获取 300G 教程资…

Python:核心知识点整理大全13-笔记

目录 6.4.3 在字典中存储字典 6.5 小结 第7章 用户输入和while循环 7.1 函数 input()的工作原理 7.1.1 编写清晰的程序 7.1.2 使用 int()来获取数值输入 7.1.3 求模运算符 7.1.4 在 Python 2.7 中获取输入 7.2 while 循环简介 7.2.1 使用 while 循环 往期快速传送门…

四川大学携手和鲸打造课内workshop,趣味实践教学助力数据思维培养

“通过描述性统计我们可以看到,学生们对第二食堂的倾慕之心真是苍天可鉴!” “综合上述分析,贫困人口有较大概率集中在性别为女,专业为理工科,日常消费次数、消费金额以及卡内盈余都较低的学生之中。” “有些学生长…

Leetcode—198.打家劫舍【中等】

2023每日刷题(五十二) Leetcode—198.打家劫舍 算法思想 具体思路 首先,我们从上面的题目描述中抽象出题意。 ● 从一个非负整数数组中找到一个子序列,并且该子序列的和最大 ● 子序列中每个数的位置不能够相邻。举例来讲&…

基于conda环境使用mamba/conda安装配置QIIME 2 2023.9 Amplicon扩增子分析环境,q2cli主要功能模块介绍及使用

QIIME 2 2023.9 Amplicon Distribution介绍: 概述 qiime团队专门针对高通量扩增子序列分析退出的conda集成环境,包括了主要和常见的扩增子分析模块,用户可以单独使用各个模块,也可以使用各模块组成不同的分析流程。从2023.09版本…

JAVA安全之Spring参数绑定漏洞CVE-2022-22965

前言 在介绍这个漏洞前,介绍下在spring下的参数绑定 在Spring框架中,参数绑定是一种常见的操作,用于将HTTP请求的参数值绑定到Controller方法的参数上。下面是一些示例,展示了如何在Spring中进行参数绑定: 示例1&am…

代码片段管理器 SnippetsLab激活中文 for Mac

SnippetsLab Mac是Mac平台非常不错的代码片段管理器软件,无需使用鼠标即可快速搜索,预览,打开或复制您的代码段,非常简单方便。SnippetsLab使用嵌套文件夹,标签和智能组支持在一个地方管理所有有价值的代码片段变得简单…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion简介

Stable Diffusion是2022年发布的深度学习文本到图像生成模型,它主要用于根据文本的描述产生详细图像,尽管它也可以应用于其他任务,如