python实现自动化测试框架如何进行数据参数化?这个包可以了解下

1.数据参数化介绍

只要你是负责编写自动化测试脚本的,数据参数化这个思想你就肯定会用 ,数据参数化的工具你肯定的懂一些 ,因为它能大大的提高我们自动化脚本编写效率 。

1.1什么是数据参数化

所谓的数据参数化 ,是指所执行的测试用例步骤相同、而数据不同 ,每次运行用例只变化的是数据 ,于是将这些数据专门放在一起进行批量循环运行 ,从而完成测试用例执行的目的 。

以登录功能为例 ,若一个登录功能每次操作的步骤是 :

  1. 输入用户名
  2. 输入密码
  3. 点击登录按钮 。

但是,因为每次输入的数据不同,导致生成的测试用例就不同了 ,同样还是这个登录功能,加上数据就变为以下的用例了 。

  • case1 : 输入正确的用户名 ,输入正确的密码 ,点击登录
  • case2 : 输入正确的用户,输入错误的密码,点击登录
  • case3 :输入正确的用户名,输入空的密码,点击登录
  • casen : ...

可以看到 ,在这些用例中,每条用例最大的不同是什么呢 ?其实就是数据不同 。但是由于数据不同,从而生成了多条测试用例 ,在功能测试中,这些用例是需要分别写、分别执行 。

1.2.为什么要进行数据参数化 ?

在功能测试中,即使是相同的步骤 ,只是数据不同 ,我们亦然也要尽量分开编写每一条用例 ,比如像上面的编写方式 ,因为这些编写它的易读性更好 ,功能测试设计测试用例和执行用例往往不是一个人 ,所以用例编写的易读性是就是一个很重要的因素 。

但是如果将上面的用例进行自动化实现 ,虽然按照一条用例对应一个方法是一种很清晰的思路 ,但是它的最大问题就是代码冗余 ,当一个功能中步骤相同,只是数据不同时,你的数据越多,代码冗余度就越高 。你会发现每个测试方法中的代码就会是相同的 。

像代码冗余这种问题,在编写自动化时是必须要考虑的一个问题,因为随着代码量越多 ,冗余度越高、越难维护 。

以下就是是通过正常方式实现登录的自动化脚本 :

  1. import unittest

  2. from package_unittest.login import login

  3. class TestLogin(unittest.TestCase):

  4. # case1 : 输入正确的用户名和正确的密码进行登录

  5. def test_login_success(self):

  6. expect_reslut = 0

  7. actual_result = login('admin','123456').get('code')

  8. self.assertEqual(expect_reslut,actual_result)

  9. # case2 : 输入正确的用户名和错误的密码进行登录

  10. def test_password_is_wrong(self):

  11. expect_reslut = 3

  12. actual_result = login('admin', '1234567').get('code')

  13. self.assertEqual(expect_reslut, actual_result)

  14. # case3 : 输入正确的用户名和空的密码进行登录

  15. def test_password_is_null(self):

  16. expect_reslut = 2

  17. actual_result = login('admin', '').get('code')

  18. self.assertEqual(expect_reslut, actual_result)

可以看到,三条用例对应三个测试方法,虽然清晰 ,代码每个方法中的代码几乎是相同的。

那如果用参数化实现的代码是什么呢 ? 可以看下面的这段代码 :

  1. class TestLogin(unittest.TestCase):

  2. @parameterized.expand(cases)

  3. def test_login(self,expect_result,username,password):

  4. actual_result = login(username,password).get('code')

  5. self.assertEqual(expect_result,actual_result)

以上代码只有一条用例 ,不管这个功能有几条都能执行 。

通过上面两种形式的比较可以看出 :为什么要进行数据参数化呢 ?其实就是降低代码冗余、提高代码复用度 ,将主要编写测试用例的时间转化为编写测试数据上来 。

1.3.如何进行数据参数化

在代码中实现数据参数化都需要借助于外部工具 ,比如专门用于unittest的ddt , 既支持unittest、也支持pytest的parameterized ,专门在pytest中使用的fixture.params .

参数化工具

支持测试框架

备注

ddt

unittest

第三方包,需要下载安装

parameterized

nose,unittest,pytest

第三方包,需要下载安装

@pytest.mark.parametrize

pytest

本身属于pytest中的功能

@pytest.fixture(params=[])

pytest

本身属于pytest中的功能

以上实现数据参数化的工具有两个共同点:

  • 都能实现数据参数化
  • 都时装饰器来作用于测试用例脚本 。

