Pytest介绍
Pytest
是一款非常成熟的全功能的python单元测试框架,支持自动检索用例、参数化、跳过、标记预期等功能,且可以通过安装插件进行功能的扩展,与python自带的unittest
单元测试框架类似,但比unittest
框架使用起来更简洁,功能更强大。
Pytest
是一款命令行工具,可以自动找到测试用例执行,并且汇报测试结果。它有丰富的基础库,可以大幅提高用户编写测试用例的效率。它具备可扩展性,用户可以自己编写插件,或者安装第三方提供的插件(Pytest
支持1170种以上的插件)。Pytest
可以直接测试各类Python程序,也可以很容易地与其他工具集成到一起使用,比如持续集成、自动化测试等。同时Pytest
是一个非常成熟的单元测试框架,灵活、简单。可以通过结合自动化测试框架Requests
、Selenium
和Appium
来完成不同类型的自动化测试。
利用pytest
单元测试框架的具体使用步骤:
- 用例设计:按照
pytest
测试用例设计规则进行用例设计; - 测试发现:从多个文件里查找到符合条件的测试用例;
- 测试执行:按照一定的顺序和规则去执行,生成结果;
- 测试判断:通过断言判断预期结果和实际结果是否一致;
- 测试报告:统计测试进度、耗时、通过率等,生成测试报告;
Pytest安装
Pytest
的官方文档:https://docs.pytest.org/en/7.2.x/
在已经安装了python3.6以上版本的基础上,通过下面2种方式都可以进行安装:
通过命令行
pip install pytest
通过Pycharm开发工具
验证安装
pytest --version
pytest 7.2.0
编写用例
pytest在执行用例时,会自动递归遍历执行目录下的所有目录,根据pytest默认的用例收集规则,自动装载用例。
用例规范
- 用例文件,所有文件名为
test_
开头或者_test
结尾的文件会被识别为用例文件; - 用例类,测试文件中每个以
Test
开头的类就是一个测试用例类; - 用例方法/函数,每个以
test_
开头的方法/函数就是一条测试用例;
注:测试类中不可以添加__init__
构造函数
上面是设计pytest
测试用例的规则,也是执行用例的时默认的收集用例规则,可以在配置文件中进行设定修改。
函数形式编写用例
用例函数名以test_
开头
def test_demo():
assert 3 == 3
类形式编写用例
测试类命名以Test
开头,用例方法以test_
开头
class TestDome:
def test_demo1(self):
assert 3 == 3
def test_demo(self):
assert 10 == 20
执行测试用例
执行用例有两种方式,命令行及IDE执行(这里的IDE指的是Pycharm)。绝大多数执行测试用例都是用命令行方式,包括集成到CI/CD中都是以命令行方式调用,而IDE形式只用于调试。
测试用例的执行顺序:
- Pycharm中通过运行方式执行的用例,按照
pytest
默认的规则:按ASCII的顺序进行执行; - 通过命令行方式执行的用例,是按照用例编写的顺序,即从上到下的顺序进行执行;
测试用例准备
根据pytest的规范创建3个pytest格式的测试用例文件,如下:

