python学习之pytest测试框架【标记】
pytest提供了标记机制,允许你是用marker对测试函数做标记。一个测试函数可以有多个marker,一个marker也可以用来标记多个测试函数,比如我们选一部分测试作为冒烟测试,以了解当前系统中是否存在大的缺陷。
一、测试函数的标记
1 @pytest.mark.smoke 2 def test_list_raises(): 3 """list() should raise an exception with wrong type param.""" 4 with pytest.raises(TypeError): 5 tasks.list_tasks(owner=123) 6 7 @pytest.mark.get 8 @pytest.mark.smoke 9 def test_get_raises(): 10 """get() should raise an exception with wrong type param.""" 11 with pytest.raises(TypeError): 12 tasks.get(task_id='123')
1、上串代码中对测试函数进行了标记了操作,现在我们只需要在命令行指定 -m marker_name,就可以运行它们了
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest -v -m smoke test_api_exceptions.py ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 -- d:\python3.7\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.0', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '5.2.0', 'py': '1.8.0', 'pl uggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8.6', 'html': '2.0.0', 'metadata': '1.8.0', 'rerunfailures': '7.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jdk1.8.0_31'} rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 7 items / 5 deselected / 2 selected test_api_exceptions.py::test_list_raises PASSED [ 50%] test_api_exceptions.py::test_get_raises PASSED [100%] -- Docs: https://docs.pytest.org/en/latest/warnings.html ==================================== 2 passed, 5 deselected, 2 warnings in 0.03s ====================================
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest -v -m get test_api_exceptions.py ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 -- d:\python3.7\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.0', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '5.2.0', 'py': '1.8.0', 'pl uggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8.6', 'html': '2.0.0', 'metadata': '1.8.0', 'rerunfailures': '7.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jdk1.8.0_31'} rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 7 items / 6 deselected / 1 selected test_api_exceptions.py::test_get_raises PASSED [100%] -- Docs: https://docs.pytest.org/en/latest/warnings.html ==================================== 1 passed, 6 deselected, 2 warnings in 0.03s ====================================
2、下面在另一个py文件中增加一个同样的标记,并且再运行个例子
1 import pytest 2 import tasks 3 from tasks import Task 4 5 6 def test_add_returns_valid_id(): 7 """tasks.add(<valid task>) should return an integer.""" 8 # GIVEN an initialized tasks db 9 # WHEN a new task is added 10 # THEN returned task_id is of type int 11 new_task = Task('do something') 12 task_id = tasks.add(new_task) 13 assert isinstance(task_id, int) 14 15 16 @pytest.mark.smoke 17 def test_added_task_has_id_set(): 18 """Make sure the task_id field is set by tasks.add().""" 19 # GIVEN an initialized tasks db 20 # AND a new task is added 21 new_task = Task('sit in chair', owner='me', done=True) 22 task_id = tasks.add(new_task) 23 24 # WHEN task is retrieved 25 task_from_db = tasks.get(task_id) 26 27 # THEN task_id matches id field 28 assert task_from_db.id == task_id 29 30 31 @pytest.fixture(autouse=True) 32 def initialized_tasks_db(tmpdir): 33 """Connect to db before testing, disconnect after.""" 34 # Setup : start db 35 tasks.start_tasks_db(str(tmpdir), 'tiny') 36 37 yield # this is where the testing happens 38 39 # Teardown : stop db 40 tasks.stop_tasks_db()
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest -v -m smoke ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 -- d:\python3.7\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.0', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '5.2.0', 'py': '1.8.0', 'pl uggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8.6', 'html': '2.0.0', 'metadata': '1.8.0', 'rerunfailures': '7.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jdk1.8.0_31'} rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 50 items / 47 deselected / 3 selected test_add.py::test_added_task_has_id_set PASSED [ 33%] test_api_exceptions.py::test_list_raises PASSED [ 66%] test_api_exceptions.py::test_get_raises PASSED [100%] -- Docs: https://docs.pytest.org/en/latest/warnings.html =================================== 3 passed, 47 deselected, 2 warnings in 0.15s ====================================
运行完成后,可以看到,带有相同标记的测试即使存放在不通的文件下,也会被一起执行
二、跳过测试
pytest自身内置了一些标记:sikp、sikpif、xfail
1、skip和sipkif允许你跳过不希望运行的测试,要跳过某个测试,只需要简单地在测试函数上方标记@pytest.mark.sipk()装饰器即可,如下:
1 # @pytest.mark.skip(reason='misunderstood the API') 2 @pytest.mark.skip('skip..........') 3 def test_unique_id_1(): 4 """Calling unique_id() twice should return different numbers.""" 5 id_1 = tasks.unique_id() 6 id_2 = tasks.unique_id() 7 assert id_1 != id_2 8 9 10 def test_unique_id_2(): 11 """unique_id() should return an unused id.""" 12 ids = [] 13 ids.append(tasks.add(Task('one'))) 14 ids.append(tasks.add(Task('two'))) 15 ids.append(tasks.add(Task('three'))) 16 # grab a unique id 17 uid = tasks.unique_id() 18 # make sure it isn't in the list of existing ids 19 assert uid not in ids
运行后的结果,如下:
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest -v test_unique_id_2.py ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 -- d:\python3.7\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.0', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '5.2.0', 'py': '1.8.0', 'pl uggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8.6', 'html': '2.0.0', 'metadata': '1.8.0', 'rerunfailures': '7.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jdk1.8.0_31'} rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 2 items test_unique_id_2.py::test_unique_id_1 SKIPPED [ 50%] test_unique_id_2.py::test_unique_id_2 PASSED [100%] =========================================== 1 passed, 1 skipped in 0.15s ============================================
实际上,可以给跳过的测试添加理由和条件,如果需要添加条件的话就得使用skipif来代替skip,skipif()的判定条件可以是任何python表达式,,无论是skip还是skipif,都可以写上跳过理由(尽管sikp没要求这样做)。建议都写会好一点
1 @pytest.mark.skipif(tasks.__version__ < '5.2.0', 2 reason='not supported until version 5.2.0') 3 def test_unique_id_1(): 4 """Calling unique_id() twice should return different numbers.""" 5 id_1 = tasks.unique_id() 6 id_2 = tasks.unique_id() 7 assert id_1 != id_2 8 9 10 def test_unique_id_2(): 11 """unique_id() should return an unused id.""" 12 ids = [] 13 ids.append(tasks.add(Task('one'))) 14 ids.append(tasks.add(Task('two'))) 15 ids.append(tasks.add(Task('three'))) 16 # grab a unique id 17 uid = tasks.unique_id() 18 # make sure it isn't in the list of existing ids 19 assert uid not in ids
运行后的显示,如下:
想要看到跳过的理由,加上 -rs 选项 会显示用户描述的跳过理由
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest -v -rs test_unique_id_3.py ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 -- d:\python3.7\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.0', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '5.2.0', 'py': '1.8.0', 'pl uggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8.6', 'html': '2.0.0', 'metadata': '1.8.0', 'rerunfailures': '7.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jdk1.8.0_31'} rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 2 items test_unique_id_3.py::test_unique_id_1 SKIPPED [ 50%] test_unique_id_3.py::test_unique_id_2 PASSED [100%] ============================================== short test summary info ============================================== SKIPPED [1] func\test_unique_id_3.py:8: not supported until version 5.2.0 =========================================== 1 passed, 1 skipped in 0.15s ============================================
2、使用xfail标记,则告诉pytest运行此测试,我们预期它会失败
1 @pytest.mark.xfail(tasks.__version__ < '5.2.0', 2 reason='not supported until version 5.2.0') 3 def test_unique_id_1(): 4 """Calling unique_id() twice should return different numbers.""" 5 id_1 = tasks.unique_id() 6 id_2 = tasks.unique_id() 7 assert id_1 != id_2 8 9 @pytest.mark.xfail() 10 def test_unique_id_is_a_duck(): 11 """Demonstrate xfail.""" 12 uid = tasks.unique_id() 13 assert uid == 'a duck' 14 15 @pytest.mark.xfail() 16 def test_unique_id_not_a_duck(): 17 """Demonstrate xpass.""" 18 uid = tasks.unique_id() 19 assert uid != 'a duck'
运行后的结果,如下:
G:\TestPytest\code\ch2\tasks_proj\tests\func>pytest test_unique_id_4.py ================================================ test session starts ================================================ platform win32 -- Python 3.7.0, pytest-5.2.0, py-1.8.0, pluggy-0.13.0 rootdir: G:\TestPytest\code\ch2\tasks_proj\tests, inifile: pytest.ini plugins: allure-pytest-2.8.6, html-2.0.0, metadata-1.8.0, rerunfailures-7.0 collected 3 items test_unique_id_4.py xxx [100%] ================================================ 3 xfailed in 0.17s =================================================
x代表XFAIL,意味着“expected to fail”(预期失败,实际上也失败了)。大写的X代表是XPASS,意味是“expected to fail but passed”(预期失败,但实际运行并没有失败),这次运行全是失败,大X没有运行出来,抱歉了......
学习总结,仅供参考,如有疑义,欢迎找茬......

浙公网安备 33010602011771号