2.模块介绍

1.下载安装 :

  1. # 下载

  2. pip install parameterized

  3. # 验证 :

  4. pip show parameterized

2.导包

  1. # 直接导入parameterized类

  2. from parameterized import parameterized

3.官网示例

@parameterized 和 @parameterized.expand 装饰器接受列表 或元组或参数(...)的可迭代对象,或返回列表或 可迭代:

  1. from parameterized import parameterized, param

  2. # A list of tuples

  3. @parameterized([

  4. (2, 3, 5),

  5. (3, 5, 8),

  6. ])

  7. def test_add(a, b, expected):

  8. assert_equal(a + b, expected)

  9. # A list of params

  10. @parameterized([

  11. param("10", 10),

  12. param("10", 16, base=16),

  13. ])

  14. def test_int(str_val, expected, base=10):

  15. assert_equal(int(str_val, base=base), expected)

  16. # An iterable of params

  17. @parameterized(

  18. param.explicit(*json.loads(line))

  19. for line in open("testcases.jsons")

  20. )

  21. def test_from_json_file(...):

  22. ...

  23. # A callable which returns a list of tuples

  24. def load_test_cases():

  25. return [

  26. ("test1", ),

  27. ("test2", ),

  28. ]

  29. @parameterized(load_test_cases)

  30. def test_from_function(name):

  31. ...

请注意,使用迭代器或生成器时,将加载所有项 在测试运行开始之前放入内存(我们显式执行此操作以确保 生成器在多进程或多线程中只耗尽一次 测试环境)。

@parameterized装饰器可以使用测试类方法,并且可以独立使用 功能:

  1. from parameterized import parameterized

  2. class AddTest(object):

  3. @parameterized([

  4. (2, 3, 5),

  5. ])

  6. def test_add(self, a, b, expected):

  7. assert_equal(a + b, expected)

  8. @parameterized([

  9. (2, 3, 5),

  10. ])

  11. def test_add(a, b, expected):

  12. assert_equal(a + b, expected)

@parameterized.expand可用于生成测试方法 无法使用测试生成器的情况(例如,当测试 类是单元测试的一个子类。测试用例):

  1. import unittest

  2. from parameterized import parameterized

  3. class AddTestCase(unittest.TestCase):

  4. @parameterized.expand([

  5. ("2 and 3", 2, 3, 5),

  6. ("3 and 5", 3, 5, 8),

  7. ])

  8. def test_add(self, _, a, b, expected):

  9. assert_equal(a + b, expected)

将创建测试用例:

  1. $ nosetests example.py

  2. test_add_0_2_and_3 (example.AddTestCase) ... ok

  3. test_add_1_3_and_5 (example.AddTestCase) ... ok

  4. ----------------------------------------------------------------------

  5. Ran 2 tests in 0.001s

  6. OK

请注意,@parameterized.expand 的工作原理是在测试上创建新方法 .class。如果第一个参数是字符串,则该字符串将添加到末尾 的方法名称。例如,上面的测试用例将生成方法test_add_0_2_and_3和test_add_1_3_and_5。

@parameterized.expand 生成的测试用例的名称可以是 使用 name_func 关键字参数进行自定义。该值应 是一个接受三个参数的函数:testcase_func、param_num、 和参数,它应该返回测试用例的名称。testcase_func是要测试的功能,param_num将是 参数列表中测试用例参数的索引,参数(参数的实例)将是将使用的参数。

  1. import unittest

  2. from parameterized import parameterized

  3. def custom_name_func(testcase_func, param_num, param):

  4. return "%s_%s" %(

  5. testcase_func.__name__,

  6. parameterized.to_safe_name("_".join(str(x) for x in param.args)),

  7. )

  8. class AddTestCase(unittest.TestCase):

  9. @parameterized.expand([

  10. (2, 3, 5),

  11. (2, 3, 5),

  12. ], name_func=custom_name_func)

  13. def test_add(self, a, b, expected):

  14. assert_equal(a + b, expected)

将创建测试用例:

  1. $ nosetests example.py

  2. test_add_1_2_3 (example.AddTestCase) ... ok

  3. test_add_2_3_5 (example.AddTestCase) ... ok

  4. ----------------------------------------------------------------------

  5. Ran 2 tests in 0.001s

  6. OK