test_01_demo.py
import pytest
# 函数func
def func(x):
return x + 1
# 测试函数test_a
def test_a():
print("____test-a____")
# 断言失败
assert func(3) == 5
# 测试函数test_b
def test_b():
print("____test-b____")
# 断言成功
assert func(4) == 5
if __name__ == '__main__':
pytest.main(["test_01_demo.py"])
test_02_demo.py
import pytest
class TestFunc:
def test_a(self):
print("test_a")
def test_b(self):
print("test_b")
if __name__ == '__main__':
pytest.main(["test_02_demo.py"])
test_03_demo.py
import pytest
class TestClass:
def test_a(self):
print("test_a")
def test_b(self):
print("test_b")
if __name__ == '__main__':
pytest.main(["test_03_demo.py"])
在Pycharm中执行用例
需要将Pycharm的默认单元测试框架设置为pytest。在
Preferences
-Tools
-Python Integrated Tools
-Testing
的default test runner
设置为pytest
,保存。在某个用例处执行用例,即执行当前光标所在位置的用例
Launching pytest with arguments test_01_demo.py::test_b --no-header --no-summary -q in /Users/laobai/workspaces/testdemo01/pytest_demo ============================= test session starts ============================== collecting ... collected 1 item test_01_demo.py::test_b PASSED [100%]____test-b____ ============================== 1 passed in 0.01s =============================== Process finished with exit code 0
在main函数或空白处执行用例,执行当前模块中的全部用例
Launching pytest with arguments /Users/laobai/workspaces/testdemo01/pytest_demo/test_01_demo.py --no-header --no-summary -q in /Users/laobai/workspaces/testdemo01/pytest_demo ============================= test session starts ============================== collecting ... collected 2 items test_01_demo.py::test_a FAILED [ 50%]____test-a____ test_01_demo.py:13 (test_a) 4 != 5 Expected :5 Actual :4 <Click to see difference> def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError test_01_demo.py::test_b PASSED [100%]____test-b____ ========================= 1 failed, 1 passed in 0.05s ========================== Process finished with exit code 1
命令行模式执行用例
确认用例文件的位置:
$ pwd
/Users/laobai/workspaces/testdemo01/pytest_demo
$ ll
total 24
drwxr-xr-x 7 laobai staff 224 Dec 7 11:53 ./
drwxr-xr-x 7 laobai staff 224 Dec 7 10:33 ../
drwxr-xr-x 6 laobai staff 192 Dec 7 10:38 .pytest_cache/
drwxr-xr-x 5 laobai staff 160 Dec 7 11:53 __pycache__/
-rw-r--r-- 1 laobai staff 430 Dec 7 11:07 test_01_demo.py
-rw-r--r-- 1 laobai staff 281 Dec 7 11:07 test_02_demo.py
-rw-r--r-- 1 laobai staff 282 Dec 7 11:07 test_03_demo.py
在用例所在目录,直接执行
pytest
命令,即可执行当前目录下的所有测试用例$ pytest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
简单描述一下执行结果:
collected 6 items
6个测试用例test_01_demo.py F.
F
是测试失败,.
是成功short test summary info
测试结果摘要,列出出错的文件及方法,统计失败数和成功数及耗费时间在
pytest
增加-s
参数,输出print()
函数的信息$ pytest -s ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py ____test-a____ F____test-b____ . test_02_demo.py test_a .test_b . test_03_demo.py test_a .test_b . ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
在
pytest
增加-v
参数,输出用例执行的详细过程$ pytest -v ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py::test_a FAILED [ 16%] test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_a PASSED [ 50%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_a PASSED [ 83%] test_03_demo.py::TestClass::test_b PASSED [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
在
pytest
增加-v
参数,精简输出用例执行的过程$ pytest -q F..... [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 1 failed, 5 passed in 0.04s
指定要执行的目录下的所有用例
$ pytest pytest_demo/ ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01 collected 6 items pytest_demo/test_01_demo.py F. [ 33%] pytest_demo/test_02_demo.py .. [ 66%] pytest_demo/test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) pytest_demo/test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED pytest_demo/test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
指定要执行的某个文件(模块)
$ pytest test_02_demo.py ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 2 items test_02_demo.py .. [100%] ======================================================================== 2 passed in 0.01s ========================================================================
指定要执行某个文件中的某个类
$ pytest test_02_demo.py::TestFunc ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 2 items test_02_demo.py .. [100%] ======================================================================== 2 passed in 0.01s ========================================================================
指定要执行某个文件某个类的某个方法
$ pytest test_02_demo.py::TestFunc::test_b ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 1 item test_02_demo.py . [100%] ======================================================================== 1 passed in 0.01s ========================================================================
通过参数
-k
过滤用例,按关键字查找执行,在文件名、类名以及方法名中查找关键字k
$ pytest -k "test_b" -v ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items / 3 deselected / 3 selected test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_b PASSED [100%] ================================================================= 3 passed, 3 deselected in 0.01s =================================================================
$ pytest -k "not test_a" -v ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items / 3 deselected / 3 selected test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_b PASSED [100%] ================================================================= 3 passed, 3 deselected in 0.01s =================================================================
$ pytest -k "test_a or test_b" -v ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py::test_a FAILED [ 16%] test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_a PASSED [ 50%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_b PASSED [ 83%] test_03_demo.py::TestClass::test_a PASSED [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
执行测试时,增加
-x
参数,-x
:用例运行失败则立即停止执行$ pytest -x ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ======================================================================== 1 failed in 0.04s ========================================================================
执行用例时增加
--maxfail=num
参数,--maxfail
:是最大失败次数,当批量执行用例时,失败次数大于等于指定的数值时,执行会立即停止。$ pytest --maxfail=1 ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ======================================================================== 1 failed in 0.04s ========================================================================
$ pytest --maxfail=2 ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
执行用例时增加
--lf
参数,--lf
(–last-failed):执行上次失败的测试用例$ pytest --lf ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 1 item run-last-failure: rerun previous 1 failure (skipped 2 files) test_01_demo.py F [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 ======================================================================== 1 failed in 0.04s ========================================================================
执行用例时增加
--ff
参数,--ff
(–failed-first):先执行上次失败的测试用例再运行其他用例$ pytest -v test_01_demo.py --ff ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 3 items run-last-failure: rerun previous 1 failure first test_01_demo.py::test_a FAILED [ 33%] test_01_demo.py::test_0 PASSED [ 66%] test_01_demo.py::test_b PASSED [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): # time.sleep(2) print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:25: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 2 passed in 0.05s ===================================================================
执行用例时增加
--tb
参数,--tb
:输出信息的显示模式(auto/long/short/line/native/no)--tb=no
不显示用例失败的错误详情$ pytest --tb=no ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.02s ===================================================================
--tb=short
只显示assert
断言的错误信息$ pytest --tb=short ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ test_01_demo.py:17: in test_a assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
--tb=line
显示用例失败的代码行数$ pytest --tb=line ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= /Users/laobai/workspaces/testdemo01/pytest_demo/test_01_demo.py:17: assert 4 == 5 ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.02s ===================================================================
执行用例时增加
--collect-only
参数,--collect-only
:仅收集可执行的用例,但不执行$ pytest --collect-only ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items <Module test_01_demo.py> <Function test_a> <Function test_b> <Module test_02_demo.py> <Class TestFunc> <Function test_a> <Function test_b> <Module test_03_demo.py> <Class TestClass> <Function test_b> <Function test_a> =================================================================== 6 tests collected in 0.01s ====================================================================
执行用例时增加
-p no:warnings
参数,-p no:warnings
:过滤警告信息$ pytest -p no:warnings ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
执行用例时增加
--durations=num -vv
参数,num=0时则是按执行时间倒序排列所有执行用例,num为具体数值n时显示耗时最长的n个用例。若用例执行速度极快,会在统计是显示成0,-vv 显示执行时间为0的用例$ pytest --durations=0 -vv ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py::test_a FAILED [ 16%] test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_a PASSED [ 50%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_b PASSED [ 83%] test_03_demo.py::TestClass::test_a PASSED [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ======================================================================== slowest durations ======================================================================== 0.00s call test_01_demo.py::test_a 0.00s setup test_01_demo.py::test_a 0.00s call test_01_demo.py::test_b 0.00s teardown test_01_demo.py::test_a 0.00s setup test_02_demo.py::TestFunc::test_a 0.00s call test_02_demo.py::TestFunc::test_a 0.00s teardown test_01_demo.py::test_b 0.00s call test_02_demo.py::TestFunc::test_b 0.00s call test_03_demo.py::TestClass::test_b 0.00s call test_03_demo.py::TestClass::test_a 0.00s setup test_01_demo.py::test_b 0.00s setup test_03_demo.py::TestClass::test_b 0.00s setup test_02_demo.py::TestFunc::test_b 0.00s setup test_03_demo.py::TestClass::test_a 0.00s teardown test_03_demo.py::TestClass::test_b 0.00s teardown test_02_demo.py::TestFunc::test_a 0.00s teardown test_03_demo.py::TestClass::test_a 0.00s teardown test_02_demo.py::TestFunc::test_b ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
$ pytest --durations=3 -vv ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo collected 6 items test_01_demo.py::test_a FAILED [ 16%] test_01_demo.py::test_b PASSED [ 33%] test_02_demo.py::TestFunc::test_a PASSED [ 50%] test_02_demo.py::TestFunc::test_b PASSED [ 66%] test_03_demo.py::TestClass::test_b PASSED [ 83%] test_03_demo.py::TestClass::test_a PASSED [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ======================================================================= slowest 3 durations ======================================================================= 0.00s call test_01_demo.py::test_a 0.00s setup test_01_demo.py::test_a 0.00s call test_02_demo.py::TestFunc::test_b ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
Mark标记
在执行测试用例时,经常会遇到,要对某个功能的执行测试用例,对某个测试环境的执行测试用例,这就要求在书写设计用例时要做好分类,而pytest
提供了mark
功能,可以通过装饰器的形式给用例打标签,可以给一个用例打多个标签。打标签的对象,可以是测试用例,测试类以及模块文件。
mark标签
语法格式:@pytest.mark.标签名
执行用例时,增加-m
参数,后面跟上标签名
新建一个测试用例文件,test_05_demo.py,共4个测试用例,标记webtest
2条,iostest
1条,androidtest
1条。
import pytest
class TestClass:
@pytest.mark.webtest
def test_a(self):
print("test_a")
@pytest.mark.iostest
def test_b(self):
print("test_b")
@pytest.mark.webtest
def test_c(self):
print("test_c")
@pytest.mark.androidtest
def test_d(self):
print("test_d")
@pytest.mark.iostest
@pytest.mark.androidtest
def test_e(self):
print("test_e")
if __name__ == '__main__':
pytest.main(["test_05_demo.py"])
按区分的标记执行用例
$ pytest -v -m webtest
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 15 items / 13 deselected / 2 selected
test_05_demo.py::TestClass::test_a PASSED [ 50%]
test_05_demo.py::TestClass::test_c PASSED [100%]
======================================================================== warnings summary =========================================================================
test_05_demo.py:10
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:14
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:18
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:22
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
test_05_demo.py:26
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:27
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================== 2 passed, 13 deselected, 6 warnings in 0.03s ===========================================================
$ pytest -v -m 'not webtest' test_05_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e PASSED [100%]
======================================================================== warnings summary =========================================================================
test_05_demo.py:10
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:14
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:18
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:22
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
test_05_demo.py:26
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:27
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================================================== 3 passed, 2 deselected, 6 warnings in 0.02s ===========================================================
$ pytest -v -m=iostest test_05_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 3 deselected / 2 selected
test_05_demo.py::TestClass::test_b PASSED [ 50%]
test_05_demo.py::TestClass::test_e PASSED [100%]
======================================================================== warnings summary =========================================================================
test_05_demo.py:10
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:14
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:18
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.webtest
test_05_demo.py:22
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
test_05_demo.py:26
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.iostest
test_05_demo.py:27
/Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.androidtest
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================================================== 2 passed, 3 deselected, 6 warnings in 0.02s ===========================================================
上面执行用例时有大量的warnings信息,可以使用之前介绍的参数-p no:warnings
过滤掉warnings信息
$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e PASSED [100%]
================================================================= 3 passed, 2 deselected in 0.02s =================================================================
注:命令中包含了较多参数,是可以配置到pytest.ini
中的,这个会在后面的内容进行介绍。
当标签数量比较多,而且又会发生标签名改动的问题,可以将所有的标签抽象到一个py文件中进行统一管理。
上面的例子中,可以新建一个mark.py文件如下:
import pytest
webtest = pytest.mark.webtest
iostest = pytest.mark.iostest
android = pytest.mark.androidtest
这样测试用例文件就可以改写为如下:
from mark import *
class TestClass:
@webtest
def test_a(self):
print("test_a")
@iostest
def test_b(self):
print("test_b")
@webtest
def test_c(self):
print("test_c")
@androidtest
def test_d(self):
print("test_d")
@iostest
@androidtest
def test_e(self):
print("test_e")
if __name__ == '__main__':
pytest.main(["test_05_demo.py"])
执行测试:
$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e PASSED [100%]
================================================================= 3 passed, 2 deselected in 0.03s =================================================================
通常情况下,打标签有3类:区分环境(测试、生产)、用例作者、特定用途(冒烟)
mark.skip用例跳过
在自动化测试过程中,经常会遇到功能阻塞、功能未实现、环境因素等导致的用例无法正常执行,此时可以通过跳过这些故障用例来完成测试,pytest提供了skip
功能来实现。
@pytest.mark.skip()
代码片段
@iostest
@androidtest
@pytest.mark.skip()
def test_e(self):
print("test_e")
测试执行:
$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (unconditional skip) [100%]
=========================================================== 2 passed, 1 skipped, 2 deselected in 0.02s ============================================================
@pytest.mark.skip(reason=’’)
代码片段
@iostest
@androidtest
@pytest.mark.skip(reason='功能尚未完成')
def test_e(self):
print("test_e")
测试执行
$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (功能尚未完成) [100%]
=========================================================== 2 passed, 1 skipped, 2 deselected in 0.02s ============================================================
@pytest.mark.skipif(判断条件,reason=’’)
判断条件满足,跳过执行此用例
代码片段
@iostest
@androidtest
@pytest.mark.skipif(2 == 2, reason='条件判断成功,跳过此用例')
def test_e(self):
print("test_e")
测试执行
$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected
test_05_demo.py::TestClass::test_b PASSED [ 33%]
test_05_demo.py::TestClass::test_d PASSED [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (条件判断成功,跳过此用例) [100%]
=========================================================== 2 passed, 1 skipped, 2 deselected in 0.03s ============================================================
mark.flaky()
通过对用例增加@pytest.mark.flaky
装饰器,来对仅用于此装饰器修饰的用例进行重试。有两个参数,reruns=n
(n表示重试次数),reruns_delay=n
(每次重跑的等待时间,n表示等待n秒)
新建test_08_demo.py
import pytest
# 函数func
def func(x):
return x + 1
@pytest.mark.flaky(reruns=2, reruns_delay=2)
def test_a():
print("____test-a____")
# 断言失败
assert func(3) == 5
def test_b():
print("____test-b____")
# 断言成功
assert func(4) == 5
def test_c():
print("____test-c____")
# 断言成功
assert func(1) == 5
if __name__ == '__main__':
pytest.main(["test_08_demo.py"])
执行用例:
$ pytest -vs test_08_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 3 items
test_08_demo.py::test_a ____test-a____
RERUN
test_08_demo.py::test_a ____test-a____
RERUN
test_08_demo.py::test_a ____test-a____
FAILED
test_08_demo.py::test_b ____test-b____
PASSED
test_08_demo.py::test_c ____test-c____
FAILED
============================================================================ FAILURES =============================================================================
_____________________________________________________________________________ test_a ______________________________________________________________________________
@pytest.mark.flaky(reruns=2,reruns_delay=2)
def test_a():
# time.sleep(2)
print("____test-a____")
# 断言失败
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_08_demo.py:20: AssertionError
_____________________________________________________________________________ test_c ______________________________________________________________________________
def test_c():
# time.sleep(2)
print("____test-c____")
# 断言成功
> assert func(1) == 5
E assert 2 == 5
E + where 2 = func(1)
test_08_demo.py:36: AssertionError
===================================================================== short test summary info =====================================================================
FAILED test_08_demo.py::test_a - assert 4 == 5
FAILED test_08_demo.py::test_c - assert 2 == 5
============================================================== 2 failed, 1 passed, 2 rerun in 4.07s ===============================================================
第三方插件
pytest-xdist
开启多个worker进程,同时执行多个测试用例,达到并发运行的效果,大大提升构建效率。
安装
pip install pytest-xdist
模拟并发执行,在每个函数中都增加
time.sleep(2)
,每个函数都sleep2秒(否则不到1ms就都执行完了),体现出多线程执行的作用。示例演示
import pytest import time # 函数func def func(x): return x + 1 # 测试函数test_a def test_a(): time.sleep(2) print("____test-a____") # 断言失败 assert func(3) == 5 # 测试函数test_b def test_b(): time.sleep(2) print("____test-b____") # 断言成功 assert func(4) == 5
import pytest import time class TestFunc: def test_a(self): time.sleep(2) print("test_a") def test_b(self): time.sleep(2) print("test_b")
import pytest import time class TestClass: def test_b(self): time.sleep(2) print("test_b") def test_a(self): time.sleep(2) print("test_a")
首先不添加参数执行上面3个用例
$ pytest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, html-3.2.0, metadata-2.0.4 collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): time.sleep(2) print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:18: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 ================================================================== 1 failed, 5 passed in 12.11s ===================================================================
可以看到上面3个文件6个用例执行的总耗时为12.11s
增加
-n
参数再次执行,-n
后面跟着一个数字,是启动几个线程,这里使用auto
,表示根据当前cpu的核心数由pytest自动启动运行。$ pytest -n auto ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, metadata-2.0.4 gw0 [6] / gw1 [6] / gw2 [6] / gw3 [6] / gw4 [6] / gw5 [6] / gw6 [6] / gw7 [6] .....F [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ [gw0] darwin -- Python 3.9.12 /Users/laobai/workspaces/testdemo01/venv/bin/python def test_a(): time.sleep(2) print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:18: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 3.22s ===================================================================
可以看到3个文件中的6个用例耗时3.22s
pytest-ordering
通过使用装饰器@pytest.mark.run(order=n)
来自定义用例的执行顺序,其中n为用例执行的顺序,1,2,3等
安装
$ pip install pytest-ordering
示例
新建一个测试文件:test_04_demo.py
import pytest class TestClass: @pytest.mark.run(order=4) def test_b(self): print("test_b") @pytest.mark.run(order=2) def test_a(self): print("test_a") @pytest.mark.run(order=1) def test_d(self): print("test_d") @pytest.mark.run(order=3) def test_c(self): print("test_c") if __name__ == '__main__': pytest.main(["test_04_demo.py"])
执行测试:
$ pytest test_04_demo.py -v ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 4 items test_04_demo.py::TestClass::test_d PASSED [ 25%] test_04_demo.py::TestClass::test_a PASSED [ 50%] test_04_demo.py::TestClass::test_c PASSED [ 75%] test_04_demo.py::TestClass::test_b PASSED [100%] ======================================================================== 4 passed in 0.02s ========================================================================
测试结果是按照指定的顺序输出的
pytest-rerunfailures
pytest-rerunfailures
失败重试插件,当执行出错时,会进行重试。(因为不是所有执行出错都是程序问题,有可能是网络问题等导致用例失败)
安装
pip install pytest-rerunfailures
示例
在执行pytest测试时,增加参数
--reruns n
(n表示重试次数),--reruns-delay n
(每次重跑的等待时间,n表示等待n秒)$ pytest --reruns 2 ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, html-3.2.0, metadata-2.0.4 collected 6 items test_01_demo.py RRF. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 ============================================================== 1 failed, 5 passed, 2 rerun in 0.07s ===============================================================
从上面的执行结果可以看出,对于测试失败的用例,重试了2次,第3次才置为失败状态。
$ pytest --reruns 2 --reruns-delay 2 ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 6 items test_01_demo.py RRF. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): # time.sleep(2) print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:18: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 ============================================================== 1 failed, 5 passed, 2 rerun in 4.09s ===============================================================
pytest-html
生成简易的测试报告。在后续的内容会介绍功能更强大、使用更广泛的allure
测试报告插件
安装
pip install pytest-html
示例
$ pytest --html=report.html ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo plugins: html-3.2.0, metadata-2.0.4 collected 6 items test_01_demo.py F. [ 33%] test_02_demo.py .. [ 66%] test_03_demo.py .. [100%] ============================================================================ FAILURES ============================================================================= _____________________________________________________________________________ test_a ______________________________________________________________________________ def test_a(): print("____test-a____") # 断言失败 > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_01_demo.py:17: AssertionError ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------- ____test-a____ ------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/pytest_demo/report.html ------------------------------------- ===================================================================== short test summary info ===================================================================== FAILED test_01_demo.py::test_a - assert 4 == 5 =================================================================== 1 failed, 5 passed in 0.07s ===================================================================
打开生成的report.html如下图:
断言
测试的主要工作目标就是验证实际结果与预期结果是否一致。通常情况下,自动化测试都是通过断言来实现这一目标。
简单理解,断言相当于功能测试用例的预期结果,没写预期结果,或者预期结果写的不准确,那么这条用例是无效的,自动化测试用例也是一样的。
pytest使用assert
关键字来进行断言。assert
关键字后面可以接一个表达式,表达式的结果为True,那么断言通过,即测试用例执行成功,否则用例执行失败。
assert语句
pytest的assert
常用的断言方法有如下几种:
- 判断方法或函数的返回值是否为真:
assert xx
:判断xx结果为真assert not xx
: 判断xx结果为假
- 判断包含或不包含:
assert a in b
: 判断b包含aassert a not in b
: 判断b不包含a
- 判断是否相等:
assert a == b
:判断a等于bassert a != b
:判断a不等于b
- 判断类型是否符合:
assert isinstance(a ,int)
:判断a是否是int类型数据
import pytest
def test_case1():
x = 1
assert x
def test_case2():
x = 0
assert not x
def test_case3():
x = 'python'
assert 'o' in x
def test_case4():
x = 'python'
assert 'i' not in x
def test_case5():
x = 10
assert x == 10
def test_case6():
x = 10
assert x != 1
def test_case7():
x = 100
assert isinstance(x, int)
if __name__ == '__main__':
pytest.main()
执行结果:
$ pytest -vs test_06_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 7 items
test_06_demo.py::test_case1 PASSED
test_06_demo.py::test_case2 PASSED
test_06_demo.py::test_case3 PASSED
test_06_demo.py::test_case4 PASSED
test_06_demo.py::test_case5 PASSED
test_06_demo.py::test_case6 PASSED
test_06_demo.py::test_case7 PASSED
======================================================================== 7 passed in 0.02s ========================================================================
pytest.raises
在断言一些代码块或者函数时会引发意料之中的异常或者其他失败的异常,导致程序无法运行时,使用 raises 捕获匹配到的异常,可以继续让代码正常运行。
raises()
方法主要有两个参数:
expected_exception
:预期的异常,可以是单个异常,也可以是多个异常,多个异常需要组装成元组;
match
:关键字或正则表达式匹配异常,预期异常信息和实际异常信息一致,可省略
示例1
import pytest
def test_case1():
with pytest.raises(expected_exception=ZeroDivisionError, match='division by zero'):
1 / 0
代码1 / 0
已经预知程序会报ZeroDivisionError
类型的异常,异常信息为division by zero
。
执行代码:
$ pytest -vs test_07_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 1 item
test_07_demo.py::test_case1 PASSED
======================================================================== 1 passed in 0.01s ========================================================================
PASSED
表示,我程序执行报错情况与预期的报错情况一致,即运行结果正确。
示例2
import pytest
def test_case1():
with pytest.raises((ZeroDivisionError, ValueError)) as excinfo:
1 / 0
assert excinfo.type is ZeroDivisionError
assert 'division by zero' in str(excinfo.value)
执行结果:
$ pytest -vs test_07_demo.py
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 1 item
test_07_demo.py::test_case1 PASSED
======================================================================== 1 passed in 0.01s ========================================================================
pytest.ini
pytest.ini
是pytest单元测试框架的核心配置文件,一般存放在项目的根目录。这个文件的作用是通过读取配置信息,从而改变pytest默认的行为,按指定的方式来运行。
pytest.ini
不能使用任何中文符号。
pytest.ini
都能做什么:
修改用例的命名规则
配置日志格式
添加标签,防止运行过程报警告错误
执行执行用例的目录
排除搜索目录
在项目的根目录新建一个文件:
pytest.ini
输入如下内容:
[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_*
[pytest]
标识当前ini
文件为pytest
的配置文件;pytest默认是执行所有以
test_
开头的文件,这里在pytest.ini
在保留test_*
开头的同时增加了pls_*
开头的文件;pytest默认是执行所有以
Test
开头的类,这里在pytest.ini
在保留Test*
开头的同时增加了PLS*
开头的类;pytest默认是执行所有以
test_
开头的方法/函数,这里在pytest.ini
在保留test_*
开头的同时增加了pls_*
开头的方法/函数;新建一个测试用例文件pls_01_demo.py
import pytest class PLSClass: def pls_a(self): print("pls_a") def pls_b(self): print("pls_b") if __name__ == '__main__': pytest.main(["pls_01_demo.py"])
执行用例
$ pytest pls_01_demo.py -vs ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 2 items pls_01_demo.py::PLSClass::pls_a pls_a PASSED pls_01_demo.py::PLSClass::pls_b pls_b PASSED ======================================================================== 2 passed in 0.01s ========================================================================
在项目根目录下新建一个文件夹testcases,将test_06_demo.py和pls_01_demo.py两个文件复制到testcases目录下。在命令行中切换到testcases执行测试用例
$ pytest -vs ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 9 items pls_01_demo.py::PLSClass::pls_a pls_a PASSED pls_01_demo.py::PLSClass::pls_b pls_b PASSED test_06_demo.py::test_case1 PASSED test_06_demo.py::test_case2 PASSED test_06_demo.py::test_case3 PASSED test_06_demo.py::test_case4 PASSED test_06_demo.py::test_case5 PASSED test_06_demo.py::test_case6 PASSED test_06_demo.py::test_case7 PASSED ======================================================================== 9 passed in 0.04s ========================================================================
全部9条用例均被执行,配置文件的内容有效。
在
pytest.ini
中增加addopts
addopts是为pytest命令增加默认的参数,它的值是在命令行执行测试用例中添加参数,命令行中可以添加多个参数,
addopts`后面的值就可以有多少个,用空格分隔修改
pytest.ini
如下:[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html
执行用例:
$ pytest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 9 items pls_01_demo.py::PLSClass::pls_a pls_a PASSED pls_01_demo.py::PLSClass::pls_b pls_b PASSED test_06_demo.py::test_case1 PASSED test_06_demo.py::test_case2 PASSED test_06_demo.py::test_case3 PASSED test_06_demo.py::test_case4 PASSED test_06_demo.py::test_case5 PASSED test_06_demo.py::test_case6 PASSED test_06_demo.py::test_case7 PASSED -------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/testcases/report.html -------------------------------------- ======================================================================== 9 passed in 0.04s ========================================================================
只使用pytest命令即可以输出详细信息及print信息,还生成了简易测试报告
指定/忽略执行目录
目前执行测试用例是在命令行下,在各个目录下切换。而正常项目测试,都是基于项目的根目录进行执行,而测试用例会集中在一个或几个专门的目录中。
testpasths
是指定执行用例的路径[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html testpaths = testcases
在项目根目录下执行用例
$ pwd /Users/laobai/workspaces/testdemo01 $ ls assets history.json pytest.ini pytest_demo report.html testcases unittestdemo venv $ pytest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 9 items testcases/pls_01_demo.py::PLSClass::pls_a pls_a PASSED testcases/pls_01_demo.py::PLSClass::pls_b pls_b PASSED testcases/test_06_demo.py::test_case1 PASSED testcases/test_06_demo.py::test_case2 PASSED testcases/test_06_demo.py::test_case3 PASSED testcases/test_06_demo.py::test_case4 PASSED testcases/test_06_demo.py::test_case5 PASSED testcases/test_06_demo.py::test_case6 PASSED testcases/test_06_demo.py::test_case7 PASSED ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html ------------------------------------------- ======================================================================== 9 passed in 0.04s ========================================================================
norecursedirs
是指忽略设定的目录下的用例[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html ;testpaths = testcases norecursedirs = testcases
执行用例的log这里不再贴了,用例比较多,占用太大篇幅。忽略testcases目录下的用例,执行pytest_demo和unittestdemo两个目录下的用例
================================================== 5 failed, 30 passed, 2 skipped, 5 warnings, 2 rerun in 7.47s ===================================================
在
pytest.ini
增加过滤warnings信息的设定[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html filterwarnings = ignore::UserWarning testpaths = testcases
在
pytest.ini
增加日志记录的设定修改
pytest.ini
如下:[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html filterwarnings = ignore::UserWarning testpaths = testcases log_cli = True log_date_format = %Y-%m-%d %H-%M-%S log_level = DEBUG log_format = %(asctime)s - %(filename)s - %(module)s - %(funcName)s - %(lineno)d -%(levelname)s - %(message)s log_file = ./logs/test.log
在测试用例中增加log输出语句
import logging import pytest def test_case1(): logging.info("这是test_case1 测试用例日志") x = 1 assert x def test_case2(): logging.info("这是test_case2 测试用例日志") x = 0 assert not x def test_case3(): logging.info("这是test_case3 测试用例日志") x = 'python' assert 'o' in x def test_case4(): logging.info("这是test_case4 测试用例日志") x = 'python' assert 'i' not in x def test_case5(): logging.info("这是test_case5 测试用例日志") x = 10 assert x == 10
import logging import pytest class PLSClass: def pls_a(self): logging.info("这是pls_a 测试用例日志") print("pls_a") def pls_b(self): logging.info("这是pls_b 测试用例日志") print("pls_b") if __name__ == '__main__': pytest.main(["pls_01_demo.py"])
执行用例
$ pytest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 9 items testcases/pls_01_demo.py::PLSClass::pls_a -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - pls_01_demo.py - pls_01_demo - pls_a - 13 -INFO - 这是pls_a 测试用例日志 pls_a PASSED testcases/pls_01_demo.py::PLSClass::pls_b -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - pls_01_demo.py - pls_01_demo - pls_b - 17 -INFO - 这是pls_b 测试用例日志 pls_b PASSED testcases/test_06_demo.py::test_case1 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case1 - 11 -INFO - 这是test_case1 测试用例日志 PASSED testcases/test_06_demo.py::test_case2 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case2 - 17 -INFO - 这是test_case2 测试用例日志 PASSED testcases/test_06_demo.py::test_case3 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case3 - 23 -INFO - 这是test_case3 测试用例日志 PASSED testcases/test_06_demo.py::test_case4 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case4 - 29 -INFO - 这是test_case4 测试用例日志 PASSED testcases/test_06_demo.py::test_case5 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case5 - 35 -INFO - 这是test_case5 测试用例日志 PASSED testcases/test_06_demo.py::test_case6 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case6 - 41 -INFO - 这是test_case6 测试用例日志 PASSED testcases/test_06_demo.py::test_case7 -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case7 - 47 -INFO - 这是test_case6 测试用例日志 PASSED ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html ------------------------------------------- ======================================================================== 9 passed in 0.06s ========================================================================
可以看到执行输出的信息中已经记录了设定格式的日志,并且在根目录下新建了log目录log日志并写入日志
在
pytest.ini
中注册管理Mark标签mark标签不在
pytest.ini
中注册不影响mark标签的使用,只是会在执行时报出大量的warnings信息。而注册mark标签后,就不会出现warnings信息。[pytest] python_files = test_*.py *_test.py pls_*.py python_classes = Test* PLS* python_functions = test_* pls_* addopts = -vs --html=report.html filterwarnings = ignore::UserWarning testpaths = testcases log_cli = True log_date_format = %Y-%m-%d %H-%M-%S log_level = DEBUG log_format = %(asctime)s - %(filename)s - %(module)s - %(funcName)s - %(lineno)d -%(levelname)s - %(message)s log_file = ./logs/test.log markers = webtest iostest androidtest
修改pls_01_demo.py
from mark import * class PLSClass: @iostest def pls_a(self): logging.info("这是pls_a 测试用例日志") print("pls_a") @androidtest def pls_b(self): logging.info("这是pls_b 测试用例日志") print("pls_b") if __name__ == '__main__': pytest.main(["pls_01_demo.py"])
mark.py
import pytest webtest = pytest.mark.webtest iostest = pytest.mark.iostest androidtest = pytest.mark.androidtest
使用mark标签执行用例
$ pytest -m iostest ======================================================================= test session starts ======================================================================= platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python cachedir: .pytest_cache metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'} rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4 collected 9 items / 8 deselected / 1 selected testcases/pls_01_demo.py::PLSClass::pls_a -------------------------------------------------------------------------- live log call -------------------------------------------------------------------------- 2022-12-09 00-11-43 - pls_01_demo.py - pls_01_demo - pls_a - 14 -INFO - 这是pls_a 测试用例日志 pls_a PASSED ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html ------------------------------------------- ================================================================= 1 passed, 8 deselected in 0.02s =================================================================