Pytest前后置处理
Pytest框架实现一些前后置的处理,常用三种。
1、setup/teardown,setup_class/teardown_class
使用场景:用例执行之前,需要做初始化操作;用例执行结束之后,需要做资源清理
总配置文件pytest.ini
[pytest]
addopts = -vs
testpaths = testcase/test_setup_teardown.py
python_files = test_*.py
python_classes = Test*
python_functions = test
1.1、setup/teardown
测试用例:
# -*- coding: UTF-8 -*-
class TestSetupTeardown:
def setup(self):
print('\n在执行测试用例之前初始化的代码')
def test_01_login(self):
print('\n登录')
def test_02_browse(self):
print('\n浏览网页')
def test_03_exit(self):
print('\n退出')
def teardown(self):
print('\n清理资源')
命令行操作:
% pytest
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.7.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/xxx/opt/anaconda3/envs/py37/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.7.9', 'Platform': 'Darwin-20.2.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'xdist': '2.3.0', 'html': '3.1.1', 'ordering': '0.6', 'rerunfailures': '10.1', 'metadata': '1.11.0', 'forked': '1.3.0'}}
rootdir: /Users/xxx/PycharmProjects/pytestDemo, configfile: pytest.ini, testpaths: testcase/test_setup_teardown.py
plugins: xdist-2.3.0, html-3.1.1, ordering-0.6, rerunfailures-10.1, metadata-1.11.0, forked-1.3.0
collected 3 items
testcase/test_setup_teardown.py::TestSetupTeardown::test_01_login
在执行测试用例之前初始化的代码
登录
PASSED
清理资源
testcase/test_setup_teardown.py::TestSetupTeardown::test_02_browse
在执行测试用例之前初始化的代码
浏览网页
PASSED
清理资源
testcase/test_setup_teardown.py::TestSetupTeardown::test_03_exit
在执行测试用例之前初始化的代码
退出
PASSED
清理资源
============================================================================== 3 passed in 0.01s ==============================================================================
1.2、setup_class/teardown_class
测试用例:
# -*- coding: UTF-8 -*-
class TestSetupTeardown:
def setup_class(self):
"""
在所有的用力之前只执行一次
:return:
"""
print('\n在每个类执行前的初始化操作。比如:创建日志对象,创建数据库的连接,创建接口的请求对象')
def test_01_login(self):
print('登录')
def test_02_browse(self):
print('\n浏览网页')
def test_03_exit(self):
print('\n退出')
def teardown_class(self):
print('\n在每个类执行后的扫尾工作。比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。')
命令行操作:
% pytest
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.7.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/xxx/opt/anaconda3/envs/py37/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.7.9', 'Platform': 'Darwin-20.2.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'xdist': '2.3.0', 'html': '3.1.1', 'ordering': '0.6', 'rerunfailures': '10.1', 'metadata': '1.11.0', 'forked': '1.3.0'}}
rootdir: /Users/xxx/PycharmProjects/pytestDemo, configfile: pytest.ini, testpaths: testcase/test_setup_teardown.py
plugins: xdist-2.3.0, html-3.1.1, ordering-0.6, rerunfailures-10.1, metadata-1.11.0, forked-1.3.0
collected 3 items
testcase/test_setup_teardown.py::TestSetupTeardown::test_01_login
在每个类执行前的初始化操作。比如:创建日志对象,创建数据库的连接,创建接口的请求对象
登录
PASSED
testcase/test_setup_teardown.py::TestSetupTeardown::test_02_browse
浏览网页
PASSED
testcase/test_setup_teardown.py::TestSetupTeardown::test_03_exit
退出
PASSED
在每个类执行后的扫尾工作。比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。
============================================================================== 3 passed in 0.01s ==============================================================================
2、使用@pytest.fixture()装饰器来实现部分
用例的前后置
@pytest.fixture(scope=‘function’, params=’’, autouse=’’, ids=’’, name=’’)
- scope表示的是被@pytest.fixture标记的方法的作用域。function(默认)、class、module、package/session
- params:参数化(支持:列表
[]
,元祖()
,字典列表[{},{},{}]
,字典元祖({},{},{})
) - autouse=True:自动执行,默认False
- ids:当使用params参数化时,给每一个值设置一个变量名。意义不大
- name:给被@pytest.fixture标记的方法取一个别名
2.1、scope是function
手动
调用前后置方法,好处是想让哪些方法执行前后置就让哪些方法执行前后置
用例:
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope='function')
def my_fixture():
print('这是前置的方法,可以实现部分以及全部用例的前置')
yield # 通过yield将前后置方法分隔
print('\n这是后置的方法,可以实现部分以及全部用例的后置')
class TestSetupTeardown:
def test_01(self):
print('没有前置方法')
def test_02(self, my_fixture): # 调用处
print('有前置方法')
命令行操作:
% pytest
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.7.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/xxx/opt/anaconda3/envs/py37/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.7.9', 'Platform': 'Darwin-20.2.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'xdist': '2.3.0', 'html': '3.1.1', 'ordering': '0.6', 'rerunfailures': '10.1', 'metadata': '1.11.0', 'forked': '1.3.0'}}
rootdir: /Users/liyabin01/PycharmProjects/pytestDemo, configfile: pytest.ini, testpaths: testcase/test_fixture.py
plugins: xdist-2.3.0, html-3.1.1, ordering-0.6, rerunfailures-10.1, metadata-1.11.0, forked-1.3.0
collected 2 items
testcase/test_fixture.py::TestSetupTeardown::test_01 没有前置方法
PASSED
testcase/test_fixture.py::TestSetupTeardown::test_02 这是前置的方法,可以实现部分以及全部用例的前置
有前置方法
PASSED
这是后置的方法,可以实现部分以及全部用例的后置
============================================================================== 2 passed in 0.01s ==============================================================================
自动
调用前后置方法,好处是方法可以自动执行,但是不能控制哪些执行哪些不执行,全部作用域命中的方法都要执行
用例:
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope='function', autouse=True) # 调用处
def my_fixture():
print('这是前置的方法,可以实现部分以及全部用例的前置')
yield
print('\n这是后置的方法,可以实现部分以及全部用例的后置')
class TestSetupTeardown:
def test_01(self):
print('没有前置方法')
def test_02(self):
print('有前置方法')
命令行操作,会发现test_01方法也加了前后置处理
% pytest
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.7.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/xxx/opt/anaconda3/envs/py37/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.7.9', 'Platform': 'Darwin-20.2.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'xdist': '2.3.0', 'html': '3.1.1', 'ordering': '0.6', 'rerunfailures': '10.1', 'metadata': '1.11.0', 'forked': '1.3.0'}}
rootdir: /Users/liyabin01/PycharmProjects/pytestDemo, configfile: pytest.ini, testpaths: testcase/test_fixture.py
plugins: xdist-2.3.0, html-3.1.1, ordering-0.6, rerunfailures-10.1, metadata-1.11.0, forked-1.3.0
collected 2 items
testcase/test_fixture.py::TestSetupTeardown::test_01 这是前置的方法,可以实现部分以及全部用例的前置
没有前置方法
PASSED
这是后置的方法,可以实现部分以及全部用例的后置
testcase/test_fixture.py::TestSetupTeardown::test_02 这是前置的方法,可以实现部分以及全部用例的前置
有前置方法
PASSED
这是后置的方法,可以实现部分以及全部用例的后置
============================================================================== 2 passed in 0.01s ==============================================================================
2.2、scope是class
fixture为class级别的时候,如果一个class里面有多个用例,都调用了此fixture,那么此fixture只在该class里所有用例开始前执行一次
(1)验证执行次数
用例:
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope="class")
def first():
print("\n获取用户名,scope为class级别只运行一次")
a = "yoyo"
return a
class TestCase():
def test_1(self, first):
'''用例传fixture'''
print("测试账号:%s" % first)
assert first == "yoyo"
def test_2(self, first):
'''用例传fixture'''
print("测试账号:%s" % first)
assert first == "yoyo"
命令行操作:
% pytest
testcase/test_fixture.py::TestCase::test_1
获取用户名,scope为class级别只运行一次
测试账号:yoyo
PASSED
testcase/test_fixture.py::TestCase::test_2 测试账号:yoyo
PASSED
(2)验证作用域
用例:
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope='class', autouse=True)
def my_fixture():
print('\n这是前置的类方法,可以实现部分以及全部用例的前置')
yield
print('\n这是后置的类方法,可以实现部分以及全部用例的后置')
class TestSetupTeardown1:
def test_01(self):
print('我是测试方法1')
class TestSetupTeardown2:
def test_02(self):
print('我是测试方法2')
命令行操作:
% pytest
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.7.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/xxx/opt/anaconda3/envs/py37/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.7.9', 'Platform': 'Darwin-20.2.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'xdist': '2.3.0', 'html': '3.1.1', 'ordering': '0.6', 'rerunfailures': '10.1', 'metadata': '1.11.0', 'forked': '1.3.0'}}
rootdir: /Users/liyabin01/PycharmProjects/pytestDemo, configfile: pytest.ini, testpaths: testcase/test_fixture.py
plugins: xdist-2.3.0, html-3.1.1, ordering-0.6, rerunfailures-10.1, metadata-1.11.0, forked-1.3.0
collected 2 items
testcase/test_fixture.py::TestSetupTeardown1::test_01
这是前置的类方法,可以实现部分以及全部用例的前置
我是测试方法1
PASSED
这是后置的类方法,可以实现部分以及全部用例的后置
testcase/test_fixture.py::TestSetupTeardown2::test_02
这是前置的类方法,可以实现部分以及全部用例的前置
我是测试方法2
PASSED
这是后置的类方法,可以实现部分以及全部用例的后置
============================================================================== 2 passed in 0.01s ==============================================================================
2.3、params
简单用例:
# -*- coding: UTF-8 -*-
import pytest
@pytest.fixture(scope='function', params=['成龙', '甄子丹', '李小龙'])
def get_data(request):
return request.param
class TestFixture:
def test_01(self):
print('我是吴奇隆')
def test_02(self, get_data):
print('我是{}'.format(get_data))
命令行操作:
% pytest
testcase/test_fixture.py::TestFixture::test_01 我是吴奇隆
PASSED
testcase/test_fixture.py::TestFixture::test_02[\u6210\u9f99] 我是成龙
PASSED
testcase/test_fixture.py::TestFixture::test_02[\u7504\u5b50\u4e39] 我是甄子丹
PASSED
testcase/test_fixture.py::TestFixture::test_02[\u674e\u5c0f\u9f99] 我是李小龙
PASSED
可以观察到test_02方法调用了三次,固定写法。
3、通过conftest.py和@pytest.fixture()结合实现全局的前置应用(比如:项目的全局登录,模块的全局处理)
1.conftest.py文件是单独存放的一个夹具(@pytest.fixture()
)配置文件,名称不能更改。
2.用处可以在不同的py文件中使用同一个fixture函数
3.原则上conftest.py需要和运行的用例放在同一层。并且不需要做任何的import导入操作。
总结:
- setup/teardown,setup_class/teardown_class:它是作用域所有用例或者所有类
- @pytest.fixture():它的作用是既可以
部分
也可以全部
前后置 - conftest.py和@pytest.fixture()结合使用,作用域全局的前后置。
用例:
pytest.ini
[pytest]
addopts = -vs
testpaths = testcase/
python_files = test_*.py
python_classes = Test*
python_functions = test
项目目录结构:
conftest.py
import pytest
@pytest.fixture(scope='function') # conftest.py
def all_fixture():
print('\n全局的前置')
yield
print('\n全局的后置')
@pytest.fixture(scope='function') # product/conftest.py
def product_fixture():
print('\nproduct的前置')
yield
print('\nproduct的后置')
@pytest.fixture(scope='function') # user/conftest.py
def user_fixture():
print('\nuser的前置')
yield
print('\nuser的后置')
test_product.py/test_user.py
import pytest
class TestProduct:
def test_product(self, all_fixture, product_fixture):
print('我是一个product~')
class TestUser:
def test_user(self, user_fixture):
print('我是一个user~')
命令行操作:
% pytest
============================================================================= test session starts =============================================================================
collected 2 items
testcase/product/test_product.py::TestProduct::test_product
全局的前置
product的前置
我是一个product~
PASSED
product的后置
全局的后置
testcase/user/test_user.py::TestUser::test_user
user的前置
我是一个user~
PASSED
user的后置
============================================================================== 2 passed in 0.02s ==============================================================================
上述执行结果可以看出,@pytest.fixture的生效顺序是函数中调用的顺序
def test_product(self, all_fixture, product_fixture)