param(...) 帮助程序类存储一个特定测试的参数 箱。它可用于将关键字参数传递给测试用例:

  1. from parameterized import parameterized, param

  2. @parameterized([

  3. param("10", 10),

  4. param("10", 16, base=16),

  5. ])

  6. def test_int(str_val, expected, base=10):

  7. assert_equal(int(str_val, base=base), expected)

如果测试用例具有文档字符串,则该测试用例的参数将为 附加到文档字符串的第一行。可以控制此行为 doc_func参数:

  1. from parameterized import parameterized

  2. @parameterized([

  3. (1, 2, 3),

  4. (4, 5, 9),

  5. ])

  6. def test_add(a, b, expected):

  7. """ Test addition. """

  8. assert_equal(a + b, expected)

  9. def my_doc_func(func, num, param):

  10. return "%s: %s with %s" %(num, func.__name__, param)

  11. @parameterized([

  12. (5, 4, 1),

  13. (9, 6, 3),

  14. ], doc_func=my_doc_func)

  15. def test_subtraction(a, b, expected):

  16. assert_equal(a - b, expected)

  17. $ nosetests example.py

  18. Test addition. [with a=1, b=2, expected=3] ... ok

  19. Test addition. [with a=4, b=5, expected=9] ... ok

  20. 0: test_subtraction with param(*(5, 4, 1)) ... ok

  21. 1: test_subtraction with param(*(9, 6, 3)) ... ok

  22. ----------------------------------------------------------------------

  23. Ran 4 tests in 0.001s

  24. OK

最后@parameterized_class参数化整个类,使用 属性列表或将应用于 .class

  1. from yourapp.models import User

  2. from parameterized import parameterized_class

  3. @parameterized_class([

  4. { "username": "user_1", "access_level": 1 },

  5. { "username": "user_2", "access_level": 2, "expected_status_code": 404 },

  6. ])

  7. class TestUserAccessLevel(TestCase):

  8. expected_status_code = 200

  9. def setUp(self):

  10. self.client.force_login(User.objects.get(username=self.username)[0])

  11. def test_url_a(self):

  12. response = self.client.get('/url')

  13. self.assertEqual(response.status_code, self.expected_status_code)

  14. def tearDown(self):

  15. self.client.logout()

  16. @parameterized_class(("username", "access_level", "expected_status_code"), [

  17. ("user_1", 1, 200),

  18. ("user_2", 2, 404)

  19. ])

  20. class TestUserAccessLevel(TestCase):

  21. def setUp(self):

  22. self.client.force_login(User.objects.get(username=self.username)[0])

  23. def test_url_a(self):

  24. response = self.client.get("/url")

  25. self.assertEqual(response.status_code, self.expected_status_code)

  26. def tearDown(self):

  27. self.client.logout()

@parameterized_class装饰器接受class_name_func论点, 它控制由 @parameterized_class 生成的参数化类的名称:

  1. from parameterized import parameterized, parameterized_class

  2. def get_class_name(cls, num, params_dict):

  3. # By default the generated class named includes either the "name"

  4. # parameter (if present), or the first string value. This example shows

  5. # multiple parameters being included in the generated class name:

  6. return "%s_%s_%s%s" %(

  7. cls.__name__,

  8. num,

  9. parameterized.to_safe_name(params_dict['a']),

  10. parameterized.to_safe_name(params_dict['b']),

  11. )

  12. @parameterized_class([

  13. { "a": "hello", "b": " world!", "expected": "hello world!" },

  14. { "a": "say ", "b": " cheese :)", "expected": "say cheese :)" },

  15. ], class_name_func=get_class_name)

  16. class TestConcatenation(TestCase):

  17. def test_concat(self):

  18. self.assertEqual(self.a + self.b, self.expected)

  19. $ nosetests -v test_math.py

  20. test_concat (test_concat.TestConcatenation_0_hello_world_) ... ok

  21. test_concat (test_concat.TestConcatenation_0_say_cheese__) ... ok

使用单个参数

如果测试函数只接受一个参数并且该值不可迭代, 然后可以提供值列表,而无需将每个值包装在 元:

 
  1. @parameterized([1, 2, 3])

  2. def test_greater_than_zero(value):

  3. assert value > 0

但请注意,如果单个参数可迭代的(例如列表或 元组),那么它必须包装在元组、列表或 param(...) 装饰器中:

  1. @parameterized([

  2. ([1, 2, 3], ),

  3. ([3, 3], ),

  4. ([6], ),

  5. ])

  6. def test_sums_to_6(numbers):

  7. assert sum(numbers) == 6

虽然看似以上功能支持的挺多 ,但其实真正用的不多 ,因为它跟框架有很大关系的 。具体说明下 :

