pytest系列文章一共有四篇,本文为第四篇完结篇。
应各位小伙伴留言,希望可以把这几篇pytest文章进行汇总。
公众号:梦无矶的测试开发之路,回复pytest可以领取对应汇总资料
本章知识点
@pytest.mark.parametrize("参数名",列表数据)
'''
参数名:用来接收每一项数据,并作为测试用例的参数。
列表数据:一组测试数据。
'''
@pytest.mark.parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None)):
参数 | 说明 |
---|---|
argnames | 必传,参数名, 以逗号分隔的字符串,表示一个或多个参数名称(key),或参数字符串的列表/元组 |
argvalues | 必传,参数值,若argnames有一个则单值列表传入,若argnames有多个,以套用元组的列表展示,元组内与参数名一一对应 |
indirect | 为true时,那argnames一定是一个fixture函数名称,argvalues值将传入对应的fixture内,相当于@pytest.fixture(params=)的用法,默认False |
ids | 标记子用例执行名称,与argvalues数量一致,未指定自动生成,默认None |
scope | 如果指定,则表示参数的范围。范围用于按参数实例对测试进行分组。它还将覆盖任何fixture函数定义的范围,允许使用测试上下文或配置设置动态范围 |
#示例
# 这里参数名 a,b,c,对应传入进去的参数a,b,c,需要一一对应。
# 如果只写了一个参数,那么就表示后面每一个以逗号分隔的内容,都为一组数据。
@pytest.mark.parametrize("a,b,c",[(1,2,3),(7,8,15),(2,2,3),(9,6,15)])
def test_add(a,b,c):
sum = a+b
assert sum==c
### 运行结果 ###
============================= test session starts =============================
collecting ... collected 4 items
test_004_参数化.py::test_add[1-2-3] PASSED [ 25%]
test_004_参数化.py::test_add[7-8-15] PASSED [ 50%]
test_004_参数化.py::test_add[2-2-3] FAILED [ 75%]
test_004_参数化.py:12 (test_add[2-2-3])
4 != 3
Expected :3
Actual :4
<Click to see difference>
a = 2, b = 2, c = 3
@pytest.mark.parametrize("a,b,c",[(1,2,3),(7,8,15),(2,2,3),(9,6,15)])
def test_add(a,b,c):
sum = a+b
> assert sum==c
E assert 4 == 3
test_004_参数化.py:16: AssertionError
test_004_参数化.py::test_add[9-6-15] PASSED [100%]
========================= 1 failed, 3 passed in 0.06s =========================
组合参数化 : 多组参数,依次组合,笛卡尔积
@pytest.mark.parametrize("x",[2,4,6])
@pytest.mark.parametrize("y",[1,3,5])
def test_add(x,y):
print(f"x:{x},y:{y}")
会生成9组测试用例
============================= test session starts =============================
collecting ... collected 9 items
test_004_参数化.py::test_add[1-2] PASSED [ 11%]x:2,y:1
test_004_参数化.py::test_add[1-4] PASSED [ 22%]x:4,y:1
test_004_参数化.py::test_add[1-6] PASSED [ 33%]x:6,y:1
test_004_参数化.py::test_add[3-2] PASSED [ 44%]x:2,y:3
test_004_参数化.py::test_add[3-4] PASSED [ 55%]x:4,y:3
test_004_参数化.py::test_add[3-6] PASSED [ 66%]x:6,y:3
test_004_参数化.py::test_add[5-2] PASSED [ 77%]x:2,y:5
test_004_参数化.py::test_add[5-4] PASSED [ 88%]x:4,y:5
test_004_参数化.py::test_add[5-6] PASSED [100%]x:6,y:5
============================== 9 passed in 0.03s ==============================
Process finished with exit code 0
1、pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行
2、位置:一般放在项目的根目录(即当前项目的顶级文件夹下)
3、命名:pytest.ini,不能使用任何中文符号,包括汉字、空格、引号、冒号等等
4、运行的规则:不管是主函数模式运行,命令行模式运行,都会自动读取这个全局配置文件。
示列
[pytest]
markers =
mark1:描述
mark2:描述
mark3:描述
addopts = -vs
;指定pytest最低版本号
minversion = 7.0
;pytest默认是搜索执行当前目录下的所有以test_开头的测试用例;
;我们可以在pytest.ini配置testpaths = test_case/test_001.py,则只执行当前配置的文件夹下或文件里的指定用例,可配置多个,空格隔开
testpaths = ./testcase
;模块名的规则,配置测试搜索的模块文件名称
python_files = test*.py
;类名的规则,配置测试搜索的测试类名
python_classes = Test*
;方法名的规则,配置测试搜索的测试函数名
python_functions = test
打标记: marks功能
1、得先注册标记名
pytest.ini
[pytest]
markers =
mark1:标签说明(只能英文,可不写)
mark2:标签说明(只能英文,可不写)
mark3:标签说明(只能英文,可不写)
2、给测试用例/测试类打标记
基本使用
@pytest.mark.已注册的标记
# 如
@pytest.mark.mark1
在测试类里面打标签,使用以下声明(测试类下,所有用例都被打上该标签):
class TestClass():
pytestmark = pytest.mark.已注册标签名
# 或者 多标签模式
pytestmark = [pytest.mark.标签名1,pytest.mark.标签名2]
在模块文件里打标签,使用以下声明(py文件下,所有测试函数和测试类里的测试函数,都被打上该标签)
import pytest
pytestmark = pytest.mark.已注册标签名
# 或者 多标签模式
pytestmark = [pytest.mark.标签名1,pytest.mark.标签名2]
3、运行时设置只运行标记的用例
pytest命令行: -m 标记名
在收集到的所有用例中,只运行对应标记名的用例。
4、可以叠加标记
参数 | 作用 |
---|---|
-s | 表示输出调试信息,用于显示测试函数中print()打印的信息 |
-v | 未加前只打印模块名,加v后打印类名、模块名、方法名,显示更详细的信息 |
-q | 表示只显示整体测试结果 |
-vs | 这两个参数可以一起使用 |
-n | 支持多线程或者分布式运行测试用例(前提需安装:pytest-xdist插件) |
–html | 生成html的测试报告(前提需安装:pytest-html插件) 如:pytest -vs --html ./reports/result.html |
-x | 表示只要有一个测试用例报错,则执行停止 |
-k | 模糊匹配,测试用例的部分字符串,指定执行测试用例。 |
-m | mark标记 |
@pytest.mark.skip
@pytest.mark.skipif
根据特定的条件,不执行标识的测试函数.
方法:
skipif(condition, reason=None)
参数:
condition:跳过的条件,必传参数
reason:标注原因,必传参数
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
当condition为True则跳过,否则执行
标记测试函数为失败函数
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用参数:
condition:预期失败的条件,必传参数
reason:失败的原因,必传参数
使用方法:
@pytest.mark.xfail(condition, reason="xx")
当condition为True则将这条用例标记为xpassed,否则标记为passed
使用"@pytest.mark.repeat"装饰器可以对测试用例进行重复运行
import pytest
@pytest.mark.repeat(3)
def test_something():
assert 1 + 1 == 2
这个测试用例会运行三次。
使用"@pytest.mark.dependency"装饰器标记测试用例之间的依赖关系,以确保在需要的测试用例之前运行先决条件
import pytest
@pytest.mark.dependency()
def test_login():
assert True
@pytest.mark.dependency(depends=["test_login"])
def test_access_profile():
assert True
@pytest.mark.dependency(depends=["test_login"])
def test_access_settings():
assert True
用例失败的情况下,可以重运行用例。
需要安装插件rerunfailures
安装方法:
pip install pytest-rerunfailures
使用方式:
命令行参数形式:
# 命令
pytest --reruns 重试次数
pytest --reruns 3 :运行失败的用例可以重新运行3次
拓展
#
pytest --reruns 重试次数 --rerun-dalay 次数之间的延时设置(单位:秒)
# 示列
pytest --reruns 3 --rerun-dalay 5
表示失败的用例可以重新运行3次,每次重运行间隔5秒。
前面有讲到pytest有自己的用例执行顺序,但有时候我们就是想指定它按照我的方式去执行,有什么办法呢?
pytest有这样一个插件可以实现这个功能,pytest-ordering
:指定用例的运行顺序
pip install pytest-ordering
通过装饰器的方法来控制case的执行顺序
1.方式一:
- 第一个执行: @ pytest.mark.run('first')
- 第二个执行: @ pytest.mark.run('second')
- 倒数第二个执行: @ pytest.mark.run('second_to_last')
- 最后一个执行: @ pytest.mark.run('last')
2.方式二:
- 第一个执行: @ pytest.mark.first
- 第二个执行: @ pytest.mark.second
- 倒数第二个执行: @ pytest.mark.second_to_last
- 最后一个执行: @pytest.mark.last
3.方式三:
- 第一个执行: @ pytest.mark.run(order=1)
- 第二个执行: @ pytest.mark.run(order=2)
- 倒数第二个执行: @ pytest.mark.run(order=-2)
- 最后一个执行: @ pytest.mark.run(order=-1)
执行优先级:
0>较小的正数>较大的正数>无标记>较小的负数>较大的负数
场景:存在一个增删改查相关的操作功能,当增加操作用例失败时,删除、修改、查询操作不执行,这种场景该怎么来处理呢?Pytest 框架提供了一个pytest-dependency 插件帮我们做了这件事情,我们只需要简单的使用即可。
pip install pytest-dependency
pip show pytest-dependency
使用方法 : 用 @pytest.mark.dependency()
对所依赖的用例进行标记,使用@pytest.mark.dependency(depends=["测试用例名称"])
引用依赖,测试用例名称可以是多个
说明:当依赖的用例执行失败,被依赖的用例直接跳过
# !/usr/bin/python3
# _*_coding:utf-8 _*_
import pytest
@pytest.mark.dependency()
def test_add():
print("我是 test_add 用例")
assert False
@pytest.mark.dependency(depends=["test_add"])
def test_update():
print("我是 test_update 用例")
assert False
@pytest.mark.dependency(depends=["test_add"])
def test_delete():
print("我是 test_delete 用例")
assert True
@pytest.mark.dependency(depends=["test_add"])
def test_select():
print("我是 test_select 用例")
assert True
if __name__ == '__main__':
pytest.main(["-s"])
用 @pytest.mark.dependency()对所依赖的用例进行标记,使用@pytest.mark.dependency(depends=[“测试用例名称”]) 引用依赖,测试用例名称可以是多个
用例多存在多个依赖时,只要存在一个依赖失败,被依赖用例就跳过,所有依赖成功才执行
还有其他插件和功能可以自行在插件库进行探索。
DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
if version.LooseVersion(pytest.version) >= version.LooseVersion(“6.0”):
网上很多说是版本过低啥的,我验证后发现,并不是,而是pytest有很多相关依赖,当前所用的环境和当前你安装的pytest所需要的一些依赖无法匹配,比如Allure之类的。
所以很简单的做法就是,你重新创建一个python的环境,保持一个项目拥有一套干净的环境,这样就不会出现类似的依赖冲突,相互不满足提示告警甚至报错的情况。
进入虚拟环境,在pycharm里面点击Terminal选择Command Prompt,前面显示有个括号的就是进入了虚拟环境。
#退出虚拟环境
deactivate
#进入虚拟环境,重开命令窗口更快
activate
至此,pytest系列完结,感谢各位小伙伴阅读至此,你一定是最棒的 ~