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没有运行出来,抱歉了......

 

学习总结,仅供参考,如有疑义,欢迎找茬......

posted @ 2019-11-10 15:42  一名小测试  阅读(222)  评论(0)    收藏  举报