pytest是一个非常成熟的全功能的Python测试框架,主要特点有以下几点:
1、简单灵活,容易上手,文档丰富;
2、支持参数化,可以细粒度地控制要测试的测试用例;
3、能够支持简单的单元测试和复杂的功能测试,还可以用来做;selenium/appnium等自动化测试、接口自动化测试pytest+requests;
4、pytest具有很多第三方插件,并且可以自定义扩展;
通过 pytest.ini 配置文件注册,在文件当中的添加如下
[pytest]
markers=
标记:标记说明
…
注意:pytestmark是固定的
可以标记文件,文件下的所有用例都会打上标记
在文件下全局变量处打上标记pytestmark = [pytest.mark.标签1,pytest.mark.标签2]
可以标记测试类,类型的所有用例都会打上标记
1、可以在类前使用装饰器@pytest.mark.xxx;
2、也可以在类下使用pytestmark = [pytest.mark.标签1];
调用 pytest.main()函数带上命令参数["-m 标签名"]即可,支持and与or关系运算;
Pytest处理前置后置有两种方式可以处理:
1、第一种是通过setup和teardown这样的方法去处理;
2、第二种是通过fixture来实现的;
1、模块级(setup_module/teardown_module),开始于模块始末,作用于全局(总用各执行一次);
用法:函数放在类外面才能生效;
2、函数级(setup_function/teardown_function),仅对函数用例生效(即不在类中,每个函数执行一次);
用法:函数放在类外面才能生效;
3、类级(setup_class/teardown_class),只在类中前后运行一次;
用法:函数放在类里面才能生效;
4、方法级(setup_method/teardown_method),开始于方法始末(在类中,每个方法执行一次);
用法:函数放在类里面才能生效;
5、类里面的(setup/teardown),运行在调用方法的前后(每个方法执行一次);
用法:函数放在类里面才能生效;
类级、方法级、类里面的前后置处理顺序:
A、setup_class是在所有用例执行前运行的,teardown_class是在所有用例执行完成后运行的。
B、setup_method是在每一条用例执行前,且在setup执行前运行的,teardown_method是在每一条用例执行完成后,且在teardown执行完成后运行的。
C、setup是在每条用例执行前运行,teardown是在每条用例执行完成后运行。
所以,他们的顺序是:
setup_class-
setup_method-setup-用例1-teardown-teardown_method-
setup_method-setup-用例2-teardown-teardown_method-
setup_method-setup-用例n-teardown-teardown_method-
……
teardown_class
pytest.fixture():作用于模块内的所有用例,但需要传递装饰函数为参数,可置于class内或class外;
1、fixture 可以作为一个函数的参数被调用
2、fixture可以在一个类、或者一个模块、或者整个session中被共享,加上范围scope参数即可,如@pytest.fixture(scope=‘module’)
3、fixture也可以单独存放
有的时候为了方便配置和访问,共享 fixture 函数,也可以将这样的fixture放到conftest.py文件中单独存放;conftest.py 文件中的 fixture 函数不需要在测试函数中导入,可以被 pytest 自动识别,查找顺序从测试类开始,然后是测试模块,然后是 conftest.py 文件,最后是内置插件和第三方插件。
4、同一个模块里出现多个范围的装饰
当出现多个范围装饰则优先实例化范围优先级高的,也就是优先级从大到小:session–>module–>class–>function;
6、yield方法实现setup/teardown的功能
在yield关键字之前的代码在case之前执行,yield之后的代码在case运行结束后执行
@pytest.fixture()
def login():
print(\"登录\")
yield
print(\"退出登录\")
7、addfinalizer方法实现setup/teardown的功能
addfinalizer也可以实现环境的清理,实现与yield方法相同的效果,跟yield不同的是需要注册作为终结器使用的函数
例如:
@pytest.fixture()
def login(request):
print(\"登录\")
def demo_finalizer():
print(\"退出登录\")
# 注册demo_finalizer为终结函数
request.addfinalizer(demo_finalizer)
注意request是固定的,不能随便写的
8、fixture中的参数 autouse
默认是False, autouse设置为True时,自动调用fixture功能。由于默认作用域为function,不指定scope则每个方法都会调用fixture方法。
9、fixture函数参数化
如果多条用例都需要调用相同参数,可以将fixture函数参数化。fixture 函数将执行每个参数值,fixture通过固定参数request传递。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope="module", params=['测试1','测试2','测试3'])
def data(request):
yield request.param
class Test_Demo():
def test_case1(self, data):
print('测试用例01')
print(data)
if __name__ == "__main__":
pytest.main(["-sv", "test.py"])
运行结果如下:
test.py::Test_Demo::test_case1[\\u6d4b\\u8bd51] 测试用例01
测试1
PASSED
test.py::Test_Demo::test_case1[\\u6d4b\\u8bd52] 测试用例01
测试2
PASSED
test.py::Test_Demo::test_case1[\\u6d4b\\u8bd53] 测试用例01
测试3
PASSED
10、pytest.mark.usefixtures的使用
@pytest.mark.usefixtures的使用,usefixtures达到的效果和setup和teardown一样
11、 当case里需要传入多个 fixture 或者 yield 怎么办呢? 先后执行的顺序是什么?
(“before”表示执行在case前,“yield”表示执行在case后)
pytest --html=report.html --self-contained-html
pytest.main(['--html=./report.html', 'test_testing_topic.py'])
1.、Pytest Marker 机制
可以在每一个测试用例加一个marker,比如pytest运行的时就只运行带有该marker的测试用例,比如下面的pytest.main(["-m test"]);
2、选择运行特定的某个测试用例
你可以按照某个测试用例的的模块,类或函数来选择你要运行的case,比如下面的方式就适合一开始在调试单个测试用例的时候;
pytest -v test_pytest_markers.py::TestClass::test_method
3、选择运行特定的某个类
pytest -v test_pytest_markers.py::TestClass
4、用-k进行关键字匹配来运行测试用例名字子串
pytest -v -k http test_pytest_markers.py
5、重跑失败的用例,使用pytest-rerunfailures
python3 -m pytest -reruns 重跑次数
6、多进程运行
安装pytest-xdist:pip install -U pytest-xdist
如何使用:使用参数"-n"
py.test test_pyexample.py -n NUM
其中NUM填写并发的进程数。
pytest里面的断言实际上就是python里面assert的断言方法,常用以下几种:
pytest数据驱动,就是参数化,使用@pytest.mark.parametrize
在测试用例的前面加上:
pytest-sugar
测试进度可视化
用法:不需要配置操作
pytest-parallel
pytest-parallel在Windows下只支持多线程,不支持多进程,在Linux和mac下即支持多进程又支持多线程
参数值配置:
--workers=n 多进程运行需要加此参数,n是进程数。默认为1 【注:在windows系统中只能为1】
--tests-per-worker=n 多线程运行需要加此参数,n是线程数。
如果两个参数都配置了,就是进程并行,每个进程最多n个线程,总线程数:进程数*线程数
pytest-parallel的workers参数在windows系统下永远是1,在linux和mac下可以取不同值。
pytest-xdist
多进程并行与分布式执行,只支持多进程不支持多线程;
用法:使用参数"-n",如py.test test_pyexample.py -n NUM
注意:使用的前提是用例之间都是独立的,没有先后顺序,随机都能执行,可重复运行不影响其他用例
pytest-rerunfailures
支持用例失败重试功能
运行时指定:pytest -v -s 文件名.py -reruns 5 --reruns-delay 1 每次等1秒 重试5次
测试方法上指定:@pytest.mark.flaky(reruns=5,reruns-delay=1) 每次等1秒 重试5次
pytest-picked
插件可以实现只运行未提交到git仓库的代码
--picked=[{only,first}] Run the tests related to the changed files either on their own, or first
--mode=PICKED_MODE Options: unstaged, branch
使用示例:
pytest --picked 所有测试都将从已修改但尚未提交的文件和文件夹中运行
pytest --picked=first 首先运行修改后的测试文件中的测试,然后运行所有未修改的测试
pytest --picked --mode=branch 运行分支上已经被暂存但尚未提交的代码
pytest --picked --mode=unstaged # default 会默认执行所有的 Untracked 文件和 not staged 文件
pytest-ordering
指定用例的执行顺序
装饰器用法:@pytest.mark.run(order=xx)
pytest-cov
在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况;
运行用例的时候加上 --cov 参数
生成html的报告,pytest --cov --cov-report=html
指定被测代码,可以通过--cov=模块 来运行
pytest-instafail
可以在运行用例的时候,实时查看用例报错内容
用法: --instafail 方便实时查看报错内容,结合--tb=line参数,看起来更直观
pytest-tldr
pytest-tldr 将默认输出限制为失败测试的回溯信息,并忽略了一些令人讨厌的颜色编码。添加 -v 标志会为喜欢它的人返回更详细的输出;
pytest-django
pytest-django 引入了使用 pytest fixture 测试 Django 项目的能力,而省略了导入 unittest 和复制/粘贴其他样板测试代码的需要,并且比标准的 Django 测试套件运行得更快;
pytest-html
pytest用于生成测试结果的HTML报告
用法:pytest --html=report.html --self-contained-html
allure-pytest
生成Allure报告,Allure框架是一个灵活的轻量级多语言测试报告工具,它不仅以web的方式展示了简介的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息;
用法:pytest.main(['-s', '-q', '--alluredir', './report/xml'])
-q 的意思是减少报告多余
--alluredir 的意思是生成allure报告的数据的目标目录,即测试目录运行后的结果,是生成xml的数据集合
生成Allure报告,在cmd下运行==> allure generate --clean ./report/xml/ -o ./results/html/
Allure学习参考:https://www.cnblogs.com/linuxchao/p/linuxchao-pytest-allure.html
pytest-assume
前面的断言失败了后继续执行
用法:pytest.assume(xxx == xxx)
pytest-timeout
设置执行用例的超时时间
用法:pytest --timeout=2
pytest-repeat
重复执行,当执行一个测试用例时偶发性BUG时,我们可以使用 pytest-repeat 进行重复测试直到失败。
用法:pytest -v -s --count=2 test_open.py 重复执行2次
pytest-dependency
使用该插件可以标记一个test作为其他test的依赖,当依赖项执行失败时,那些依赖它的test将会被跳过;
用法:用 @pytest.mark.dependency()对所依赖的方法进行标记,使用@pytest.mark.dependency(depends=[“test_name”])引用依赖
import pytest
@pytest.mark.dependency()
def test_01(test):
assert False
@pytest.mark.dependency(depends=["test_01"])
def test_02(test):
print("执行测试2")
运行结果:test_01是test_02的依赖,故而test_01失败后,test_02被跳过