前言

unittest单元测试框架使用DDT进行数据驱动测试,那么身为功能更加强大且更加灵活的Pytest框架怎么可能没有数据驱动的概念呢?其实Pytest是使用@pytest.mark.parametrize装饰器来实现数据驱动测试的,那么今天我们就简单来说说在它是如何进行数据驱动测试的

装饰测试类

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = [
(1, 2, 3),
(4, 5, 9)
] def add(a, b):
return a + b @pytest.mark.parametrize('a, b, expect', data_1)
class TestParametrize(object): def test_parametrize_1(self, a, b, expect):
print('\n测试函数1测试数据为\n{}-{}'.format(a, b))
assert add(a, b) == expect def test_parametrize_2(self, a, b, expect):
print('\n测试函数2数据为\n{}-{}'.format(a, b))
assert add(a, b) == expect if __name__ == '__main__':
pytest.main(['-sv'])

输出

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[1-2-3]
测试函数1测试数据为
1-2
PASSED
test_parametrize.py::TestParametrize::test_parametrize_1[4-5-9]
测试函数1测试数据为
4-5
PASSED
test_parametrize.py::TestParametrize::test_parametrize_2[1-2-3]
测试函数2数据为
1-2
PASSED
test_parametrize.py::TestParametrize::test_parametrize_2[4-5-9]
测试函数2数据为
4-5
PASSED ========================== 4 passed in 0.21 seconds =========================== Process finished with exit code 0

说明

当装饰器装饰测试类时,给数据集合会被传递给给类的所有方法

装饰测试函数

单个数据

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data = [1, 2] @pytest.mark.parametrize('a', data)
def test_parametrize(a):
print('\n被加载测试数据为\n{}'.format(a)) if __name__ == '__main__':
pytest.main(['-s'])

输出

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 2 items test_parametrize.py
被加载测试数据为
1
.
被加载测试数据为
2
. ========================== 2 passed in 0.16 seconds =========================== Process finished with exit code 0

说明

当测试用例只需要一个参数时,我们存放数据的列表无序嵌套序列,@pytest.mark.parametrize('a', data)装饰器的第一个参数也只需要一个变量接收列表中的每个元素,第二个参数传递存储数据的列表,那么测试用例需要使用同名的字符串接收测试数据(实例中的a)且列表有多少个元素就会生成并执行多少个测试用例

一组数据

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data = [
[1, 2, 3],
[4, 5, 9]
] # 列表嵌套列表
# data_tuple = [
# (1, 2, 3),
# (4, 5, 9)
# ] # 列表嵌套元组 @pytest.mark.parametrize('a, b, expect', data)
def test_parametrize_1(a, b, expect): # 一个参数接收一个数据
print('\n测试数据为\n{},{},{}'.format(a, b, expect))
actual = a + b
assert actual == expect @pytest.mark.parametrize('value', data)
def test_parametrize_2(value): # 一个参数接收一组数据
print('\n测试数据为\n{}'.format(value))
actual = value[0] + value[1]
assert actual == value[2] if __name__ == '__main__':
pytest.main(['-s'])

输出

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 4 items test_parametrize.py
测试数据为
1,2,3
.
测试数据为
4,5,9
.
测试数据为
[1, 2, 3]
.
测试数据为
[4, 5, 9]
. ========================== 4 passed in 0.17 seconds =========================== Process finished with exit code 0

说明

当测试用例需要多个数据时,我们可以使用嵌套序列(嵌套元组&嵌套列表)的列表来存放测试数据

装饰器@pytest.mark.parametrize()可以使用单个变量接收数据,也可以使用多个变量接收,同样,测试用例函数也需要与其保持一致

当使用单个变量接收时,测试数据传递到测试函数内部时为列表中的每一个元素或者小列表,需要使用索引的方式取得每个数据

当使用多个变量接收数据时,那么每个变量分别接收小列表或元组中的每个元素

列表嵌套多少个多组小列表或元组,测生成多少条测试用例

图解对应关系