总结:

  • 它支持nose是最好的 . 如果你的自动化中使用nose,那么以上功能基本都能用到 。
  • 如果你用的测试框架是unittest ,你只能用到它的expand()这个函数 ,不过有这个函数也就够了 。
  • 如果你用的测试框架是pytest , 它支持了Pytest3的版本,再高版本的就不支持了,同时pytest也有自己的参数化工具,一般也不用它了。

3.项目实践

通过数据参数胡重新编写登录测试用例 ,将以前yaml中的登录用例数据转化为paramterized的数据格式 ,它的数据格式要求为:[(),(),()] . 所以,编写测试用例的数据就变为了以下的代码 。

  1. # 将登录数据转化为paramterize所识别的格式。

  2. def get_data():

  3. yaml_path = get_file_path('login.yaml') # 获取login.yaml的全路径

  4. result = read_yaml(yaml_path) # 转化为python对象

  5. login_data = result.get('login') # 获取字典中login的值

  6. logger.debug("登录结果:{}".format(login_data))

  7. return (login_data) # 获取字典中login的值

  8. @allure.epic("vshop")

  9. @allure.story("登录")

  10. class TestLogin(unittest.TestCase):

  11. # case1 : 测试登录功能

  12. @parameterized.expand(get_data())

  13. def test_login(self,case_name,username,password,code,message):

  14. logger.info("从参数化获取的数据:{}|{}|{}|{}|{}".format(case_name,username,password,code,message))

  15. with allure.step("执行用例:{},输入用户名:{},输入密码:{}".format(case_name,username,password)):

  16. login_result = login(username,password)

  17. self.assertEqual(code, login_result.get('errno'))

  18. self.assertEqual(message, login_result.get('errmsg'))

这样的话,我们只编写了一条测试用例 ,但是在测试数据中有几条数据 ,都可以正常运行 。

 

行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 455787643,里面有各种软件测试+开发资料和技术可以一起交流学习哦。

总结:感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity 自定义房间布局系统 设计与实现一个灵活的房间放置系统 ——物体占用的区域及放置点自动化

放置物体功能 效果: 功能: 自定义物体占用区域的大小一键调整占用区域调整旋转度数,分四个挡位: NoRotation:该物体不能调整旋转。MaximumAngle:每次转动90。NormalAngle:每次转动45&#xff…

Solr 日志系统7.4.0部署和迁移到本地,Core Admin 添加新的core报错

文章目录 Solr部署Docker部署二进制部署 Tips:Solr设置账号密码方法1:(不使用)方法2: Core Admin 添加新的core报错Solr数据迁移 Solr部署 Docker部署 docker run -d -p 8983:8983 --name solr solr:latest docker run -d -p 8983:8983 -v /opt/solr:/…

操作系统入门系列-MIT6.828(操作系统工程)学习笔记(五)---- 操作系统的组织结构(OS design)

系列文章目录 操作系统入门系列-MIT6.S081(操作系统)学习笔记(一)---- 操作系统介绍与接口示例 操作系统入门系列-MIT6.828(操作系统工程)学习笔记(二)----课程实验环境搭建&#x…

网工内推 | 深信服、中软国际技术支持工程师,最高13k*13薪

01 深信服 🔷招聘岗位:远程技术支持工程师 🔷任职要求: 一、专业能力和行业经验: ①具备友商同岗位工作经验1.5年以上,具备良好的分析和判断能力,有独立问题处理思路,具备常见协…

python中魔术方法__str__与__repr__的区别

在Python中,__str__和__repr__是两个常见的魔法方法(也称为双下方法或dunder方法),它们用于定义对象的字符串表示形式。它们的主要区别在于它们的用途和使用场景。 __str__ 用途:__str__方法用于为用户提供一个易读的…

【嵌入式DIY实例】-Nokia 5110显示DHT11/DHT22传感器数据

Nokia 5110显示DHT11/DHT22传感器数据 文章目录 Nokia 5110显示DHT11/DHT22传感器数据1、硬件准备2、代码实现2.1 显示DHT11数据2.2 显示DHT22数据本文介绍如何将 ESP8266 NodeMCU 开发板 (ESP-12E) 与 DHT11 数字湿度和温度传感器以及诺基亚 5110 LCD 连接。 NodeMCU 从 DHT11…

.NET Core 服务注册步骤总结

