一、挑选用例执行
Pytest 可以灵活的挑选测试用例执,方式如下:
1.1.指定一个模块
可以像这样只挑选一个模块执行
pytest .\autotest\test_case01.py
1.2.指定目录
可以像这样只挑选一个目录执行
pytest test_case
也可以指定多个目录
pytest autotest test_case
1.3.指定模块里面的函数或者类
指定一个类
pytest .\test_case\test_case01.py::Test_pass
也可以指定类里面的方法
pytest .\test_case\test_case01.py::Test_pass::test_02
1.4.根据名字
可以使用 命令行参数 -k 后面加名字来挑选要执行的测试项,比如像这样后面跟测试函数名字的一部分:
pytest -k test_33 -s
注意,-k 后面的名字
- 可以是测试函数的名字,可以是类的名字,可以是模块文件名,可以是目录的名字
- 是大小不敏感
- 不一定要完整,只要能有部分匹配上就行
- 可以用 not 表示选择名字中不包含,比如
pytest -k "not test_33" -s
- 可以用 and 表示选择名字同时包含多个关键字,比如
pytest -k "33 and test" -s
- 可以用 or 表示选择名字 包含指定关键字之一即可,比如
pytest -k "33 or test" -s
1.5.根据标签
- 可以这样给 某个方法加上标签 weblogintest
import pytest
class Test_pass:
@pytest.mark.weblogintest
def test_02(self):
print('********用例02********')
assert 2 == 2
然后,可以这样运行指定标签的用例
pytest test_case -m weblogintest -s
- 也可以这样给整个类加上标签
import pytest
@pytest.mark.weblogintest
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
def test_03(self):
print('********用例03********')
assert 3 == 2
- 可以这样同时添加多个标签
import pytest
@pytest.mark.weblogintest
@pytest.mark.webapilogintest
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
def test_03(self):
print('********用例03********')
assert 3 == 2
- 可以定义一个全局变量 pytestmark 为
整个模块文件
设定标签
使用pytestmark全局变量:在模块文件的顶部,在导入语句之前,定义一个pytestmark变量,该变量是一个装饰器。使用该装饰器可以给整个模块文件设置标签。示例如下:
import pytest
pytestmark = pytest.mark.slow
def test_func1():
print("************test_func1************")
assert True
def test_func2():
print("************test_func2************")
assert True
根据上面的标签slow执行如下:
- 如果需要定义多个标签,可以定义一个列表
import pytest
# 定义多个标签,放在列表中
pytestmark = [pytest.mark.login, pytest.mark.weblogintest, pytest.mark.webapilogintest]
def test_func1():
print("************test_func1************")
assert True
def test_func2():
print("************test_func2************")
assert True
执行后如下:
二、skip、skipif跳过用例
2.1.场景说明
- pytest.mark.skip 可以标记无法在某些平台上运行的测试功能,或者您希望失败的测试功能
- 希望满足某些条件才执行某些测试用例,否则pytest会跳过运行该测试用例
- 实际常见场景:跳过非Windows平台上的仅Windows测试,或者跳过依赖于当前不可用的外部资源(例如数据库)的测试
2.2.@pytest.mark.skip注解
@pytest.mark.skip作用是跳过执行测试用例,有可选参数reason:跳过的原因,会在执行结果中打印
import pytest
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
@pytest.mark.skip(reason="不在本次测试范围,所以跳过cls")
def test_03(self):
print('********用例03********')
assert 3 == 2
执行发现test_03执行跳过没有执行,如下:
注意:
- @pytest.mark.skip 可以加在函数上,类上,类方法上
- 如果加在类上面,类里面的所有测试用例都不会执行
pytest.skip(msg="",allow_module_level=False),设置参数当 allow_module_level=True 时,可以设置在模块级别跳过整个模块,test_case13.py如下:
import pytest
@pytest.skip(msg="整个模块跳过", allow_module_level = True)
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
@pytest.mark.skip(reason="不在本次测试范围,所以跳过cls")
def test_03(self):
print('********用例03********')
assert 3 == 2
执行结果如下,发现test_case13.整个模块跳过没有执行:
2.3.@pytest.mark.skipif(condition, reason="")
@pytest.mark.skipif(condition, reason=""),作用主要是希望有条件地跳过某些测试用例
注意:condition需要返回True才会跳过
案例代码如下:
import sys
import pytest
@pytest.mark.skipif(sys.platform=="win32", reason="在Windows系统上不能运行")
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
def test_03(self):
print('********用例03********')
assert 3 == 2
执行发现由于系统是win32,条件满足所以不执行
2.4.跳过标记
可以将 pytest.mark.skip 和 pytest.mark.skipif 赋值给一个标记变量,在不同模块之间共享这个标记变量,若有多个模块的测试用例需要用到相同的 skip 或 skipif ,可以用一个单独的文件去管理这些通用标记,然后适用于整个测试用例集
import sys
import pytest
# 赋值给一个标记变量
skipmark = pytest.mark.skip(reason="不在测试范围之内,跳过不执行")
skipifmark = pytest.mark.skipif(sys.platform == "win32", reason="在Windows系统上不能运行")
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
@skipifmark
def test_02(self):
print('********用例02********')
assert 2 == 2
@skipmark
def test_03(self):
print('********用例03********')
assert 3 == 2
执行发现test_03和test_02由于添加了标记变量作为装饰器,跳过了用例不执行,如下:
三、失败重跑插件pytest-rerunfailures
pytest-rerunfailures
是一个用于在测试失败时重新运行失败的测试用例的Pytest插件。它可以在测试失败后自动重新运行指定次数的失败用例,以提高测试用例的可靠性和稳定性。
3.1.安装插件
pip3 install pytest-rerunfailures -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
执行有两种方式,命令行和装饰器方式如下:
- 命令行参数:--reruns n(重新运行次数),--reruns-delay m(等待运行秒数)
- 装饰器参数:reruns=n(重新运行次数),reruns_delay=m(等待运行秒数)
3.2.重新运行所有失败的用例
要重新运行所有测试失败的用例,使用 --reruns 命令行选项,并指定要运行测试的最大次数:
pytest --reruns 5 -s
运行失败的 fixture 或 setup_class 也将重新执行,案例:准备测试用例test_case01.py:
import pytest
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
def test_03(self):
print('********用例03********')
# 故意制造错误
1 / 0
assert 3 == 2
执行代码如下,会发现test_03被执行了6次,其中5次是设置的重试的次数:
3.3.添加重新运行的延时
要在两次重试之间增加延迟时间,使用 --reruns-delay 命令行选项,指定下次测试重新开始之前等待的秒数
pytest --reruns 5 --reruns-delay 10 -s
上面设置后会重试5次,每次重试间隔10s后继续下一次重试
3.4.重新运行指定的测试用例
要将单个测试用例添加flaky装饰器 @pytest.mark.flaky(reruns=5) ,并在测试失败时自动重新运行,需要指定最大重新运行的次数,下面用例test_03添加了装饰器,如下:
import pytest
# @pytest.mark.weblogintest
# @pytest.mark.webapilogintest
class Test_pass:
def test_01(self):
print('********用例01********')
assert 1 == 1
def test_02(self):
print('********用例02********')
assert 2 == 2
# 通过装饰器设置失败重试
@pytest.mark.flaky(reruns=5, reruns_delay=10)
def test_03(self):
print('********用例03********')
1 / 0
assert 3 == 2
执行后如下:
注意:
- 如果指定了用例通过装饰器指定了重新运行次数,则在命令行运行的时候添加 --reruns 对这些用例是不会生效的
3.5.失败重跑插件的兼容性
虽然pytest-rerunfailures
插件可以提供测试失败重跑的功能,但在使用时需要注意其与@pytest.fixture()
装饰器、pytest-xdist
的--looponfail
标志和核心的--pdb
标志的不兼容性。根据具体的需求和情况,选择合适的插件和标志来进行测试。
- pytest-rerunfailures插件不支持与@pytest.fixture()装饰器一起使用。这是因为pytest-rerunfailures插件在测试失败时会重新运行整个测试用例,包括其中的fixture。如果fixture具有副作用或者状态,可能会导致不可预测的行为。因此,建议不要在使用pytest-rerunfailures插件时与@pytest.fixture()装饰器一起使用。
- pytest-rerunfailures插件与pytest-xdist的--looponfail标志不兼容。--looponfail标志是pytest-xdist插件提供的功能,它可以在测试失败时自动重新运行失败的测试用例。由于这两个插件都提供了类似的功能,因此在使用时可能会产生冲突。建议在使用pytest-rerunfailures插件时不要同时使用--looponfail标志。
- pytest-rerunfailures插件与核心的--pdb标志不兼容。--pdb标志用于在测试失败时自动进入Python调试器(PDB)以进行调试。由于pytest-rerunfailures插件会在测试失败时重新运行失败的测试用例,这可能会导致PDB会被多次触发,从而导致不可预测的行为。因此,在使用pytest-rerunfailures插件时不建议同时使用--pdb标志。
四、配置文件pytest.ini
4.1.pytest.ini文件说明
pytest.ini
是一个配置文件,用于自定义和配置Pytest运行测试时的行为和选项。通过在项目根目录下创建名为pytest.ini
的文件并在其中定义配置选项,可以修改和扩展Pytest的默认行为。
pytest里面有些文件是非test文件
- pytest.ini:pytest的主配置文件,可以改变pytest的默认行为
- conftest.py:测试用例的一些fixture配置
- _init_.py:识别该文件夹为python的package包
查看pytest.ini的配置选项,cmd执行
pytest --help
4.2.pytest.ini应该放哪里?
pytest.ini放在项目根目录下 ,位置不要随便改动,名字是固定的不要修改。
4.3.常见配置
4.3.1.marks
- 作用:如果测试用例中添加了 @pytest.mark.webinfotest 装饰器,如果不添加marks选项的话,就会报warnings
- 格式:list列表类型
案例如下:
[pytest]
markers =
slow: mark a test as slow
smoke: mark a test as smoke
api: mark a test as an API test
上面markers
:定义了测试用例标签(markers)。在这个示例中,定义了slow
、smoke
和api
三个标签,用于对测试用例进行分类和选择性运行。
4.3.2.xfail_strict
- 作用:设置xfail_strict = True可以让那些标记为@pytest.mark.xfail,但实际通过显示XPASS的测试用例被报告为失败
- 格式:True 、False(默认),1、0
在Pytest中,.xfail_strict
是一个标记,用于指定关于"expected failure"的严格级别。它可以用于在某些测试用例上标记为"expected failure",并根据指定的级别来处理这些失败。.xfail_strict
的作用是控制关于被标记为"expected failure"的用例的处理方式。它有以下几个选项:
True
:严格模式,如果标记为"expected failure"的用例在执行时没有失败(即断言成功),则会被视为失败。这意味着,即使预期这些用例会失败,但如果它们成功了,测试运行结果也会显示为失败。False
:宽松模式,如果标记为"expected failure"的用例在执行时没有失败(即断言成功),则会被视为通过。这意味着,即使这些用例成功了,测试运行结果也会显示为通过。'no'
:与False
相同,这是.xfail
标记的默认行为,表示宽松模式。
案例
[pytest]
markers =
slow: mark a test as slow
smoke: mark a test as smoke
api: mark a test as an API test
xfail_strict = True
准备测试用例如下:
import pytest
@pytest.mark.xfail()
def test_20():
assert "a" != "b"
未设置 xfail_strict = True 时,执行用例,测试结果显示XPASS
已设置 xfail_strict = True 时,测试结果显示failed
4.3.3.addopts
addopts参数可以更改默认命令行选项,这个当我们在cmd输入一堆指令去执行用例的时候,就可以用该参数代替了,省去重复性的敲命令工作,例如:当测试结束生成报告,失败重跑两次,一共运行两次,通过分布式去测试,如果在cmd中写的话,命令会很长
pytest -v --rerun=2 --count=2 --html=report.html --self-contained-html -n=auto
通过pytest.ini 中addopts就可以解决这个问题,命令如下:
addopts = -vs -rsxX -l --tb=short --reruns=1 -count=2 --html=./result/reports.html --self-contained-html
参数:
- -v:表示输出详细的测试结果信息。对每个执行的测试用例,会显示其名称和结果。
-
-rsxX
分别表示显示测试结果的简要信息、显示跳过的测试、显示出错的测试和显示预期失败的测试; -
-l
表示显示每个测试的结果一行; -
--tb=short
表示在测试失败时只显示简短的回溯信息。 - --reruns=1:表示对测试失败的用例重新运行一次。如果某个测试用例在第一次运行时失败了,Py test会自动再次运行该用例。
- --count=2:表示运行测试用例的次数。每个测试用例将被运行两次。
- --html=./result/reports.html:表示生成HTML格式的测试报告,并将其保存为reports.html文件。
- --self-contained-html:表示生成的HTML测试报告是自包含的,即在单个HTML文件中包含了所有相关的资源(CSS、Java Scrip等),可以单独打开和查看
加了addopts之后,我们在cmd中只需要敲pytest就可以生效了!!
4.3.4.log_cli
pytest
提供了一个名为log_cli
的命令行选项,用于控制测试日志的输出。
- 作用:控制台实时输出日志
- 格式:log_cli=True 或False(默认),或者log_cli=1 或 0
在pytest.ini 中添加如下:
[pytest]
addopts = -vs -rsxX -l --tb=short --reruns=1 -count=2 --html=./result/report.html --self-contained-html
markers =
slow: mark a test as slow
smoke: mark a test as smoke
api: mark a test as an API test
log_cli=1
执行后如下:
注意:加了log_cli=1之后,可以清晰看到哪个package下的哪个module下的哪个测试用例是否passed还是failed;所以平时测试代码是否有问题的情况下推荐添加上加,但如果拿去批量跑测试用例的话不建议加,会影响运行性能。
4.3.5.norecursedirs
norecursedirs
是 pytest
的一个配置选项,用于告诉 pytest
在递归查找测试文件时要忽略的目录。这对于排除特定目录下的测试文件非常有用,以避免重复运行测试或不必要的测试。
以下是一个在 pytest.ini
文件中使用 norecursedirs
的示例,
- 假设你的项目的结构如下:
project/
├── tests/
│ ├── test_folder/
│ │ ├── test_file1.py
│ │ └── test_file2.py
│ └── test_file3.py
└── pytest.ini
- 在
pytest.ini
文件中,添加以下内容:
[pytest]
norecursedirs = test_folder
上述配置告诉 pytest
在搜索测试文件时忽略名为 test_folder
的目录。
- 确保在
pytest.ini
文件所在的目录中执行pytest
命令。
现在,当你运行 pytest
命令时,pytest
将会递归搜索 tests
目录下的测试文件,但会忽略名为 test_folder
的目录中的测试文件。
请注意,norecursedirs
配置选项接受一个逗号分隔的目录列表,可以同时指定多个需要忽略的目录。例如,norecursedirs = test_folder, docs
将同时忽略 test_folder
和 docs
目录中的测试文件。
在 pytest.ini
中配置 norecursedirs
选项可以方便地一次性定义要忽略的目录,而不必在每次运行 pytest
命令时都手动指定 --ignore
选项。
4.3.6.更改测试用例收集规则
pytest默认的测试用例收集规则
- 文件名以 test_*.py 文件和 *_test.py
- 以 test_ 开头的函数
- 以 Test 开头的类,不能包含 __init__ 方法
- 以 test_ 开头的类里面的方法
可以修改或者添加这个用例收集规则的,是建议在原有的规则上添加的,在 pytest.ini
文件中使用 testpaths
, python_files
, python_classes
, python_functions
是用来筛选测试文件、测试类和测试函数的选项。
testpaths
:用于指定测试文件所在的路径。可以接受一个逗号分隔的目录列表,用来指定要搜索测试文件的目录。例如,testpaths = ./test_case
将会在./test_case
目录下搜索测试文件。python_files
:用于指定测试文件的命名格式。可以接受一个通配符格式的字符串,比如test_*.py
。这个选项告诉pytest
只运行文件名符合test_*.py
格式的测试文件。python_classes
:用于过滤测试类。可以接受一个通配符格式的字符串,比如Test*
。这个选项告诉pytest
只运行以Test
开头的测试类。python_functions
:用于过滤测试函数。可以接受一个通配符格式的字符串,比如test*
。这个选项告诉pytest
只运行以test
开头的测试函数。
这些选项的作用是帮助你在运行测试时筛选出指定的测试文件、测试类和测试函数,以便更精确地控制哪些测试要被运行。
例如,假设你的测试文件都位于 ./test_case
目录下,且文件名以 test_
开头,测试类以 Test
开头,测试函数以 test
开头,你可以在 pytest.ini
文件中进行如下配置:
[pytest]
addopts = -vs -rsxX -l --tb=short --reruns=1 -count=2 --html=./result/report.html --self-contained-html
markers =
slow: mark a test as slow
smoke: mark a test as smoke
api: mark a test as an API test
log_cli=1
xfail_strict = True
testpaths = ./test_case
python_files = test_*.py
python_classes = Test*
python_functions = test*
这样配置后,当你运行 pytest
命令时,pytest
将只运行满足上述条件的测试文件、测试类和测试函数。这些选项的使用可以让你更有效地管理测试文件和测试用例,将关注点更集中在你要测试的特定部分上,提高测试运行的效率。