组合数据

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = [1, 2]
data_2 = [3, 4] @pytest.mark.parametrize('a', data_1)
@pytest.mark.parametrize('b', data_2)
def test_parametrize_1(a, b):
print('\n测试数据为\n{},{}'.format(a, b)) if __name__ == '__main__':
pytest.main(['-s'])

输出

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 4 items test_parametrize.py
测试数据为
1,3
.
测试数据为
2,3
.
测试数据为
1,4
.
测试数据为
2,4
. ========================== 4 passed in 0.24 seconds =========================== Process finished with exit code 0

说明

通过测试结果,我们不难分析,一个测试函数还可以同时被多个参数化装饰器装饰,那么多个装饰器中的数据会进行交叉组合的方式传递给测试函数,进而生成n*n个测试用例,这也为我们的测试设计时提供了方便

标记用例

可以直接标记测试用例,参数化装饰器也可以识别(标记用例失败或跳过)

标记为无条件跳过(标记为失败为xfail,自己尝试)

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = [
[1, 2, 3],
pytest.param(3, 4, 8, marks=pytest.mark.skip)
] def add(a, b):
return a + b @pytest.mark.parametrize('a, b, expect', data_1)
def test_parametrize_1(a, b, expect):
print('\n测试数据为\n{},{}'.format(a, b))
assert add(a, b) == expect if __name__ == '__main__':
pytest.main(['-vs'])

输出

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0 -- C:\Programs\Python\Python37-32\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.7.2', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '4.3.1', 'py': '1.8.0', 'pluggy': '0.9.0'}, 'Plugins': {'rerunfailures': '7.0', 'metadata': '1.8.0', 'html': '1.20.0'}, 'JAVA_HOME': 'D:\\JDK'}
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collecting ... collected 2 items test_parametrize.py::test_parametrize_1[1-2-3]
测试数据为
1,2
PASSED
test_parametrize.py::test_parametrize_1[3-4-8] SKIPPED ===================== 1 passed, 1 skipped in 0.17 seconds ===================== Process finished with exit code 0

说明

输出结果显示收集到2个用例,一个通过,一个被跳过,当我们不想执行某组测试数据时,我们可以标记skip或skipif;当我们预期某组数据会执行失败时,我们可以标记为xfail等

嵌套字典

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = (
{
'user': 1,
'pwd': 2
},
{
'user': 3,
'pwd': 4
}
) @pytest.mark.parametrize('dic', data_1)
def test_parametrize_1(dic):
print('\n测试数据为\n{}'.format(dic)) if __name__ == '__main__':
pytest.main(['-s'])

输出

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 2 items test_parametrize.py
测试数据为
{'user': 1, 'pwd': 2}
.
测试数据为
{'user': 3, 'pwd': 4}
. ========================== 2 passed in 0.20 seconds =========================== Process finished with exit code 0

增加可读性

使用ids参数

参数化装饰器有一个额外的参数ids,可以标识每一个测试用例,自定义测试数据结果的显示,为了增加可读性,我们可以标记每一个测试用例使用的测试数据是什么,适当的增加一些说明

在使用前你需要知道,ids参数应该是一个字符串列表,必须和数据对象列表的长度保持一致,我们可以试着使用ids,看下效果

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = [
(1, 2, 3),
(4, 5, 9)
] ids = ["a:{} + b:{} = expect:{}".format(a, b, expect) for a, b, expect in data_1] def add(a, b):
return a + b @pytest.mark.parametrize('a, b, expect', data_1, ids=ids)
class TestParametrize(object): def test_parametrize_1(self, a, b, expect):
print('\n测试函数1测试数据为\n{}-{}'.format(a, b))
assert add(a, b) == expect def test_parametrize_2(self, a, b, expect):
print('\n测试函数2数据为\n{}-{}'.format(a, b))
assert add(a, b) == expect if __name__ == '__main__':
pytest.main(['-v']) # -v : 更加详细的输出测试结果

输出