总结一下 .NET Core 服务注册的步骤: .NET Core Web Api 项目服务注册步骤: 创建一个接口,和实现类 比如:IMyService, CnService 在 Program.cs 的 var app builder.Build(); 语句之前加上: var builder WebApplic…

【面经总结】 Java基础 - 异常

异常 介绍一下 Java 的异常体系 Java 的异常体系是由 Throwable 类及其子类构成的。 Throwable 包含两个子类:Error(错误)和 Exception(异常) Error 表示错误,通常不需要程序员处理,如内存溢…

python中的turtle

turtle个别指令 初始箭头默认指向为东(右) 往前(右)三个格:turtle.forward(3) 往后(左)三个格:turtle.backward(3) 往左转90度:turtle.left(90) 往右转90度&#xf…

Attention与轻量级ResNet融合,低资源消耗下实现效率和性能完美平衡

注意力机制通过让模型关注图像关键区域提升了识别精度,而轻量级残差网络通过减少参数和计算量,实现了在低资源消耗下的优秀性能。 结合注意力机制与轻量级残差网络,既能让模型能够更高效地关注输入数据中的关键信息,提升模型处理…

vs调试时无法找到文件-chromium源码编译

一直跟着教程走结果报错了,找了半天的教程无法解决,于是乎只好重来,因为这个是属于项目调试,报错了可以重新编译项目就好。在重新做的过程中发现路径写错了

人工智能的等价形式

经典的人工智能,采用“梯度下降法”,运算量很大,约是esp2。其中e是epoch,训练的周期数;s是sample,训练样本的数量;p是parameter,参数的数量。 人工智能有等价形式,它不需…

DPI简析

DPI简析 一、DPI与PPI二、硬件设备的DPI2.1打印机DPI2.2显示器DPI2.2.1显示器DPI计算2.2.2显示器分辨率与系统分辨率2.2.3常见分辨率 2.3鼠标DPI 三、图片DPI3.1图片DPI与打印尺寸3.1.1图片打印尺寸计算3.1.2常用的照片尺寸及DPI 3.2图片DPI与屏幕显示3.3修改图片DPI 参考文档 …

Windos10上Podman安装运行mysql8

记录以下在windows10系统上Podman v5.1.1安装MySQL8全过程。 目录 一、拉取mysql8镜像二、创建宿主目录三、创建 my.cnf文件四、创建Mysql8容器五、windows上Podman安装运行mysql8失败问题描述 解决办法① 通过PowerShell进入wsl② 修改wsl系统配置③ 重启wsl,Podma…

3个月搞定计算机二级C语言!高效刷题系列进行中

文章目录 前言备考计算机二级C语言为什么考二级C语言?刷题总结后发布系列文章后记免责声明 前言 大家好,我是梁国庆。 计算机二级应该是每一位大学生的必修课,相信很多同学的大学flag中都会有它的身影。 我在大学里也不止一次的想要考计算…

【运维知识大神篇】运维界的超神器Kubernetes教程14(RBAC三种认证方式详解:基于用户+基于用户组+基于服务账号)

本篇文章继续给大家介绍Kubernetes,内容依旧烧脑,不过内容也已经过了一大半了,如果你把我Kubernetes前面的教程都看懂了的话,那么你已经很厉害了,坚持下去!本篇文章主要介绍RBAC的三种认证方式,…

Python使用tkinter库设置背景图片、label显示位置和label设置显示图片

tkinter 设置背景图片 label显示位置 label设置显示图片 from tkinter import * import tkinter as tk from PIL import ImageTk from PIL import Imagedef get_img(filename, width, height):im Image.open(filename).resize((width, height))im ImageTk.PhotoImage(im)…

Java的集合框架总结

Map接口和Collection接口是所有集合框架的父接口: Collection接口的子接口包括:Set接口和List接口 Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等 Set接口的实现类主要有:HashSet、Tr…

【CH32V305FBP6】USBD HS 描述符修改

文章目录 前言设备描述符完整描述符配置描述符CDC 描述符接口关联描述符接口描述符功能描述符端点描述符接口描述符端点描述符 HID 描述符接口描述符 新增一个 HID 设备 前言 USB HS 复合设备,CDCHID 功能:串口、DAP、CAN-HID、RS485 设备描述符 htt…

远程抄表系统方案

一、远程抄表系统简述 远程抄表系统,是一种智能化的自动化数据收集技术性,主要运用于电力工程、供水公司、天然气等公共服务行业,完成对消费者表计数据的远程、即时、智能采集。这类系统不但降低了成本,降低了人力抄表错误&#…