书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》
一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!
附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客
实际工作中,测试用例的执行可能会依赖于一些外部条件,例如:只能运行在某个特定的操作系统(Windows),或者我们本身期望它们测试失败,例如:被某个已知的Bug所阻塞。如果我们能为这些用例提前做上标记,那么pytest就可以相应地预处理它们,并提供一个更加准确的测试报告。
在这种场景下,常用的标记有以下几种。
skip:只有当某些条件得到满足时,才执行测试用例,否则跳过整个测试用例的执行。例如,在非Windows平台上跳过只支持Windows系统的用例。
xfail:因为一个确切的原因,我们知道这个用例会失败。例如,对某个未实现的功能的测试,或者阻塞于某个已知Bug的测试。
pytest默认不显示skip和xfail用例的详细信息,我们可以通过-r选项来自定义这种行为。
通常,我们使用一个字母作为一种类型的代表,具体的规则如下:
(f)ailed,(E)rror,(s)kipped,(x)failed,(X)passed,(p)assed,(P)assed with output,(a)ll except passed(p/P),or(A)ll。
下面通过几种不同的实现方式对不同层次的用例进行跳过操作。
@pytest.mark.skip装饰器
跳过执行某个用例最简单的方式就是使用@pytest.mark.skip装饰器,并且可以设置一个可选参数reason,表明跳过的原因,使用装饰器的原因是在不修改测试方法函数代码的前提下就可达到目的。
例如:如果要直接跳过test_cakan()方法,则可以在方法的上面直接加上@pytest.mark.skip,这样这种方法就不会被执行了。
import pytest
def f():
return 3
@pytest.mark.skip
def test_cakan():
pass
assert f() == 4
加上跳过的原因,其代码可以这样写。
import pytest
def f():
return 3
@pytest.mark.skip(reason="当前没有这个办法")
def test_cakan():
pass
assert f() == 4
执行的结果如下,如果显示s,则表示跳过,第一种方法没写原因,则显示unconditional skip,第二种方法写了原因,则直接显示具体原因。
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target test_assert_skip_1.py::test_cakan
Testing started at 10:58 ...
Launching pytest with arguments test_assert_skip_1.py::test_cakan in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ... collected 1 item
test_assert_skip_1.py::test_cakan SKIPPED [100%]
Skipped: 当前没有这个办法
============================= 1 skipped in 0.01s ==============================
Process finished with exit code 0
pytest.skip方法
如果我们想在测试执行期间(也可以在SetUp/TearDown期间)强制跳过后续的步骤,则可以考虑pytest.skip()方法,它同样可以设置一个参数msg,表明跳过的原因。
例如:从配置文件中读取的数值与系统返回的值比对。如果读取的是32位系统的数值,但配置所返回的数值是64位的系统的数值,比较后不符合,所以返回0。返回1表示有效配置,返回0表示无效配置。这个可以测试不同配置在不同环境下是否配置成功。
代码如下,其他功能代码需要自己补全:
import pytest
def valid_config():
return 0
def test_function():
if not valid_config():
pytest.skip("不支持此项配置")
执行的结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target test_assert_skip_1.py::test_function
Testing started at 11:03 ...
Launching pytest with arguments test_assert_skip_1.py::test_function in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ... collected 1 item
test_assert_skip_1.py::test_function SKIPPED [100%]
Skipped: 不支持此项配置
============================= 1 skipped in 0.09s ==============================
Process finished with exit code 0
另外,还可以为其设置一个布尔型的参数allow_module_level(默认为False),表明是否允许在模块中调用这种方法,如果置为True,则跳过模块中剩余的部分,也就是说其值为True时这个模块中所有测试方法都被跳过。
例如,在Mac平台下,sys.platform返回darwin,测试这个模块。
代码如下:
import sys
import pytest
if not sys.platform.startswith("darwin"):
print(sys.platform)
pytest.skip("如果不是MAC系统,就跳过而不执行,如果是MAC系统,因为后面参数是模块级参数,所以是整个文件的用例", allow_module_level=True)
def f():
print("\n"+sys.platform)
return 3
def test_cakan():
assert f() == 4
def test_2():
print("查看这个用例是不是也被执行,当参数是win32时不执行")
pass
将参数换成win32后,运行结果如下:因为笔者的计算机是Mac系统的,所以就跳过不执行。
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest .\test_Windows_skip.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 0 items / 1 skipped
========================================================================================================= 1 skipped in 0.06s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
注意:当在用例中设置allow_module_level参数时,并不会生效,因为这个参数是模块级专用参数。
在代码中增加语句allow_module_level=True,由于这个是模块级专用的参数,所以放在某个测试方法中起不到把整个模块也就是文件都跳过的作用。
代码如下:
import sys
import pytest
def test_login():
pytest.skip("跳出", allow_module_level=True)
assert 'linda' == 'linda'
def f():
print("\n"+sys.platform)
return 3
def test_cakan():
assert f() == 4
def test_2():
print("查看这个用例是不是也被执行,当参数是win32时不执行")
pass
执行的结果如下,只跳过这个用例,而不跳过这个用例下面所有的用例。也就是说,allow_module_level=True这个参数虽然影响范围是文件模块级但放在某种方法用例中,它就不起作用了,如图2-12所示。第1种方法跳过,第2种方法失败,第3种方法成功了。
后两种方法并未跳过,而是都执行了,所以用例中的allow_module_level=True这个参数未影响到整个模块。
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest .\test_Windows_skip.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 3 items
test_Windows_skip.py sF. [100%]
============================================================================================================== FAILURES ===============================================================================================================
_____________________________________________________________________________________________________________ test_cakan ______________________________________________________________________________________________________________
def test_cakan():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
test_Windows_skip.py:13: AssertionError
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
win32
======================================================================================================= short test summary info =======================================================================================================
FAILED test_Windows_skip.py::test_cakan - assert 3 == 4
=============================================================================================== 1 failed, 1 passed, 1 skipped in 0.11s ================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
@pytest.mark.skipif装饰器
带条件的跳过执行:满足条件就跳过,不满足条件就不跳过。在单个用例中使用有条件的跳过,以及在单个用例中使用两种有条件的跳过,体会这两个条件的逻辑组合所产生的不同的结果。使用装饰器可以将条件跳过在模块级共享
- 在单个用例中使用pytest.mark.skipif标记
如果想有条件地跳过某些测试用例的执行,可以使用@pytest.mark.skipif装饰器。
例如:不同的环境执行不同的用例。
代码如下:
- 一个用例上用两个不同的pytest.mark.skipif标记
例如,当Python的版本低于3.6时,跳过用例,并且一个用例上面可以叠加不同的跳过条件。
代码如下:
import pytest
import sys
environment = 'android'
@pytest.mark.skipif('environment=="android"', reason='android平台没有这个功能')
def test_cart_3():
pass
@pytest.mark.skipif(sys.platform=='win32', reason='不在Windows下运行')
@pytest.mark.skipif(sys.version_info<(3,6), reason='3.6版本以下不执行,你需要更高版本')
def test_cart():
print("当Python版本大于或等于3.6时执行,Windows下不执行")
pass
执行结果如下,第一种方法跳过,第二种方法执行了。因为Python的版本为3.6.8,并且不是Windows平台,所以执行了。
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest .\test_assert_skip_2.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 2 items
test_assert_skip_2.py ss [100%]
========================================================================================================= 2 skipped in 0.07s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
你也可以把版本中子版本6改成9,这样就应该跳过了。提示信息未改,执行结果如下:
注意:sys.version_info <(3,6),这个3后面是逗号“,”而不是点。
当一个用例指定了多个skipif条件时,只需满足其中一个,就可以跳过这个用例的执行。
- 两个模块之间共享pytest.mark.skipif标记
例如:我们新建test_module文件夹,创建两个文件:test_module_skip_1和test_module_skip_2,在文件test_module_skip_1中定义minversion,表明支持的Python最低版本。
代码如下:
import sys
import pytest
minversion = pytest.mark.skipif(sys.version_info<(3,8), reason='请使用Python3.8或者更高的版本')
@minversion
def test_one():
assert True
此外,在test_module_skip_2.py中引入minversion,代码如下:
from test_module_skip_1 import minversion
@minversion
def test_two():
assert True
现在,我们来执行这两个用例(当前的Python版本为3.6.8),执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module> pytest -rs -k "module" .
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 2 items
test_module_skip_1.py s [ 50%]
test_module_skip_2.py s [100%]
======================================================================================================= short test summary info =======================================================================================================
SKIPPED [1] test_module\test_module_skip_1.py:6: 请使用Python3.8或者更高的版本
SKIPPED [1] test_module\test_module_skip_2.py:3: 请使用Python3.8或者更高的版本
========================================================================================================= 2 skipped in 0.02s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module>
可以看到,minversion在两个测试模块中都生效了。
因此,在大型的测试项目中,可以在一个文件中定义所有的执行条件,需要时再引入模块中。
注意:不存在pytest.skipif()的方法。
pytest.importorskip方法
当引入某个模块失败或者引入的版本不符合时,我们同样可以跳过后续部分的执行。
- 引入失败时跳过
代码如下:
import pytest
docutils = pytest.importorskip("docutils")
@docutils
def test_importorskip():
pass
def test_seven():
print("7")
执行结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_importorskip.py
Testing started at 14:04 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_importorskip.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ...
Skipped: could not import 'docutils': No module named 'docutils'
collected 0 items / 1 skipped
============================= 1 skipped in 0.03s ==============================
Process finished with exit code 5
- 引入模块版本不符合时跳过
我们也可以为其指定一个最低满足要求的版本,判断的依据是检查引入模块的__version__属性,代码如下:
import pytest
csv1 = pytest.importorskip("csv", minversion="2.0")
import csv
@csv1
def test_import_minversion():
pass
执行结果如下,csv的版本是1.0,要获得的最小版本是2.0,所以跳过而未导入。
执行结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_import_minversion.py
Testing started at 14:10 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_import_minversion.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ...
Skipped: module 'csv' has __version__ '1.0', required is: '2.0'
collected 0 items / 1 skipped
============================= 1 skipped in 0.07s ==============================
Process finished with exit code 5
我们还可以再为其指定一个reason参数,表明跳过的原因。
我们注意到,pytest.importorskip和pytest.skip(allow_module_level=True)都可以在模块的引入阶段跳过剩余部分。实际上,在源码中它们抛出的都是同样的异常,代码如下:
只是importorskip额外增加了minversion参数,代码如下:
从代码中也证实了,它实际检查的是模块的__version__属性。
- 使用pytest.skip()实现导入不成功时跳过
对于一般场景下,使用下面的方法可以实现同样的效果:
跳过测试类
跳过的层次也是对应的:跳过函数、跳过方法、跳过模块、跳过类。那么在类上如何应用@pytest.mark.skip或@pytest.mark.skipif呢?
代码如下:
import pytest
@pytest.mark.skip("作用于类中的每个用例,所以pytest共收集到两个SKIPPED的用例。")
class TestMyClass():
def test_one(self):
assert True
def test_two(self):
assert True
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_skip_class.py
Testing started at 14:13 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_skip_class.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ... collected 2 items
test_skip_class.py::TestMyClass::test_one
test_skip_class.py::TestMyClass::test_two
============================= 2 skipped in 0.07s ==============================
Process finished with exit code 0
SKIPPED [ 50%]
Skipped: 作用于类中的每个用例,所以pytest共收集到两个SKIPPED的用例。
SKIPPED [100%]
Skipped: 作用于类中的每个用例,所以pytest共收集到两个SKIPPED的用例。
跳过测试模块
在模块中定义pytestmark变量的方法以便跳过测试模块。
代码如下:
import pytest
pytestmark = pytest.mark.skip('作用于模块中的每个用例,所以pytest共收集到两个SKIPPED的用例。')
def test_one():
assert True
def test_two():
assert True
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module_skip_3.py
Testing started at 14:16 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module_skip_3.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ... collected 2 items
test_module_skip_3.py::test_one SKIPPED [ 50%]
Skipped: 作用于模块中的每个用例,所以pytest共收集到两个SKIPPED的用例。
test_module_skip_3.py::test_two SKIPPED [100%]
Skipped: 作用于模块中的每个用例,所以pytest共收集到两个SKIPPED的用例。
============================= 2 skipped in 0.09s ==============================
Process finished with exit code 0
或者,在模块中调用pytest.skip方法,并设置allow_module_level=True。
代码如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module_skip_3.py
Testing started at 14:17 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_module_skip_3.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ...
Skipped: 在用例收集极端就已经跳出了,所以不会收集到任何用例。
collected 0 items / 1 skipped
============================= 1 skipped in 0.08s ==============================
Process finished with exit code 5
跳过指定文件或目录
通过在conftest.py中配置collect_ignore_glob项,可以在用例的收集阶段跳过指定的文件和目录。
例如:在chapter-2文件夹下执行,跳过test_module测试目录中文件名匹配test_*.py规则的文件和以test_assert开头的测试用例的收集,也就是不执行,代码如下:
collect_ignore_glob = ['test_module/test_*.py', 'test_assert*.py']
执行结果如下,未执行test_module文件下的文件,也未执行以test_assert开头的文件。
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 15 items / 3 skipped / 12 selected
test_Windows_skip.py sF. [ 20%]
test_fail_cus.py F [ 26%]
test_invoke_via_main.py . [ 33%]
test_markers.py ... [ 53%]
test_nodeid.py ...F [ 80%]
test_raise.py F [ 86%]
test_skip_class.py ss [100%]
============================================================================================================== FAILURES ===============================================================================================================
_____________________________________________________________________________________________________________ test_cakan ______________________________________________________________________________________________________________
def test_cakan():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
test_Windows_skip.py:13: AssertionError
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
win32
__________________________________________________________________________________________________________ test_foo_compare ___________________________________________________________________________________________________________
def test_foo_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert 1 == 2
test_fail_cus.py:12: AssertionError
______________________________________________________________________________________________________ TestNodeId.test_two[3-4] _______________________________________________________________________________________________________
self = <test_nodeid.TestNodeId object at 0x0000020B3B573B88>, x = 3, y = 4
@pytest.mark.parametrize('x,y', [(1,1),(3,4)])
def test_two(self, x, y):
print(f'TestNodeId::test_two::{x} == {y}')
> assert x == y
E assert 3 == 4
test_nodeid.py:14: AssertionError
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
TestNodeId::test_two::3 == 4
__________________________________________________________________________________________________________ test_mytest_error __________________________________________________________________________________________________________
def test_mytest_error():
with pytest.raises(ImportError):
print("如果不是解释器请求退出,则这个原因引起的异常会被测试认定为不通过")
> f()
test_raise.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def f():
> raise SystemExit(1)
E SystemExit: 1
test_raise.py:4: SystemExit
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
如果不是解释器请求退出,则这个原因引起的异常会被测试认定为不通过
========================================================================================================== warnings summary ===========================================================================================================
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289: PytestDeprecationWarning: TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.
See https://docs.pytest.org/en/latest/deprecations.html#terminalreporter-writer for more information.
"TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
-- Docs: https://docs.pytest.org/en/latest/warnings.html
======================================================================================================= short test summary info =======================================================================================================
FAILED test_Windows_skip.py::test_cakan - assert 3 == 4
FAILED test_fail_cus.py::test_foo_compare - assert 1 == 2
FAILED test_nodeid.py::TestNodeId::test_two[3-4] - assert 3 == 4
FAILED test_raise.py::test_mytest_error - SystemExit: 1
========================================================================================= 4 failed, 8 passed, 6 skipped, 1 warning in 20.36s ==========================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
如果注释本设置,则执行chapter-2下的全部用例,执行统计结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 42 items / 3 skipped / 39 selected
test_Windows_skip.py sF. [ 7%]
test_assert_1.py FFFF [ 16%]
test_assert_2.py FFFF [ 26%]
test_assert_3.py .FF.FF [ 40%]
test_assert_except.py F [ 42%]
test_assert_sample.py ...FFF. [ 59%]
test_assert_skip_1.py s [ 61%]
test_assert_skip_2.py ss [ 66%]
test_fail_cus.py F [ 69%]
test_invoke_via_main.py . [ 71%]
test_markers.py ... [ 78%]
test_nodeid.py ...F [ 88%]
test_raise.py F [ 90%]
test_skip_class.py ss [ 95%]
test_module\test_module_skip_1.py s [ 97%]
test_module\test_module_skip_2.py s [100%]
============================================================================================================== FAILURES ===============================================================================================================
_____________________________________________________________________________________________________________ test_cakan ______________________________________________________________________________________________________________
def test_cakan():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
test_Windows_skip.py:13: AssertionError
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
win32
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_1.py:4: AssertionError
____________________________________________________________________________________________________________ test_eq_list _____________________________________________________________________________________________________________
def test_eq_list():
> assert [0,1,2] == [0,1,3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
test_assert_1.py:7: AssertionError
________________________________________________________________________________________________________ test_dict_comparison _________________________________________________________________________________________________________
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
> assert dict1 == dict2
E AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'age': 18} != {'age': 88}
E Use -v to get the full diff
test_assert_1.py:18: AssertionError
_________________________________________________________________________________________________________ test_set_comparison _________________________________________________________________________________________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get the full diff
test_assert_1.py:23: AssertionError
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_2.py:5: AssertionError
____________________________________________________________________________________________________________ test_eq_list _____________________________________________________________________________________________________________
def test_eq_list():
> assert [0,1,2] == [0,1,3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
test_assert_2.py:8: AssertionError
________________________________________________________________________________________________________ test_dict_comparison _________________________________________________________________________________________________________
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
> assert dict1 == dict2
E AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'age': 18} != {'age': 88}
E Use -v to get the full diff
test_assert_2.py:19: AssertionError
_________________________________________________________________________________________________________ test_set_comparison _________________________________________________________________________________________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get the full diff
test_assert_2.py:24: AssertionError
______________________________________________________________________________________________________________ test_true ______________________________________________________________________________________________________________
def test_true():
assert (1<3) is True
> assert (5>3) is False
E assert (5 > 3) is False
test_assert_3.py:7: AssertionError
____________________________________________________________________________________________________________ test_in_dict _____________________________________________________________________________________________________________
def test_in_dict():
x = 'linda1'
> assert x in "I'm linda"
E assert 'linda1' in "I'm linda"
test_assert_3.py:11: AssertionError
___________________________________________________________________________________________________________ test_long_list ____________________________________________________________________________________________________________
def test_long_list():
x = [str(i) for i in range(100)]
y = [str(i) for i in range(0, 100, 2)]
> assert x == y
E AssertionError: assert ['0', '1', '2...'4', '5', ...] == ['0', '2', '4...8', '10', ...]
E At index 1 diff: '1' != '2'
E Left contains 50 more items, first extra item: '50'
E Use -v to get the full diff
test_assert_3.py:20: AssertionError
______________________________________________________________________________________________________________ test_long ______________________________________________________________________________________________________________
def test_long():
> assert [12]*50 == [11]*50
E assert [12, 12, 12, 12, 12, 12, ...] == [11, 11, 11, 11, 11, 11, ...]
E At index 0 diff: 12 != 11
E Use -v to get the full diff
test_assert_3.py:23: AssertionError
_____________________________________________________________________________________________________________ test_match ______________________________________________________________________________________________________________
def test_match():
with pytest.raises((ValueError, RuntimeError),match=r'.*40011.*'):
> myfunc()
test_assert_except.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def myfunc():
> raise ValueError("返回40013支付错误")
E ValueError: 返回40013支付错误
test_assert_except.py:4: ValueError
During handling of the above exception, another exception occurred:
def test_match():
with pytest.raises((ValueError, RuntimeError),match=r'.*40011.*'):
> myfunc()
E AssertionError: Pattern '.*40011.*' does not match '返回40013支付错误'
test_assert_except.py:8: AssertionError
_______________________________________________________________________________________________________ test_approx_simple_fail _______________________________________________________________________________________________________
def test_approx_simple_fail():
> assert 0.1 + 0.2 == approx(0.35)
E assert (0.1 + 0.2) == 0.35 ± 3.5e-07
E + where 0.35 ± 3.5e-07 = approx(0.35)
test_assert_sample.py:18: AssertionError
____________________________________________________________________________________________________ test_warrior_long_description ____________________________________________________________________________________________________
def test_warrior_long_description():
desc = get_long_class_description("warrior")
> assert (
desc
== textwrap.dedent(
"""\
A seasoned veteran of many battles. Strength and Dexterity
allow to yield heavy armor and weapons, as well as carry
more equipment. Weak in magic.
"""
)
)
E AssertionError: assert 'A seasoned v...k in magic.\n' == 'A seasoned v...k in magic.\n'
E - A seasoned veteran of many battles. Strength and Dexterity
E + A seasoned veteran of many battles. High Strength and Dexterity
E ? +++++
E allow to yield heavy armor and weapons, as well as carry
E - more equipment. Weak in magic.
E + more equipment while keeping a light roll. Weak in magic.
test_assert_sample.py:32: AssertionError
_____________________________________________________________________________________________________ test_get_starting_equiment ______________________________________________________________________________________________________
def test_get_starting_equiment():
expected = ["长剑", "战士装备"]
> assert get_starting_equipment("战士") == expected, "装备不符"
E AssertionError: 装备不符
E assert ['长剑', '战士装备', '盾'] == ['长剑', '战士装备']
E Left contains one more item: '盾'
E Use -v to get the full diff
test_assert_sample.py:49: AssertionError
__________________________________________________________________________________________________________ test_foo_compare ___________________________________________________________________________________________________________
def test_foo_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert 1 == 2
test_fail_cus.py:12: AssertionError
______________________________________________________________________________________________________ TestNodeId.test_two[3-4] _______________________________________________________________________________________________________
self = <test_nodeid.TestNodeId object at 0x000002707CDE3548>, x = 3, y = 4
@pytest.mark.parametrize('x,y', [(1,1),(3,4)])
def test_two(self, x, y):
print(f'TestNodeId::test_two::{x} == {y}')
> assert x == y
E assert 3 == 4
test_nodeid.py:14: AssertionError
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
TestNodeId::test_two::3 == 4
__________________________________________________________________________________________________________ test_mytest_error __________________________________________________________________________________________________________
def test_mytest_error():
with pytest.raises(ImportError):
print("如果不是解释器请求退出,则这个原因引起的异常会被测试认定为不通过")
> f()
test_raise.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def f():
> raise SystemExit(1)
E SystemExit: 1
test_raise.py:4: SystemExit
-------------------------------------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------------------------------------
如果不是解释器请求退出,则这个原因引起的异常会被测试认定为不通过
========================================================================================================== warnings summary ===========================================================================================================
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289: PytestDeprecationWarning: TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.
See https://docs.pytest.org/en/latest/deprecations.html#terminalreporter-writer for more information.
"TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
-- Docs: https://docs.pytest.org/en/latest/warnings.html
======================================================================================================= short test summary info =======================================================================================================
FAILED test_Windows_skip.py::test_cakan - assert 3 == 4
FAILED test_assert_1.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_1.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_1.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_1.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
FAILED test_assert_2.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_2.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_2.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_2.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
FAILED test_assert_3.py::test_true - assert (5 > 3) is False
FAILED test_assert_3.py::test_in_dict - assert 'linda1' in "I'm linda"
FAILED test_assert_3.py::test_long_list - AssertionError: assert ['0', '1', '2...'4', '5', ...] == ['0', '2', '4...8', '10', ...]
FAILED test_assert_3.py::test_long - assert [12, 12, 12, 12, 12, 12, ...] == [11, 11, 11, 11, 11, 11, ...]
FAILED test_assert_except.py::test_match - AssertionError: Pattern '.*40011.*' does not match '返回40013支付错误'
FAILED test_assert_sample.py::test_approx_simple_fail - assert (0.1 + 0.2) == 0.35 ± 3.5e-07
FAILED test_assert_sample.py::test_warrior_long_description - AssertionError: assert 'A seasoned v...k in magic.\n' == 'A seasoned v...k in magic.\n'
FAILED test_assert_sample.py::test_get_starting_equiment - AssertionError: 装备不符
FAILED test_fail_cus.py::test_foo_compare - assert 1 == 2
FAILED test_nodeid.py::TestNodeId::test_two[3-4] - assert 3 == 4
FAILED test_raise.py::test_mytest_error - SystemExit: 1
======================================================================================== 20 failed, 14 passed, 11 skipped, 1 warning in 20.79s ========================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
各种跳过小结
通过小结看到不同跳过的使用层次,如表2-1所示。
表2-1 各种跳过小结