装饰器不传递ids参数的输出

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[1-2-3] PASSED   [ 25%]
test_parametrize.py::TestParametrize::test_parametrize_1[4-5-9] PASSED [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_2[1-2-3] PASSED [ 75%]
test_parametrize.py::TestParametrize::test_parametrize_2[4-5-9] PASSED [100%] ========================== 4 passed in 0.16 seconds =========================== Process finished with exit code 0

装饰器传递ids参数的输出

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[a:1 + b:2 = expect:3] PASSED [ 25%]
test_parametrize.py::TestParametrize::test_parametrize_1[a:4 + b:5 = expect:9] PASSED [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_2[a:1 + b:2 = expect:3] PASSED [ 75%]
test_parametrize.py::TestParametrize::test_parametrize_2[a:4 + b:5 = expect:9] PASSED [100%] ========================== 4 passed in 0.20 seconds =========================== Process finished with exit code 0

说明

执行命令我使用了-v,会更加详细的显示输出结果,可以看到所有的数据结果中的用例都被一个列表明确的标记了,而且通过这种标记可以更加直观的看出来,每个测试用例使用的数据名称及测试内容

自定义id做标识

除了使用ids参数增加输出可读性外,我们还可以在参数列表的参数旁边定义一个id值来做标识,看下面实例

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import pytest data_1 = [
pytest.param(1, 2, 3, id="(a+b):pass"), # id的值可以自定义, 只要方便理解每个用例是干什么的即可
pytest.param(4, 5, 10, id="(a+b):fail")
] def add(a, b):
return a + b class TestParametrize(object): @pytest.mark.parametrize('a, b, expect', data_1)
def test_parametrize_1(self, a, b, expect):
assert add(a, b) == expect if __name__ == '__main__':
pytest.main(['-v'])

输出

test_parametrize.py::TestParametrize::test_parametrize_1[(a+b):pass] PASSED [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_1[(a+b):fail] FAILED [100%] ================================== FAILURES ===================================
_______________ TestParametrize.test_parametrize_1[(a+b):fail] ________________ self = <pytest_parametrize.test_parametrize.TestParametrize object at 0x000001D7BFC4C748>
a = 4, b = 5, expect = 10 @pytest.mark.parametrize('a, b, expect', data_1)
def test_parametrize_1(self, a, b, expect):
> assert add(a, b) == expect
E assert 9 == 10
E -9
E +10 test_parametrize.py:28: AssertionError
===================== 1 failed, 1 passed in 0.35 seconds ====================== Process finished with exit code 0

说明

如果使用此方法来标记测试用例,一定要严格按照我写的格式来使用,语法为pytest.param(value, id='somthing')

总结

Pytest中实现数据驱动就是如此了

掌握

1.装饰器与测试用例使用单个变量接收多组数据与多个变量接收多个数据的访问方法

2.不同测试数据形式(列表嵌套元组,列表,字典等)时,如何传递数据及访问数据

3.装饰器装饰测试类和测试函数的不同之处:装饰测试类时,类内所有的方法必须接送测试数据,否则会报错,装饰测试函数时比较灵活,如果函数不使用数据就可以不装饰

4.为了输出结果的可读性,可以选择使用ids参数,与测试数据中定义id参数值来标识测试用例

注意

1. 装饰器的第一个参数是一个列表形式的字符串参数"a, b, c" 不能写成"a", "b", "c"

2. ids是个字符串列表,它的长度需要与测试数据列表的长度一致

Pytest进阶之参数化的更多相关文章

  1. 11、pytest -- 测试的参数化

    目录 1. @pytest.mark.parametrize标记 1.1. empty_parameter_set_mark选项 1.2. 多个标记组合 1.3. 标记测试模块 2. pytest_g ...

  2. pytest -- 测试的参数化

    目录 1. @pytest.mark.parametrize标记 1.1. empty_parameter_set_mark选项 1.2. 多个标记组合 1.3. 标记测试模块 2. pytest_g ...

  3. Pytest进阶使用

    fixture 特点: 命令灵活:对于setup,teardown可以省略 数据共享:在conftest.py配置里写方法可以实现数据共享,不需要import导入,可以跨文件共享 scope的层次及神 ...

  4. pytest进阶之配置文件

    前言 pytest配置文件能够改变pytest框架代码的运行规则.比如修改pytest收集用例的规则,添加命令行参数等等!下面我们来一一讲解常用的一些配置项 Help 通过命令pytest --hel ...

  5. pytest进阶之html测试报告

    前言 Pytest系列已经写了几篇文章了,也不知道对多少人有帮助,总之对于我自己来说该掌握的都已经掌握了,那么今天我们再来说说pytest如何生成一个完整的html测试报告,让你在吹牛逼的路上再多一份 ...

  6. pytest进阶之xunit fixture

    前言 今天我们再说一下pytest框架和unittest框架相同的fixture的使用, 了解unittest的同学应该知道我们在初始化环境和销毁工作时,unittest使用的是setUp,tearD ...

  7. pytest进阶之conftest.py

    前言 前面几篇随笔基本上已经了解了pytest 命令使用,收集用例,finxture使用及作用范围,今天简单介绍一下conftest.py文件的作用和实际项目中如是使用此文件! 实例场景 首先们思考这 ...

  8. pytest进阶之fixture

    前言 学pytest就不得不说fixture,fixture是pytest的精髓所在,就像unittest中的setup和teardown一样,如果不学fixture那么使用pytest和使用unit ...

  9. pytest进阶

    参考文章 使用 pytest pytest 这个 库是一个第三方库,严格来说,它的设计思路不属于 xUnit 系列.但它使用起来比较方便,同时他又兼容 unittest 的用例:用 unittest ...

随机推荐

  1. hdu4767_Bell_矩阵快速幂+中国剩余定理

    2013长春赛区网络赛的1009题 比赛的时候这道题英勇的挂掉了,原因是写错了一个系数,有时候粗心比脑残更可怕 本题是关于Bell数,关于Bell数的详情请见维基:http://en.wikipedi ...

  2. mac下 编译php的 openssl

    编译openssl.so tar zxvf php-7.2.8.tar.gz# 进入PHP的openssl扩展模块目录cd php-7.2.8/ext/openssl/brew install ope ...

  3. Java基础(六) static五大应用场景

    static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.上一个章节我们讲了final关键字的原理及用法,本 ...

  4. 常用Linux网络命令

    TCP状态统计: netstat -anp TCP各个状态的连接数:netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a] ...

  5. 【JDK8】HashMap集合 源码阅读

    JDK8的HashMap数据结构上复杂了很多,因此读取效率得以大大提升,关于源码中红黑树的增删改查,博主没有细读,会在下一篇博文中使用Java实现红黑树的增删改查. 下面是类的结构图: 代码(摘抄自J ...

  6. .NET Core IdentityServer4实战 第Ⅴ章-单点登录

    OiDc可以说是OAuth的改造版,在最初的OAuth中,我们需要先请求一下认证服务器获取下Access_token,然后根据Access_token去Get资源服务器, 况且OAuth1 和 2 完 ...

  7. Linux 中 IDEA 不能调试(Debug)项目

    问题描述: can't debug project on idea linux. 在Linux 中, IDEA能运行项目,但是点击调试项目,弹出警告.警告内容如下: Required connecto ...

  8. .NET开发框架(五)-IIS上部署ASP.NET Core项目教程

    系列教程:从初学者到架构师的一步步蜕变 本篇经验将和大家介绍如何在IIS上部署ASP.NET Core项目,希望对初学.NET CORE的童靴入门有所帮助! 1.打开VS,创建项目,选择ASP.NET ...

  9. JAVA 从一个List里删除包含另一个List的数据

    /** * 从listA里删除listB里有的数据 * @param listA * @param listB * @return */ public static List<String> ...

  10. 【机器学习实践】解决Jupyter Notebook中不能正常显示中文标签及负号的方法

    import matplotlib.pyplot as plt plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签plt.rcParams[' ...